1 题目描述:
有一个数组:2,1,4,3。对于数组,有一种操作op(idx):将该index对应的数字移到首位。比如:
op(3): 2 1 43 -> 3 2 1 4
op(1): 3 2 14 -> 2 3 1 4
op(2): 2 3 1 4 -> 1 2 3 4
问对于给定的数组,数组各个元素是任意的整数,可能有重复值,需要多少次OP操作,才能使得数组有序?
对于上面的例子,需要3次即可。
2. 问题解析:
最坏的情况需要 n 次就够了,第 i 次找到数组中倒数第 i 大的数,然后进行OP操作,最后一定会使得该数组是有序的。
但是要最少几次,如何解决?
2.1 时间:O(n^2) 空间:O(1)
对于 2 1 4 3,4肯定不需要OP,接下来对于3,必须要OP,变成了 3 2 1 4。如果3进行OP了,那么其他小于3的数(1,2)都需要进行OP的,否则不能跑到 3 的前面去。
再比如 2 1 6 3 7 5 4, 发现7、6都需要OP,而从 5 开始需要进行OP了,那么答案就是所有<=5 的数了。这种算法需要能找到 2 1 6 3 7 5 4 中 递增序列6, 7. 复杂度是O(n^2),空间复杂度是O(1)。
2.2 时间:O(n) 空间:O(1)
8 6 5 7 9 5 4
目标:找到最大的数、第二大的数、第三大的数,且最大的数在最后,其他几位数依次在前面。比如
8 6 5 7 9 5 4 ->8 6 5 7 9 5 4
5 2 1 6 3 7 0 4 ->5 2 1 6 3 7 0 4
3 4 8 10 8 -> 3 4 8 10 8 (这种情况下只能是10,因为8出现2次,其中1次还在10后面,因此排除8)
当找到这个递增序列后,比如:5 2 1 6 3 7 0 4->5 2 1 6 3 7 0 4。 那么我知道4一定在5之后出现了,那么4一定要进行op操作,放到首位,只要4放到了首位,那么其他的3,2,1,0等<=4的数,全都是要进行操作的。(3也要放到首位,那么2也要放到首位,那么...)。可以看到只有 5, 6, 7三个数不需要进行op操作。因此总的op数量是n-3 = 8 - 3 = 5.
为了找到这个序列,我打算:从右往左遍历,
idx: 为遍历的指针;p: 上述递增序列的最低位指针, _min: 记录arr[idx, p-1]之间的最大的值。在遍历过程中,不断地把递增序列移到右侧,比如
idx: 6, p: 6,
8 6 5 7 9 5 4
_min: uninitialzed
idx: 5, p: 6,
8 6 5 7 9 5 4 [ 5比4大,因此交换 5和4的位置 ]
8 6 5 7 9 4 5
_min: 4 (_min更新为4)
idx: 4, p: 6
8 6 5 7 9 4 5 [ 9比5大,因此交换 9 和 5 的位置 ]
8 6 5 7 5 4 9
_min: 5 (更新为5和4中的最大者)
idx: 3, p: 6
8 6 5 7 5 4 9
8 6 5 4 5 7 9 [ 7 比 9 小,因此7可以纳入递增序列中: 交换4和7,p-- ]
_min: 5 (4,5中的最大者)
idx: 2, p: 5
8 6 5 4 5 7 9 [ 由于5 <= _min(5), 因此直接 idx--]
idx:1 , p: 5
8 6 5 4 5 7 9 [ 6大于_min,因此是ok,更新递增序列, swap(6, 5), p--]
8 5 5 4 6 7 9 [swap之后的结果]
idx: 0, p: 4
8 5 5 4 6 7 9 [ 8比_min大,8比6都大,因此p不断自增,知道8 < 9才停止,此时p指向了9,如下图]
8 5 5 4 6 7 9 [ 8 比 9 小,因此swap(8, 7)]
7 5 5 4 6 8 9 [结果]
idx: -1, p: 5
-> break;
因此最大递增序列是长度为2,{8, 9}。因此返回 n- 2 即 7 - 2 = 5
复杂度分析:
扫描的时间为 O(n)。对于swap操作,最后的递增序列类似于一个栈,每个元素最多进栈出栈一次,也是最多 O(n)。 因此时间复杂度是 O(n),空间复杂度是 O(1)。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/nisxiya/article/details/46762597