【算法-快速排序】第k大元素(Kth Largest Element)
2017-12-28 18:32
405 查看
问题描述
问题:找出数组中第k大的元素(LintCode 5.Kth Largest Element)要求:O(n)时间复杂度,O(1)空间复杂度
标签:Sort、Quick Sort
解决思路
本题解法
常规想法先通过快排先对数组进行从大到小排序,再选取第k个数,然而单纯的快排需要O(nlogn)时间复杂度,不符合本题的复杂度要求。这里需要对快排进行简单的优化。本题依旧借助快排的思想,只不过每次只选择一边的子序列进行划分。每次选定基准之后,比基准值大的都在左边子序列,小的在右边子序列,所以只要将k与当前基准所在下标进行比较(基准下标index即为第index大的数),确认下次对哪半边子序列进行操作。
结合快速排序思想以及上述改进措施,代码如下(已通过LintCode测试):
class Solution { public: /* * @param n: An integer * @param nums: An array * @return: the Kth largest element */ int kthLargestElement(int k, vector<int> nums){ //第k大 下标k-1 return QuickSort(nums, 0, nums.size() - 1, k-1); } int QuickSort(vector<int> &nums, int left, int right, int k) { if (left == right) { return nums[left]; } if(left<right){ //快排思想 从大至小排序 int i=left; int j=right; int key=nums[left]; while(i<j){ while(i<j&&nums[j]<key) j--; if(i<j){ nums[i]=nums[j]; i++; } while(i<j&&nums[i]>=key) i++; if(i<j){ nums[j]=nums[i]; j--; } } //i=j退出循环 nums[i]=key; //进行基准的新下标与k的比较 if(i>k) return QuickSort(nums,left,i-1,k); else if(i<k) return QuickSort(nums,i+1,right,k); else return nums[i]; } } };
常用方法总结
选择排序,k次选择后得第k大数;或者冒泡排序,冒泡k趟得第k大数,时间复杂度O(k*n)快速排序,选择第k大数,时间复杂度O(nlogn)
借助快排思想,本题解法有详细说明
建最大堆(大顶堆),pop出k次得第k大数,时间复杂度O(k*logn)
建大小为k的最小堆(小顶堆),数组中每个元素与堆顶元素比较大小,若比堆顶大,则弹出堆顶元素,插入该元素,重新调整最小堆,堆顶元素即为第k大数,时间复杂度O(n*logk)
利用hash表统计元素出现个数,线性从大到小扫描,的第k大数,时间复杂度O(n),空间复杂度O(n)。
算法讲解
快排Quick Sort
快排也叫分区排序,是目前应用最广泛的排序算法。但快排是一种不稳定的排序算法。快速排序的过程
快排是一种划分交换的方法,采用分治法进行排序。其基本思想是:任取待排序元素序列A中的某个元素作为基准key,例如取第一个元素A[0]
根据基准元素,将整个序列划分为左右两个序列(左侧小,右侧大于等于基准元素)
分别对左右两个子序列重复上述方法,直至各区间只有一个数,则排序完成
QuickSort(List){ if(List的长度大于1){ //将序列List划分为两个子序列LeftList,RightList QuickSort(LeftList); //分别对两个子序列排序 QuickSort(RightList); //将两个子序列合并为一个序列List } }
算法描述:
令i =A[0],j = A[n-1],基准值key=A[i]
j - -,由后向前找比key小的数,找到后将此数A[j]与A[i]交换位置
i++,由前向后找比key大的数,找到后将此数A[i]与A[j]交换位置
再重复执行2,3二步,直到i==j,最后令A[i]=key
实例说明:
数组下标 | 0 | 1 | 2 | 3 | 4 | 6 | 7 |
---|---|---|---|---|---|---|---|
数组值 | 25 | 35 | 17 | 18 | 33 | 29 | 20 |
j - -从后往前找比key小的数,j=7,A[7]=20符合,交换位置为:
数组下标 | 0 | 1 | 2 | 3 | 4 | 6 | 7 |
---|---|---|---|---|---|---|---|
数组值 | 20 | 35 | 17 | 18 | 33 | 29 | 25 |
i++从前往后找比key大的数,i=1,A[i]=35符合,交换位置为:
数组下标 | 0 | 1 | 2 | 3 | 4 | 6 | 7 |
---|---|---|---|---|---|---|---|
数组值 | 20 | 25 | 17 | 18 | 33 | 29 | 35 |
j - -从后往前找比key小的数,j=3,A[3]=18符合,交换位置为:
数组下标 | 0 | 1 | 2 | 3 | 4 | 6 | 7 |
---|---|---|---|---|---|---|---|
数组值 | 20 | 18 | 17 | 25 | 33 | 29 | 35 |
i++从后往前找比key大的数,直至i=j=3.
快速排序的算法
算法QuickSort是一个递归的算法。快速排序的关键部分是数组的划分,即算法Partition过程,利用序列第一个元素作为基准,将整个序列分为左右两个子序列。算法通过循环,将小于基准元素的移到序列左端,大于等于的移到右端。快速排序的算法:
void QuickSort(int list[],int left,int right){ //对元素list[left],...,list[right]进行排序 if(left<right){ int pivotpos=Partition(list,left,right); QuickSort(list,left,pivotpos-1); QuickSort(list,pivotpos+1,right); } } //返回序列调整后基准数的新位置 int Partition(int list[],int left,int right){ int i=left; int j=right; int key=list[left]; //基准 while(i<j){ // 从后往前找小于key的数 while(i<j&&list[j]>=key) j--; if(i<j){ list[i]=list[j]; //对应实例两数交换过程 i++; } // 从前往后找大于或等于key的数 while(i<j&&list[i]<key) i++; if(i < j){ list[j]=list[i]; //对应实例两数交换过程 j--; } } //i=j退出循环 list[i]=key; //将基准赋值给最后的空缺处 return i; //返回基准的新位置 }
上述将两个过程分开描述,其实可以将代码进行合并,如下:
void QuickSort(int list[],int left,int right){ //对元素list[left],...,list[right]进行排序 if(left<right){ //int pivotpos=Partition(list,left,right); int i=left; int j=right; int key=list[left]; //基准 while(i<j){ // 从后往前找小于key的数 while(i<j&&list[j]>=key) j--; if(i<j){ list[i]=list[j]; //对应实例两数交换过程 i++; } // 从前往后找大于或等于key的数 while(i<j&&list[i]<key) i++; if(i<j){ list[j]=list[i]; //对应实例两数交换过程 j--; } } //i=j退出循环 list[i]=key; //将基准赋值给最后的空缺处 QuickSort(list,left,i-1);//i为基准新位置 QuickSort(list,i+1,right); //QuickSort(list,left,pivotpos-1); //QuickSort(list,pivotpos+1,right); } }
快速排序的性能分析
由算法可知,快排的趟数取决于递归的深度。最坏情况时间复杂度为O(n2),最理想的情况是,一次划分后,左右子序列长度相等,下一步就是对两个长度减半的序列排序。在n个元素中,定位一个元素时间为O(n),若T(n)为排序所需时间则为:cn+2T(n/2),经过转换计算,最终得出快速排序的时间复杂度为O(nlogn)。
相关文章推荐
- lintcode 中等题:kth-largest-element 第k大元素
- leetcode:Kth Largest Element in an Array 使用快速选择算法,以及对其复杂度的分析
- LeetCode OJ 之 Kth Largest Element in an Array(数组中的第k大元素)
- [LintCode] 第K大元素 Kth Largest Element
- 【LeetCode-面试算法经典-Java实现】【215-Kth Largest Element in an Array(数组中第K大的数)】
- Leetcode 215 Kth Largest Element in an Array 数组中第k大的元素
- leetcode215---Kth Largest Element in an Array(第k大元素)
- The kth largest element in max-heap 最大堆的第k大元素
- Kth Largest Element in an Array(数组中第K大元素)
- [LeetCode] Kth Largest Element in an Array (找出数组的第k大的元素)
- [C++]Kth Smallest Element in a BST 在一个二叉排序树中找第k小的元素
- leetcode 215. Kth Largest Element in an Array 堆排序的简单应用 + 快速排序
- 读书笔记 算法导论 快速排序 QuickSort 使用最后一个元素作为pivot
- [LeetCode] Kth Largest Element in an Array 数组中第k大的数字
- 数组-Kth Largest Element in an Array(找出第K大的数)
- 面试算法:lg(k)时间查找两个排序数组合并后第k小的元素
- 字符串算法——查找数组第K个最大值( Kth Largest Element in an Array)
- [LeetCode] Kth Smallest Element in a BST 二叉搜索树中的第K小的元素
- 【Top K 问题】[Leetcode-215] Kth Largest Element in an Array 数组中第K大的数
- 215. Kth Largest Element in an Array 暴力-堆排序-快速排序