题目描述
统计一个数字在排序数组中出现的次数。
思路一:
利用二分法找到等于k的位置,但可能左右两边都有k,所以需要在左右两边顺序扫描,分别找出第一个k和最后一个k。
因为要查找的数字在长度为n的数组中可能出现n次,所以时间复杂度是O(n)
代码:
class Solution { public: int GetNumberOfK(vector<int> data ,int k) { if(data.size()==0) return 0; int low=0; int high=data.size()-1; while(low<=high) { int mid=(low+high)/2; if(k>data[mid]) low=mid+1; else if(k<data[mid]) high=mid-1; else { int i,j; for(i=mid;i>=0;i--) if(data[i]!=k) break; for(j=mid+1;j<=data.size();j++) if(data[j]!=k) break; return j-i-1; } } return 0; } };思路二:
仍然使用二分查找思想,尝试不使用两边扫描,便能找到第一个k和最后一个k
分析:当有数字和K相等时,我们先判断这个数字是不是第一个k。如果中间数字的前面一个数字不是K,则此时中间的数字刚好就是第一个k,同理,如果中间数字的后面一个数字不是K,那么中间的数字就是最后一个k
因为GetFirstK和GetLastK都是利用二分查找,所以总的时间复杂度O(logn)
代码:
class Solution { public: int GetNumberOfK(vector<int> data ,int k) { int n=data.size(); int number=0; if(n>0) { int first=GetFirstK(data,k,0,n-1); int last=GetLastK(data,k,0,n-1); if(first>-1&&last>-1) number=last-first+1; } return number; } //第一个k的位置 int GetFirstK(vector<int> data ,int k,int low,int high) { if(low>high) return -1; int mid=(low+high)/2; if(k==data[mid]) { if(mid>=0&&data[mid-1]!=k) return mid; else high=mid-1; } else if(k>data[mid]) low=mid+1; else high=mid-1; return GetFirstK(data,k,low,high); } //最后一个k的位置 int GetLastK(vector<int> data ,int k,int low,int high) { if(low>high) return -1; int mid=(low+high)/2; if(k==data[mid]) { if(mid>=0&&data[mid+1]!=k) return mid; else low=mid+1; } else if(k>data[mid]) low=mid+1; else high=mid-1; return GetLastK(data,k,low,high); } };
举一反三:
0~n-1中缺失的数字
一个长度为n-1的递增顺序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
思路一:
算出当前数组的数组和s1,和数字0~n-1之间的数字和(n(n-1)/2)s2,利用s2-s1就是不在数组中的数字。
时间复杂度O(N)
思路二:
1、由于数组是递增的,且每个数字都是在0~n-1之内,所以在不缺少任何数字的情况下,元素值和下标值应该是相等的。
2、记不在数组中的数字为m,则所有比m小的数字的下标都与他们的值相同,比m大的值下标都与其值相差1.
3、因此问题转化为找出数组中第一个元素值与其下标不相等的元素。
代码:
int GetMissingNumber(vector<int>num) { int n=num.size(); if(n==0) return -1; int left=0; int right=n-1; while(left<=right) { int mid=(left+right)/2; if(num[mid]!=middle) { if(middle===0||num[mid-1]==mid-1) return mid; right=mid-1; } else left=mid+1; } if(left==n) return n; //无效输入,数组没有按照要求排序,或者有数字不在0~n-1范围之内 return -1; }
数组中数值和下标相等的元素
思路:
由上一题得到启发,我们可以先分析数组{-3,-1,1,3,5},我们发现,元素值与其下标的差是递增且过0的,-3,-2,-1,0,1,当差值等于零时,就是我们想要的值。
因此,我们根据差值与0比较利于二分法找出元素值与下标相同的值。
代码:
int GetNumberSameAsIndex(vector<int>num) { int n=num.size(); if(n==0) return -1; int left=0; int right=n-1; while(left<=right) { int mid=(left+right)/2; if(num[mid]-mid>0) right=mid-1; else if(num[mid]-mid<0) left=mid+1; else return mid; } return -1; }