排序相关面试题总结
来源:互联网 发布:core java mobi 编辑:程序博客网 时间:2024/05/16 14:01
- 旋转数组中的最小元素如{3,4,5,1,2},就是{1,2,3,4,5}左旋两个得到,因为数组可以分为两个递增的部分,左边大于右边,用二分法的思想,中间元素如5大于2,说明5在左边部分,最小元素在右半部分,缩小区间到{5, 1, 2}之间。时间复杂度为O(logN).
注:如果左边元素与右边元素中间元素相同,需要另外处理。如{1,1,1,0,1}和{1,0,1,1,1}都是旋转数组,无法用二分法判断。
int _Min(int *arr, int left, int right){ if (NULL == arr || left < 0 || right < 0 || left > right) throw new exception("参数错误"); for (int i = left+1; i <= right; ++i) { if (arr[left] > arr[i]) arr[left] = arr[i]; } return arr[left];}int Min(int *arr, int sz){ if (NULL == arr || sz < 0) throw new exception("参数错误"); int left = 0; int right = sz - 1; int mid = 0; while (arr[left] >= arr[right]) { mid = left + ((right - left) >> 1); if (right - left == 1) { arr[mid] = arr[right]; break; } if (arr[left] == arr[right] && arr[left] == arr[mid]) { return _Min(arr, left, right); } else if (arr[mid] >= arr[right]) { left = mid; } else { right = mid; } } return arr[mid];}
2.数组中出现超过一半的数字
数组中超过一半的数字排序后一定位于数组的中间位置。但是全部排序耗费时间。
快速排序可以将数组分为两部分,左半部分小于等于关键值,右半部份大于等于关键值,当关键值恰好位于中间位置时,它就是要找的元素。
注:这种方法会改变数组的顺序
int partition(int *arr, int left, int right){ int slow = left - 1; for (int fast = left; fast < right; ++fast) { if (arr[fast] < arr[right]) { slow++; if (slow != fast) swap(arr[slow], arr[fast]); } } ++slow; swap(arr[slow], arr[right]); return slow;}bool invalied = false;bool check(int *arr, int sz, int ret){ int count = 0; for (int i = 0; i < sz; ++i) { if (arr[i] == ret) count++; } if (2 * count > sz) { invalied = true; return ret; } else return 0;}int MoreThanHalf(int *arr, int sz){ if (NULL == arr || sz < 1) return 0; int index = partition(arr, 0, sz - 1); int left = 0; int right = sz - 1; int mid = (sz >> 1); while (index != mid) { if (index > mid) index = partition(arr, left, index - 1); else index = partition(arr, index + 1, right); } int ret =check (arr,sz,arr[mid]); return ret;}
如果不希期忘改变顺序,可以用计数的方法,ret与下一次数相同加一,小于0更新ret。超过一半最后一定会留下。
int MoreThanHalf_2(int *arr, int sz){ invalied = false; if (NULL == arr || sz < 1) return 0; int result = arr[0]; int times = 1; for (int i = 1; i < sz; ++i) { if (times == 0) { result = arr[i]; times = 1; } else if (arr[i] == result) times ++ ; else times--; } result = check(arr, sz, result); return result;}
3.数组最小的K个数
方法与上一题相同,快速排序可以将数据分为两部分,小于关键值,大于关键值得,如果关键值下标恰好是K,前面的K个数就是要找的数。
注:会改变数据的顺序
void SmallKnum(int *arr, int sz, int K){ if (NULL == arr || sz < K) return; int left = 0; int right = sz - 1; int index = partition_(arr, left, right); while (index != K) { if (index < K) { left = index + 1; index = partition_(arr, left, right); } else { right = index - 1; index = partition_(arr, left, right); } } for (int i = 0; i < index; ++i) cout << arr[i] << " "; cout << endl;}
用multiset(底层红黑树,可以报存相同的元素)/大堆, 保存K个最小值,有值小于multiset中的最大值时,更新。
这里用multiset实现:
typedef multiset<int, greater> Set;typedef multiset<int, greater>::iterator Setit;void SmallKnum_(int *arr, int sz, int K){ if (NULL == arr || sz < 1 || sz < K) return; Set myset; Setit it = myset.begin(); for (int i = 0; i < sz; ++i) { if (myset.size() < K) { myset.insert(arr[i]); } else { Setit it = myset.begin(); if (arr[i] < *myset.begin()) { myset.erase(it); myset.insert(arr[i]); } } } for (it = myset.begin() ; it != myset.end(); ++it) cout << *it << " "; cout << endl;}
4.数组中的逆序对
用归并排序,先将数组分到最小,排序合并。
int _InverseMerge(int *arr, int *copy, int left, int right){ if (left == right) return 0; int mid = (left + ((right - left) >> 1)); int leftPairs = _InverseMerge(arr, copy, left, mid); int rightPairs = _InverseMerge(arr, copy, mid+1, right); int end = right; int i = mid; int j = right; int Pairs = 0; while (left <= i && mid + 1 <= j) { if (arr[i] > arr[j]) { Pairs += (j - mid); copy[right--] = arr[i]; i--; } else { copy[right--] = arr[j]; j--; } } if (left <= i) { copy[right--] = arr[i--]; } if (mid + 1 <= j) { copy[right--] = arr[j--]; } for (; left <= end; ++left) { arr[left] = copy[left]; } return Pairs + leftPairs + rightPairs;}int InversePairs(int arr[], int sz){ if (NULL == arr || sz < 2) return 0; int *copy = new int[sz]; return _InverseMerge(arr, copy, 0, sz-1); delete[]copy;}
5.数字在排序数组中出现的次数
排序好的数组可以用二分法,因为是重复数字,先找第一次出现的位置,找到K,如果K左边的数字相同,继续往左边找,直到下标为0或者左边元素不相同为止,同方法找到最后一次出现的位置。两个相减就是出现的次数。
int GetFirstK(int *arr, int sz, int K){ if (NULL == arr || sz < 1) return -1; int left = 0; int right = sz - 1; while (left <= right) { int mid = left + ((right - left) >> 1); if (arr[mid] > K) { right = mid - 1; } else if (arr[mid] < K) { left = mid + 1; } else { if (mid == 0) return mid; else if (arr[mid - 1] != K) return mid; else right = mid - 1; } } return -1;}int GetLastK(int *arr, int sz, int K){ if (NULL == arr || sz < 1) return -1; int left = 0; int right = sz - 1; while (left <= right) { int mid = left + ((right - left) >> 1); if (arr[mid] > K) { right = mid - 1; } else if (arr[mid] < K) { left = mid + 1; } else { if (mid == sz-1) return mid; else if (arr[mid + 1] != K) return mid; else left = mid + 1; } } return -1;}int HasMoreK(int *arr, int sz, int K){ int lastK = GetLastK(arr, sz, K); int firstK = GetFirstK(arr, sz, K); if (lastK == -1 || firstK == -1) return 0; return lastK - firstK + 1;}
6.扑克牌是否为顺子,大王可以看做任意牌
把扑克牌用数字替代,A为1,JQK为,11,12,13,大王看作0,先对数据排序,统计0的个数,在统计相差(两张牌之间差几个)的个数,看是否能用0抵消。
int partition_2(int *arr, int left, int right){ int slow = left - 1; for (int i = left; i < right; ++i) { if (arr[i] < arr[right]) { slow++; if (slow != i) swap(arr[i], arr[slow]); } } ++slow; swap(arr[slow], arr[right]); return slow;}void qSort(int *arr, int left, int right){ if (left < right) { int index = partition_2(arr, left, right); qSort(arr, left, index - 1); qSort(arr, index + 1, right); }}//把大王看作0bool IsContinue(int *arr, int sz){ if (NULL == arr || sz < 1) return false; qSort(arr,0,sz-1); //0的数目 int zeronums = 0; for (int i = 0; i < sz; ++i) { if (0 == arr[i]) zeronums++; else break; } //第一个不为0的下标 int small = zeronums; int big = small + 1; int difference = 0; while (big < sz) { //如果相等一定不为顺子 if (arr[big] == arr[small]) return false; //计算相邻卡片多余的差 difference += (arr[big] - arr[small] - 1); small++; big++; } //多余的差是否能用0填充 return (difference <= zeronums );}
7.n个数,数的范围是0~n-1。求任意一个重复出现的数。
由于数的大小在0~n-1之间,所以可以用arr[n] = n的顺序排序,如{2, 3, 1, 0, 2, 5, 3},从第一个数开始,下标为0,值为2,不匹配,把2交换到下标为2的位置.
{1, 3, 2, 0, 2, 5, 3},不匹配,把1交换到下标为1的位置
{3, 1, 2, 0, 2, 5, 3},不匹配,把3交换到下标为3的位置
{0, 1, 2, 3, 2, 5, 3}匹配,下标为1,2,3也匹配,跳过
下标为4的值是2,交换,发现与下标为2的值相同,则找到相同元素返回。
这种方法每次查找都会把一个元素归位,所以最坏是O(n)。
bool repeatnum(int *arr, int sz){ if (NULL == arr || sz < 2) return false; for (int i = 0; i < sz;++i) { while (arr[i] != i) //不等于下标交换位置 { //待交换位置上的元素与当前元素相同,找到重复元素 if (arr[i] == arr[arr[i]]) { cout << arr[i] << endl; return true; } swap(arr[i], arr[arr[i]]); //移动元素位置 } } return false; //没找到重复元素}
- 排序相关面试题总结
- TCP相关面试题总结
- 概率相关面试题总结
- TCP相关面试题总结
- TCP相关面试题总结
- TCP相关面试题总结
- 单链表相关面试题总结
- 前端相关面试题总结
- 面试题、排序、socket总结
- 链表相关的面试题总结
- 子串子序列相关面试题总结
- javascript字符串相关的面试题总结
- 面试题二叉树相关问题总结
- HDFS的相关面试题总结
- 前端面试总结---CSS相关面试题
- 《STL源码剖析》相关面试题总结
- 队列和栈相关面试题总结
- 链表相关面试题总结大全
- js学习入坑第二篇
- C中str家族和mem家族的简介
- UVA
- List<T>转为Excel和Excel解析Map
- hdu3999 The order of a Tree二叉搜索树
- 排序相关面试题总结
- 随机二叉平衡树treap学习
- JS选择结构3
- Java——介绍
- jQuery多库冲突
- Spring boot 学习总结
- Jave集合框架之_LinkedList独有特性
- 一次印象不错的面试
- 喷水装置(一)