原文
上次那篇日志发布之后,据说大家解题的热情相当高。Michael Brand告诉我说,他收到了很多来自中国的邮件,他感到非常高兴。在揭晓谜底之前,还是让我们先回顾一下题目:
对数列的一次“块移动”是指把一段数取出来插入到数列中的另一个地方(说穿了就是一次选择剪切粘贴的操作)。例如,数列1,4,5,6,2,3,7可以通过一次块移动完成排序(把456挪到3后面)。那么,想要让一个1到n的逆序排列n, n-1, ..., 3, 2, 1变为顺序排列,最少需要多少次块移动?给出你的算法,并证明这个移动数目不能再少了。
需要指出的是,答案并不是n-1那么简单。当n=5时,只需要三步就可以搞定了:
5 4 [3 2] 1
3 2 5 [4 1]
[3 4] 1 2 5
1 2 3 4 5
事实上,给出1到n的逆序排列,最少需要Ceil[(n+1)/2]次块移动就可以完成排序(除了n=1或n=2,Ceil表示取上整)。当n为奇数时,一个满足要求的算法是:每一次把数字n后面那一段的正中间两个元素拿出来,插入到数字n前面那一段数的正中间。当数字n后面的数被移动完了后,把它前面n-1个数左右两半对换一下就行了。例如,当n=7时:
7 6 5 [4 3] 2 1
4 3 7 6 [5 2] 1
4 5 2 3 7 [6 1]
[4 5 6] 1 2 3 7
1 2 3 4 5 6 7
算法的移动步数显然为(n+1)/2,其正确性可以用数学归纳法说明,这里不再详细叙述了。
当n为偶数时,只需要用n/2次操作把前面n-1个元素排好序,再花一次操作把末一个元素移动到最前面,加起来正好Ceil[(n+1)/2]次操作。下面我们证明,移动次数不可能比Ceil[(n+1)/2]更少。
对于数列中相邻的两个数,如果前面那个数比后面的大,我们就把它们俩称作一组“逆序相邻数”。初始时,数列中有n-1个这样的逆序相邻数,我们的目标就是通过块移动把这个数目减少到0。整个证明过程的关键就在于,一次块移动操作最多只能消除两个逆序相邻数。
原数列: **aA--Bb***CD****
新数列: **ab***CA--BD****
假如我们把块A--B插入到CD中间。注意到,相邻数发生变动的地方只有三处。要想同时消除三个逆序相邻数,只有一种可能:原数列中a>A, B>b, C>D,同时新数列中的a<b, C<A, B<D。这将导出一个很荒谬的结论:A < a < b < B < D < C < A。这告诉我们,一次块移动同时消除三个逆序相邻数是不可能的,它最多只能消除两个逆序相邻数。另外,容易看出,第一次移动只能消除一个逆序的相邻数,因为初始时原数列完全逆序,即有a > A > B > b > C > D,在新数列中只有C<A成立。对称地,最后一次移动也只可能消除一个逆序相邻数,因为新数列中a < b < C < A < B < D,只有B>b是成立的。
于是我们得知,k次块移动最多消除1+2*(k-2)+1个逆序相邻数。为了消除n-1个逆序相邻数,我们有1+2*(k-2)+1 >= n-1,整理得k>=(n+1)/2。
分享到:
相关推荐
用线性时间和常数附加空间将一个长度为 n 的字符串向左循环移动 m 位(例如,"abcdefg"移动 3 位就变成了"defgabc")。 答案:把字符串切成长为 m 和 n-m 的两半。将这两个部分分别逆序,再对整个字符串逆序。 ...
20. **LRU缓存淘汰算法**:最近最少使用的策略,当缓存满时,淘汰最近最少使用的数据。 21. **LRU与LFU的区别**:LRU基于访问频率,LFU基于访问次数,LFU在长期使用中可能更优,但实现复杂。 22. **循环队列**:...
问最少需要多少次操作可以使所有硬币都变成反面朝上? **解决方案**:虽然题目本身没有给出详细背景,但从描述中可以推断出这是一个经典的动态规划或者数学推导问题。题目中提到“某神牛”的解答为`ans = sqrt(n) *...
- 提高Cache命中率的关键因素是替换策略,如LRU(最近最少使用)或LFU(最不经常使用)。 7. **IP网络**: - 私有IP地址范围包括10.0.0.0/8,172.16.0.0/12,192.168.0.0/16。选项c的172.32.50.80不属于私有IP...
- **题目解析**:此题考察的是线性表的插入操作,特别是当线性表采用顺序存储结构时,插入一个元素所需移动的元素个数的平均情况。 - **知识点说明**: - **线性表**:一种常见的数据结构,用于存储一系列按照某种...
### 数据结构练习题知识点解析 #### 一、简答题知识点解析 **1. 什么是有根的有向图?** - **定义**:有根的有向图是指一个有向图,其中一个特定的节点被指定为根节点。在这个图中,每条边都有方向,指向表明了...
题目中的代码实现了一个操作,将链表的最后一个节点指向头节点,实现了环的闭合。 5. 顺序表的地址计算:顺序表中,元素的地址可以通过首元素地址加上元素的偏移量来计算。题目中提到的第14个元素地址是首元素地址...
4. 缓存管理:LRU(最近最少使用)缓存策略中,常用链表记录缓存项的使用情况。 总结,单链表作为一种基本的数据结构,其灵活性和动态性使得它在解决各种问题时都能发挥重要作用。了解和熟练掌握单链表的原理和操作...
13. **线性表操作时间复杂度**:对于用一维数组d[1..n]顺序存储的线性表,查找第i个元素(1≤i≤n)的时间复杂度为O(1)(选项C)。这是由于数组可以快速定位到指定位置。 14. **静态链表结点指针**:静态链表(使用数组...
- 插入或删除操作需移动大量元素,效率较低; - 需要连续的存储空间,可能导致碎片问题。 #### 链式存储 - **优点**: - 动态分配存储单元,提高空间利用率; - 插入和删除操作效率较高。 - **缺点**: - 每...
- **最少找硬币问题(贪心策略-深搜实现)**:解决找零问题,即如何使用最少数量的硬币找零。 - **棋盘分割**:将棋盘分割成若干个特定形状的问题。 - **汉诺塔**:解决汉诺塔问题,即将圆盘从一根柱子移动到另一根...
3. 冒泡排序中,关键字反向移动可能出现在逆序对交换时;希尔排序和快速排序中,也存在类似现象,例如希尔排序的增量序列可能导致局部逆序,而快速排序的分区过程中,某些元素也可能暂时移至错误位置。 4. 二叉树的...
以上是对数据结构选择题中的主要知识点的详细解释,涵盖了算法分析、链表操作、队列、堆栈、二叉树遍历、树的性质、散列表、排序算法、字符串操作、广义表以及哈夫曼树等多个主题。这些知识点对于理解和解决实际编程...
删除第i个元素,可以通过将最后一个元素移动到i的位置并减少数组大小来实现,时间复杂度为O(1)。b.对于有序数组,同样操作,但无需减少数组大小,只需标记为特殊符号,时间复杂度也为O(1)。 以上解析涵盖了算法设计...
2. **状态表示**:每个状态可能用一个数组或二维向量表示,或者使用位操作来节省空间。 3. **移动操作**:函数或方法用于在四个方向(上、下、左、右)上移动空格。 4. **搜索算法**:如深度优先搜索、广度优先搜索...