【问题】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
【思路】首先,第一个思路,我们不考虑空间复杂度,这种在笔试时最好用,使用一个哈希表,然后遍历,由于unordered_map中不允许重复的key,因此每遍历到相同的key,value就加一。然后判断这个值大不大于数组的一半,如果大于,直接返回即可,否则返回零。
class Solution { public: int MoreThanHalfNum_Solution(vector<int> numbers) { unordered_map<int, int> hash_map; if(numbers.size() == 1) return 1; for(auto i: numbers){ hash_map[i]++; if(hash_map[i] > (numbers.size() / 2)){ return i; } } return 0; } };
另一种思路时,先对整个序列进行排序操作,如果数组中一个数的数量超过这个数组的一半,那么对整个数组排序后,这个数一定位于数组的中间位置!那么既然要排序,我们这里使用快排对数组进行排序,O(Nlogn),虽然有人可以优化到O(n),但是代码太多太复杂了!经过排序后,我们首先获得中间位置的值,然后遍历整个排序数组,统计这个值的个数,如果确实大于size/2,则返回这个数的个数!既然你要学算法,就尽量别调库了,老老实实自己写个快排!
class Solution { public: vector<int> qs_partition(vector<int>& list, int L, int R){ int more = R; // 选取最后一个数为分界点 int less = L - 1; int cur = L; vector<int> res(2); while(cur < more){ if(list[cur] < list[R]){ swap(list[++less], list[cur++]); }else if(list[cur] > list[R]){ swap(list[--more], list[cur]); // 交换后cur有可能小于中间值,还需要再次交换,因此cur不进行自加 }else{ cur++; } } swap(list[more], list[R]); res[0] = less+1; res[1] = more; return res; } // 经典快排 void QuickSort(vector<int>& list, int L, int R){ if(list.size() < 2) return; if(L < R){ vector<int> p = qs_partition(list, L, R); QuickSort(list, L, p[0]-1); QuickSort(list, p[1]+1, R); // 递归处理其他部分 } } int MoreThanHalfNum_Solution(vector<int> numbers) { if(numbers.size() == 0) return 0; int size = numbers.size(); QuickSort(numbers, 0, size-1); int mid = size / 2; // 获得中间位置的值 int count = 0; for(auto num: numbers){ if(num == numbers[mid]){ count++; // 统计中间位置值的数量 if(count > (size / 2)){ return num; } } } return 0; } };