题目一
统计一个数字在排序数组中出现的次数。
思路一
当然可以无脑用哈希表,空间复杂度O(n),时间复杂度O(1)
class Solution { public: int GetNumberOfK(vector<int> data ,int k) { if (data.size() == 0) return 0; int length = data.size(); map<int,int> mp; for (int i = 0; i < length; i++) mp[data[i]]++; return mp[k]; } };
思路二
哈希表法其实没有利用排序数组的条件。在排序数组中,可以用二分法来寻找k
但问题出在,就算我们找到了数组中间的数等于k,我们还是需要向左向右遍历找到头和尾,在这一步上会花很大的复杂度,最差O(n)。
因此,在找第一个k时,首先也是二分找到中间的k,然后和它上一个数字相比,如果还是k,则在前半段数组继续二分,直到找到中间的数为k且前一个不等于k。
用同样的方法,找最后一个k
时间复杂度为O(logn)
class Solution { public: int GetNumberOfK(vector<int> data ,int k) { int length = data.size(); int count = 0; if (length == 0) return 0; int firstK = GetFirstOfK(data, k, 0, length - 1); int lastK = GetLastOfK(data, k, 0, length - 1); if (firstK > -1 && lastK > -1) count = lastK - firstK + 1; return count; } int GetFirstOfK(vector<int> data, int k, int start, int end){ if (start > end) return -1; int mid = (start + end) / 2; int mid_data = data[mid]; if (mid_data > k) end = mid - 1; else if (mid_data < k) start = mid + 1; else if (mid_data == k) { if ( (mid_data != data[mid-1] && mid > 0) || mid == 0) return mid; else end = mid - 1; } return GetFirstOfK(data, k, start, end); } int GetLastOfK(vector<int> data, int k, int start, int end){ if (start > end) return -1; int mid = (start + end) / 2; int mid_data = data[mid]; if (mid_data > k) end = mid - 1; else if (mid_data < k) start = mid + 1; else if (mid_data == k) { if ( (mid_data != data[mid+1] && mid < end - 1) || mid == end) return mid; else start = mid + 1; } return GetLastOfK(data, k, start, end); } };
题目二
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在0~n-1之内。在范围0~n-1内的n个数字有且只有一个不在数组中,找出该数字。
思路一
题目很微妙,相当于0~n-1个数字中只有一个不在数组里。
当然我们求出0~n-1个数字的和为s1,然后求出数组数字之和为s2
s1-s2就是我们要找的数字,但是这也没有利用数组的递增性,时间复杂度为O(n)
思路二
遇到有序数组中的查找,往往要想到二分,因为能把复杂度降到O(logn)
data[index_mid] = data_mid
1、若 index_mid = data_mid,则要找的数字在右半段
2、(a) 若 index_mid != data_mid 且 data[index_mid - 1] = index_mid - 1,也就是当前索引与值不等,但上一个索引与数值相等,则index_mid就是我们要找的数字
(b) 若 index_mid != data_mid 且 data[index_mid - 1] != index_mid - 1,那么我们要找的数字在左半段
class Solution { public: int GetMissingNumber(vector<int> data) { int length = data.size(); if(length == 0) return -1; int start = 0, end = length - 1; while (start <= end) { int index_mid = (start + end) / 2; int data_mid = data[index_mid]; if (index_mid != data_mid){ if (data[index_mid-1] == index_mid - 1 || index_mid == 0) //处理了0不在数组里 return index_mid; else end = index_mid - 1; } else start = index_mid + 1; } // 很关键,比如 0 1 2 3 4,每个数字都是对应相等,此时start=5,因此5是要找的数字 if(start == length) return length; return -1; // 无效的输入,如数组不是递增,输入数字不在范围 } };
题目三
假设单调递增的数组里每个元素都是整数且都是唯一的。请实现函数,找出数组中任意一个数值等于其下标的元素。如
{-3,-1,1,3,5},数字3和下标相等
思路
value | -3 | 0 | 1 | 2 | 4 | 6 |
index | 0 | 1 | 2 | 3 | 4 | 5 |
很容易发现,在下标和数值相等的左边,索引会大于(或者等于)数值,在下标与数值相等的右边,索引会小于(等于)数值。
根据这个规律就可以二分查找
若Index = value,则找到
若index > value,在右半段
若index < value,在左半段
(代码略,很简单)