1.数组中出现次数超过一半的数字
思路一:数组中出现次数超过一半的数字是排好序后数组的中位数,我们采用随机快速排序的思路,先在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中数字小的数字都在他的左面,大的都在他的右面。如果选中数字的下标刚好是2/n,这个数就是中位数,如果小于,则中位数位于其右面,如果大于,中位数位于其左面。可以继续进行递归查找。
注:要注意无效操作的情况(数组为空,长度小于1),以及数组中本来就没有出现次数超过一半的数字。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { int len=numbers.size(); if(len<=0) return 0; int mid=len>>1; int start=0; int end=len-1; int index=Partition(numbers,len,start,end); while(index!=mid) { if(index>mid) { end=index-1; index=Partition(numbers,len,start,end);//中位数在左面 } else { start=index+1; index=Partition(numbers,len,start,end);//中位数在右面 } } int result=numbers[index]; int times=0; for(int i=0;i<len;i++) { if(numbers[i]==result) times++;//判断出现次数是否超过一半 } return times*2>len?result:0; } int Partition(vector<int> &numbers,int length,int start,int end)//快排核心函数 { if(length<=0||start<0||end>=length) return false; int index=RandomInRange(start,end); Swap(&numbers[index],&numbers[end]);//随机生成一个点,与尾结点互换 int small=start-1; for(index=start;index<end;index++) { if(numbers[index]<numbers[end]) { ++small; if(small!=index) Swap(&numbers[index],&numbers[small]);//说明small与index直接有大于end下标的数字 } } ++small; Swap(&numbers[small],&numbers[end]); return small; } int RandomInRange(int start,int end) { srand (time(NULL)); return rand() % (end-start+1) + start;//随机数函数 } void Swap(int *a,int *b) { int temp=*a; *a=*b; *b=temp; } };
思路二:遍历数组时保存两个值,一个是数组中的一个数字,一个是次数,如果下一个数字与此数字相同,那么次数加一,如果不相同,次数减一。如果次数为0,那么保存下一个数字,重置次数为1.由于要找的数字出现的次数比其他所有数字出现的次数之和还要多,因此最后一个次数被设为1的数字就是要找的数字。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { int len=numbers.size(); if(len<=0) return 0; int result=numbers[0];//当前数 int count=1;//次数 for(int i=1;i<len;i++) { if(numbers[i]!=result) { count--;//不等于,次数-- } else { count++;//等于,次数++ } if(count==0) { result=numbers[i]; count=1;//次数为0,重置 } } int times=0; for(int i=0;i<len;i++) { if(numbers[i]==result) times++; } return times*2>len?result:0; //加一个判断 } };
2.最小的k个数
思路一:仍基于快排,如果基于数组的第k个数字来调整,则使得比第k个数字小的所有数字都位于数组的左边,比k个数字大的所有数字都位于数组的右边,这样调整之后,位于数组左中左边的k个数字就是最小的k个数字。
class Solution { public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { vector<int> res; int len=input.size(); if(len<=0||k<=0||k>len) return res; int start=0; int end=len-1; int index=Partition(input,len,start,end); while(index!=k-1) { if(index>k-1) { end=index-1; index=Partition(input,len,start,end); } else { start=index+1; index=Partition(input,len,start,end); } }//直到index为k时,此时前k个数就是最小的k个数 for(int i=0;i<k;i++) res.push_back(input[i]); return res; } int Partition(vector<int> &numbers,int length,int start,int end)//快排核心函数 { if(length<=0||start<0||end>=length) return false; int index=RandomInRange(start,end); Swap(&numbers[index],&numbers[end]);//随机生成一个点,与尾结点互换 int small=start-1; for(index=start;index<end;index++) { if(numbers[index]<numbers[end]) { ++small; if(small!=index) Swap(&numbers[index],&numbers[small]);//说明small与index直接有大于end下标的数字 } } ++small; Swap(&numbers[small],&numbers[end]); return small; } int RandomInRange(int start,int end) { srand (time(NULL)); return rand() % (end-start+1) + start;//随机数函数 } void Swap(int *a,int *b) { int temp=*a; *a=*b; *b=temp; } };
思路二:用于处理海量数据,利用最大堆(根节点的值总是大于它的子树中任意节点的值)或者红黑树(STL里面的set和multiset)
class Solution { public: typedef multiset<int,greater<int>> intSet; typedef multiset<int,greater<int>>::iterator setIterator; void GetLeastNumbers_Solution(const vector<int> &input, int k,intSet& leastNumbers) { leastNumbers.clear(); if(k<1||input.size()<k) return; vector<int>::const_iterator iter=input.begin(); for(;iter!=input.end();iter++) { if(leastNumbers.size()<k) { leastNumbers.insert(*iter);//先插入k个数 } else { setIterator iterGreatest=leastNumbers.begin(); if(*iter<*(leastNumbers.begin())) { leastNumbers.erase(iterGreatest); leastNumbers.insert(*iter);//如果小于set里的最大值,就替换掉最大值 } } } } };
3.数据流中的中位数
思路一:如题1,利用快速排序的partition函数寻找中位数。
思路二:最大堆和最小堆。
class Solution { public: vector<int> res; void Insert(int num) { res.push_back(num); } double GetMedian() { sort(res.begin(),res.end()); int len=res.size(); if(len%2==1) return (double)res[len/2]; else return (double)((res[len/2]+res[len/2-1])/2.0); } };