面试算法:波浪型数组的快速排序法
2017-08-04 11:00
736 查看
波浪型数组是具备这样特性的数组,它的元素先是递增,到达某个点后开始低贱,接着达到某个点时又递增,接着右递减,以下数组就是一个波浪型数组:
A: 57, 131, 493, 294, 221, 339, 418, 452, 442, 190
A[0] A[1] A[2] A[3] A[4] A[5] A[6], A[7], A[8], A[9]
不难发现,A[0]-A[4]是一个波浪,因为它经历了递增然后递减的过程,同理A[5]-A[9]又是一个波浪。所以这个数组有两个波浪。
假设给定一个含有k个波浪的数组,要求设计一个高效算法对其进行排序,你可以申请一个同等大小的空数组,并且允许你再申请O(k)大小的额外空间。
首先我们先对每个波浪进行排序,从给定例子的第一个波浪,由于它具备先增后减的特性,于是我们可以在波浪的开头和末尾个放置一个指针,然后比对两个指针指向元素的大小,把较小的一个元素放入到另一个数组的相应位置,然后改变相应指针的位置,例如对于第一个波浪:
57(p1), 131, 493, 294, 221(p2)
我们用P1指向第一个元素,用P2指向最后一个元素,然后比较两者大小,用于p1指向的值小于p2指向的值,所以把p1的值放入另一个数组相应的位置,然后前进p1的位置:
A: 57, 131(p1), 493, 294, 221(p2)
A1: 57
根据这个流程走完后,一个波浪中的元素就可以被排序了:
A: 57, 131, 493(p1,p2), 294, 221
A1: 57, 131, 221, 294, 493
我们先把每个波浪都排序,相关算法的实现代码如下:
不难看出,对每个波浪进行排序时,只要遍历一遍波浪中的元素就可以了,所以对所有波浪进行排序,所需的时间复杂度是O(n)。
上面代码运行后,波浪数组的每个波浪都会得到排序,根据上面例子,代码执行后,数组情况如下:
A1: 57, 131, 221, 294, 493, 190 , 339, 418, 442, 452
接下来要做的是,从每个波浪中取出最大值用来组成一个大堆,利用extract-maximun()从堆中获得最大值,把这个最大值放入到数组的末尾,假设这个最大值来自k个波浪中的第 t 个,那么我们从第t个波浪中取得它倒数第二大的值加入大堆,然后通过继续调用extract-maximun()获得最大值,这个最大值则是整个数组倒数第二大的值,以此类推我们就可以把整个波浪数组进行排序了。
假定给定一个波浪数组如下:
A: 57, 131, 493, 294, 221, 339, 418, 452, 442, 190, 230, 310, 510, 432, 271, 280, 350, 631, 450, 332
对每个波浪进行排序后,情况如下:
57, 131, 221, 294, 493 || 190, 339, 418, 442, 452 ||230, 271, 310, 432, 510 || 280, 332, 350, 450, 631
取出每个波浪的最大值组成一个大堆后情况如下:
heap: 631, 510, 452, 493
执行一次extract-maximun() 得到631, 由于它来自最后一个波浪,因此把631前面的450加入大堆:
heap: 510, 493, 452, 450
再次执行extract-maximun() 得到510,然后把510前面的值432加入大堆:
heap: 493, 452, 450, 432
继续执行extract-maximun()得到的值就是493,以此类推我们就可以把整个数组进行排序了。
如果数组有k个波浪,那么构造大堆的复杂度就是O(k* lgk),extract-maximun的复杂度是O(1), 把元素加入一个含有k个元素的大堆,时间复杂度是O(lgk), 我们总共需要将n-k个元素加入大堆,因此总时间复杂度是O(n*lgk).
为了实现上面算法,我们定义下面数据结构:
其中,exchange用来交换两个Pair对象的内容。
堆排序时,要对Pair中的val来排序,extract-maximun时,返回的对象不再是int,而是Pair。对上节堆排序代码的对应修改在这里就不罗列出来,具体内容可以查看视频。
我们再看看最后排序算法的具体实现:
我们对每个波浪排序时,用Pair对象记录下波浪最后一个元素的值,以及波浪的起始和末尾位置。如果数组由k个波浪,那么我们会申请k个Pair对象。接着我们看看排序的实现:
sortWave 把每个波浪中的最大值集合在一起形成一个大堆,通过extract-maximun()得到大堆的最大元素,并把它放入到数组的合适位置,然后再把该最大元素的前一个元素加入到大堆中,该函数所实现的就是我们前面所说的堆排序算法。
完成以上代码后,在入口处我们添加如下代码:
我们先构造一个波浪形数组,然后使用sortArray对其排序,得到的最终结果为:
57 131 190 221 230 271 280 294 310 332 339 350 418 432 442 450 452 493 510 631
由此可见,数组被正确排序了,我们代码对算法的实现是正确的,更详实的讲解和代码调试演示过程,请参看视频:
如何进入google,算法面试技能全面提升指南
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
A: 57, 131, 493, 294, 221, 339, 418, 452, 442, 190
A[0] A[1] A[2] A[3] A[4] A[5] A[6], A[7], A[8], A[9]
不难发现,A[0]-A[4]是一个波浪,因为它经历了递增然后递减的过程,同理A[5]-A[9]又是一个波浪。所以这个数组有两个波浪。
假设给定一个含有k个波浪的数组,要求设计一个高效算法对其进行排序,你可以申请一个同等大小的空数组,并且允许你再申请O(k)大小的额外空间。
首先我们先对每个波浪进行排序,从给定例子的第一个波浪,由于它具备先增后减的特性,于是我们可以在波浪的开头和末尾个放置一个指针,然后比对两个指针指向元素的大小,把较小的一个元素放入到另一个数组的相应位置,然后改变相应指针的位置,例如对于第一个波浪:
57(p1), 131, 493, 294, 221(p2)
我们用P1指向第一个元素,用P2指向最后一个元素,然后比较两者大小,用于p1指向的值小于p2指向的值,所以把p1的值放入另一个数组相应的位置,然后前进p1的位置:
A: 57, 131(p1), 493, 294, 221(p2)
A1: 57
根据这个流程走完后,一个波浪中的元素就可以被排序了:
A: 57, 131, 493(p1,p2), 294, 221
A1: 57, 131, 221, 294, 493
我们先把每个波浪都排序,相关算法的实现代码如下:
public class WaveArraySorter { int[] waveArray = null; int[] orignalArray = null; int waveBegin = 0; int waveEnd = 0; public WaveArraySorter(int[] array) { waveArray = new int[array.length]; this.orignalArray = array; findWaveAndSort(); } private void findWaveAndSort() { int i = waveBegin = 0; boolean waveDown = false; while (i < orignalArray.length - 1) { if (orignalArray[i] > orignalArray[i+1]) { waveDown = true; } boolean risingAgain = orignalArray[i] < orignalArray[i+1]; boolean reachingEnd = (i + 1 == orignalArray.length - 1); if (waveDown == true && (risingAgain || reachingEnd)) { if (reachingEnd) { waveEnd = i + 1; } else { waveEnd = i; } sortWave(); waveBegin = i + 1; waveDown = false; } i++; } } private void sortWave() { int p = waveBegin; int begin = waveBegin; int end = waveEnd; while (begin <= end) { if (orignalArray[begin] <= orignalArray[end]) { waveArray[p] = orignalArray[begin]; begin++; } else { waveArray[p] = orignalArray[end]; end--; } p++; } } }
不难看出,对每个波浪进行排序时,只要遍历一遍波浪中的元素就可以了,所以对所有波浪进行排序,所需的时间复杂度是O(n)。
上面代码运行后,波浪数组的每个波浪都会得到排序,根据上面例子,代码执行后,数组情况如下:
A1: 57, 131, 221, 294, 493, 190 , 339, 418, 442, 452
接下来要做的是,从每个波浪中取出最大值用来组成一个大堆,利用extract-maximun()从堆中获得最大值,把这个最大值放入到数组的末尾,假设这个最大值来自k个波浪中的第 t 个,那么我们从第t个波浪中取得它倒数第二大的值加入大堆,然后通过继续调用extract-maximun()获得最大值,这个最大值则是整个数组倒数第二大的值,以此类推我们就可以把整个波浪数组进行排序了。
假定给定一个波浪数组如下:
A: 57, 131, 493, 294, 221, 339, 418, 452, 442, 190, 230, 310, 510, 432, 271, 280, 350, 631, 450, 332
对每个波浪进行排序后,情况如下:
57, 131, 221, 294, 493 || 190, 339, 418, 442, 452 ||230, 271, 310, 432, 510 || 280, 332, 350, 450, 631
取出每个波浪的最大值组成一个大堆后情况如下:
heap: 631, 510, 452, 493
执行一次extract-maximun() 得到631, 由于它来自最后一个波浪,因此把631前面的450加入大堆:
heap: 510, 493, 452, 450
再次执行extract-maximun() 得到510,然后把510前面的值432加入大堆:
heap: 493, 452, 450, 432
继续执行extract-maximun()得到的值就是493,以此类推我们就可以把整个数组进行排序了。
如果数组有k个波浪,那么构造大堆的复杂度就是O(k* lgk),extract-maximun的复杂度是O(1), 把元素加入一个含有k个元素的大堆,时间复杂度是O(lgk), 我们总共需要将n-k个元素加入大堆,因此总时间复杂度是O(n*lgk).
为了实现上面算法,我们定义下面数据结构:
public class Pair { public int val; public int begin; int end; public Pair(int v, int b, int e) { this.val = v; this.begin = b; this.end = e; } public void exchange(Pair pair) { int v = val; int b = begin; int e = end; this.val = pair.val; this.begin = pair.begin; this.end = pair.end; pair.val = v; pair.begin = b; pair.end = e; } }
其中,exchange用来交换两个Pair对象的内容。
堆排序时,要对Pair中的val来排序,extract-maximun时,返回的对象不再是int,而是Pair。对上节堆排序代码的对应修改在这里就不罗列出来,具体内容可以查看视频。
我们再看看最后排序算法的具体实现:
private void findWaveAndSort() { int i = waveBegin = 0; boolean waveDown = false; while (i < orignalArray.length - 1) { if (orignalArray[i] > orignalArray[i+1]) { waveDown = true; } boolean risingAgain = orignalArray[i] < orignalArray[i+1]; boolean reachingEnd = (i + 1 == orignalArray.length - 1); if (waveDown == true && (risingAgain || reachingEnd)) { if (reachingEnd) { waveEnd = i + 1; } else { waveEnd = i; } sortWave(); Pair pair = new Pair(waveArray[waveEnd], waveBegin, waveEnd); pairArray.add(pair); waveBegin = i + 1; waveDown = false; } i++; } }
我们对每个波浪排序时,用Pair对象记录下波浪最后一个元素的值,以及波浪的起始和末尾位置。如果数组由k个波浪,那么我们会申请k个Pair对象。接着我们看看排序的实现:
public void sortArray() { Pair[] array = new Pair[pairArray.size()]; for (int i = 0; i < array.length; i++) { array[i] = pairArray.get(i); } HeapPairSort hp = new HeapPairSort(array); hp.buildMaxHeap(); int count = orignalArray.length - 1; while (count >= 0) { orignalArray[count] = max.val; if (max.end > max.begin) { max.end--; max.val = waveArray[max.end]; hp.insert(max); } count--; } }
sortWave 把每个波浪中的最大值集合在一起形成一个大堆,通过extract-maximun()得到大堆的最大元素,并把它放入到数组的合适位置,然后再把该最大元素的前一个元素加入到大堆中,该函数所实现的就是我们前面所说的堆排序算法。
完成以上代码后,在入口处我们添加如下代码:
int[] wave = new int[] {57, 131, 493, 294, 221, 339, 418, 452, 442, 190, 230, 310, 510, 432, 271, 280, 350, 631, 450, 332}; WaveArraySorter was = new WaveArraySorter(wave); was.sortArray(); for (int i = 0; i < wave.length; i++) { System.out.print(wave[i] + " "); }
我们先构造一个波浪形数组,然后使用sortArray对其排序,得到的最终结果为:
57 131 190 221 230 271 280 294 310 332 339 350 418 432 442 450 452 493 510 631
由此可见,数组被正确排序了,我们代码对算法的实现是正确的,更详实的讲解和代码调试演示过程,请参看视频:
如何进入google,算法面试技能全面提升指南
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
相关文章推荐
- 面试常考算法题 局部最小 求二叉树结点 求两个数组中所有数的上中位数 两个数组的所有数中第K小的数
- 面试算法(四十)数组中只出现一次的数字
- [面试算法]有一无符号整型数组,大小为10, 初始的数值随机,但在[0, 99]之间。请用C语言写一个过滤程序,令数组内的数据互不相等。
- leetcode:Remove Duplicates from Sorted Array II (允许重复一次,去掉数组多余数字)【面试算法题】
- C/C++面试之算法系列--1~n无序数组时间复杂度为O(n)排序
- C/C++面试之算法系列--去除数组中的重复数字
- 阿里算法工程师校招面试之-找最长的连续递增子数组的长度
- 【有趣的面试算法题】 数组循环移动算法细究
- 【LeetCode-面试算法经典-Java实现】【004-Median of Two Sorted Arrays(两个排序数组的中位数)】
- 微软等数据结构+算法面试100题(5)--怎样编写一个程序,把一个有序整数数组放到二叉树中
- 微软等数据结构+算法面试100题(39)-- 左旋数组中查找
- 面试算法--二位数组的计算
- 微软等数据结构+算法面试100题(40)-- 旋转数组中的最小元素
- 【LeetCode-面试算法经典-Java实现】【088-Merge Sorted Array(合并排序数组)】
- 面试算法(三十八)数字在排序数组中出现的次数
- Java企业面试算法新得体会之8数组和矩阵问题6问
- 微软等数据结构+算法面试100题(44)-- 求子数组的最大和
- 面试算法:在整形数组中构建元素之和能整除数组长度的子集
- 【LeetCode-面试算法经典-Java实现】【053-Maximum Subarray(最大子数组和)】
- 【LeetCode-面试算法经典-Java实现】【152-Maximum Product Subarray(子数组的最大乘积)】