【算法】O(1)空间,不改变原数组的情况下找第K大的数
2017-04-15 16:35
225 查看
找到无序数组中第K大的数,这道题蜜汁经典,腾讯和头条的面试中都被问到了,我一般用的都是大小为K的小顶堆和快排扫一半这两种思路做这道题,直到前几天面试头条的时候,终面面试官问,有没有O(1)空间【严格的O(1)空间,即不能用递归】且不改变原数组的情况下,找第K大的数。当场没有想起来,下来之后突然就想到了。所以特此来总结一下。
找数组第K大的数 ,Leetcode上有一道原题:
215. Kth Largest Element in an Array
这种方法的时间复杂度,平均意义上是O(n)
这种算法的复杂度是O(nlogK)
这种方法的复杂度是需要讨论的,如果数组中全都是int类型的整数,那么时间复杂度是O(n)
因为本质上查找一遍数组需要O(n),而至多需要查找log(Max−Min)≤32所以至多查找32遍,所以复杂度还是O(n)的
PS,这种方法其实还是只是适用于整数,因为如果是浮点数的话,其复杂度是不可估计的。
找数组第K大的数 ,Leetcode上有一道原题:
215. Kth Largest Element in an Array
常规的两种方法——小顶堆和快排分治
快排扫一半
就是快排每次扫完之后,看轴值得位置在哪里,如果大于k则扫前一半,小于k则扫后一半,如果等于k则说明找到了【PS,这里的k是指从0开始数的第K大的数】这种方法的时间复杂度,平均意义上是O(n)
int findKsort(vector<int>& nums,int k,int left,int right){ if(left==right) //其实这一步也可以不加的,因为下面有判断出如果相等的话,就直接返回了 return nums[left]; int i = left; int j = right; int q = nums[j]; while(i<j){ while(i<j && nums[i]>q) i++; nums[j] = nums[i]; while(i<j && nums[j]<=q) j--; nums[i] = nums[j]; } nums[i] = q; if(i == k) return nums[i]; if(i>k) return findKsort(nums,k,left,i-1); //顺便要注意这个地方是i-1和i+1,不能是i否则会死循环。 else return findKsort(nums,k,i+1,right); } int findKthLargest(vector<int>& nums, int k) { return findKsort(nums,k-1,0,nums.size()-1); }
K大小的小顶堆
这种方法就是,先把数组中前K个数建立一个小顶堆,然后再依次往后扫,如果小于堆顶元素则继续往后扫,如果大于堆顶元素,则把堆顶元素pop出,然后再push进这个元素。这种算法的复杂度是O(nlogK)
int findKthLargest(vector<int>& nums, int k) { priority_queue<int,vector<int>,greater<int>> q; for(int i=0;i<k;i++) q.push(nums[i]); for(int i = k;i<nums.size();i++){ if(nums[i]>q.top()){ q.pop(); q.push(nums[i]); } } return q.top(); }
O(1)空间,不改变原数组的做法
这其实也是一个二分法啦,只不过之前的二分法都是从数组索引的角度去二分,而这次等于是二分一个数是否是满足第K大数的条件。 【即相当二分法求一个单调函数值f(x)−k=0,依次去二分x看这个函数什么时候相等。】这种方法的复杂度是需要讨论的,如果数组中全都是int类型的整数,那么时间复杂度是O(n)
因为本质上查找一遍数组需要O(n),而至多需要查找log(Max−Min)≤32所以至多查找32遍,所以复杂度还是O(n)的
PS,这种方法其实还是只是适用于整数,因为如果是浮点数的话,其复杂度是不可估计的。
int findKthLargest(vector<int>& nums, int k) { int left = INT_MAX; int right = INT_MIN; for(auto x:nums){ left = min(x,left); right = max(x,right); } while(left<=right){ int mid = left + (right-left)/2; int count1 = 0; int count2 = 0; for(auto x:nums){ if(x>=mid) count1++; if(x>mid) count2++; } if(count1 >=k && count2 <k) return mid; if(count1 <k) right = mid-1; else left = mid+1; } return -1; }
相关文章推荐
- 算法有序数组合并---在空间足够的情况下,进行O(n)的合并 并且移动次数最小
- 算法,不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- [算法学习]不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 求一个数组中第k大的数,要求不能另外申请空间,数组顺序不能被改变
- [算法学习]不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 在数组A中寻找第k小的元素-最坏情况为线性时间的算法
- 一个数组中,存在两个只出现一次的数字,其余的数字均出现两次。要求在时间复杂度o(n),空间复杂度为o(1)的情况下找出这两个数字
- 长度为n的整数数组,找出其中任意(n-1)个乘积最大的那一组,只能用乘法,不可 以用除法。要求对算法的时间复杂度和空间复杂度作出分析,可以写思路也可以写程序。
- 给定一个单向链表(长度未知),请设计一个既节省时间又节省空间的算法来找出该链表中的倒数第m个元素。实现这个算法,并为可能出现的特例情况安排好处理措施。“倒数第m个元素”是这样规定的:当m=0时,链表的
- 算法题:将一个数组中所有奇数放前面和偶数放后面(不开辟新的内存空间)
- O(n)算法得到数组中任意第k大的数字
- 从一个数组中找出第k小元素的随机化算法 c语言实现 算法导论第九章
- 【2012年百度实习生算法题】数组al[0,mid-1]和 al[mid,num-1],都分别有序。将其merge成有序数组al[0,num-1],要求空间复杂度O(1)
- leetcode:Remove Duplicates from Sorted Array(去掉数组重复数字,常数空间限制)【面试算法题】
- 【每天学点算法题10.18】找出数组中的第K大的数
- 算法导论:快速找出无序数组中第k小的数
- 不改变正负数之间相对顺序重新排列数组(时间O(N),空间O(1))(桶排序)
- 算法设计--查找无序数组中第K大的数字
- 算法设计--查找无序数组中第K大的数字
- 设计算法并写出代码移除字符串中重复的字符,不能使用额外的缓存空间。注意: 可以使用额外的一个或两个变量,但不允许额外再开一个数组拷贝。