0前言
- 11 旋转数组的最小数字
- 53 数字在排序数组中出现的次数
- 50 第一个只出现一次的字符
查找算法
二分查找
11 旋转数组的最小数字
题目描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解题思路: 两个排序的数组,不能纯粹的二分查找(要求整体排序)。
1.取数组中间数,若中间的数大于等于数组第一个数,说明中间数位于前半个非递减数组中,因此最小值在数组的后半部分。
2.若中间数小于等于数组最后一个数,说明中间数位于后半个非递减数组中,因此最小值在数组的前半部分
3.直到指向数组前和后的指针下标相邻时,后面的指针对应数组的最小数字。
循环
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.empty())
return 0;
int low=0,high=rotateArray.size()-1,mid=low;
// 特殊情况:1. 没有旋转 2. 出现重复
while(rotateArray[low]>=rotateArray[high]){//确保是旋转的,如果没有旋转最小的就是第一个
mid=low+(high-low)/2;
//第一个指针low总是指向前面递增数组的元素,第二个指针high总是指向后面递增的数组元素。
// 最终指向两个相邻的元素,而第二个指针指向的刚好是最小的元素,这就是循环的结束条件。
if(high-low==1){
mid=high;
break;
}
if(rotateArray[mid]>=rotateArray[low])
low=mid;// 这里没有mid+1因为low=mid第一个指针仍然位于前半数组中
else if(rotateArray[mid]<=rotateArray[high])
high=mid;// high=mid第二个指针仍位于后半数组中
}
return rotateArray[mid];
}
};
法二:
public class Solution {
public int minNumberInRotateArray(int [] array) {
int low = 0 ; int high = array.length - 1;
while(low < high){
int mid = low + (high - low) / 2;
if(array[mid] > array[high]){
low = mid + 1;
}else if(array[mid] == array[high]){
high = high - 1;
}else{
high = mid;
}
}
return array[low];
}
}
严重注意:high=mid没关系,但low=mid有可能导致死循环。因为low=high-1时low=mid,如果又进入low=mid分支相当于搜索区间没缩小。但high一定大于mid,所以不会造成死循环。
根本原因:[4,6] array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;mid=(high+low)/2 偏向于左侧。
53 数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数。
思路:排序数组,考虑二分查找。分别找到该数第一次和最后一次出现的位置。
方法一:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
//搜索k-0.5和k+0.5这两个数应该插入的位置,然后相减即可。
return fun(data, k+0.5) - fun(data, k-0.5) ;
}
int fun(const vector<int> & data, double num){
int low = 0, high = data.size()-1,mid=low;
while(low <= high){
mid = low+(high-low)/2;
if(data[mid] < num)
low = mid + 1;// 这边用+1,1 最后low=high=mid,再来一次3>2.5,high-1;
else if(data[mid] > num)
high = mid - 1;
}
return low;// 不是用的mid
}
};
方法二:(递归+循环)
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
int len=data.size();
if(len==0)
return 0;
int firstk=getfirst(data,k,0,len-1);
int lastk=getlast(data,k,0,len-1);
if(firstk!=-1&&lastk!=-1){
return lastk-firstk+1;
}
return 0;
}
// 递归
int getfirst(vector<int> data ,int k,int low,int high){
if(low>high)
return -1;// 找不到
int mid=low;
mid=low+(high-low)/2;
if(data[mid]==k){
if(mid>=0&&data[mid-1]!=k)
return mid;
else
high=mid-1;
}
else if(data[mid]>k)
high=mid-1;
else if(data[mid]<k)
low=mid+1;
return getfirst(data,k,low,high);//递归,最终要继续这个函数
}
// 循环
int getlast(vector<int> data ,int k,int low,int high){
int len=data.size();
int mid=low;
while(low<=high){//循环<=
mid=low+(high-low)/2;
if(data[mid]==k){// mid和k相等,
if(mid+1<=len&&data[mid+1]==k)
low=mid+1;//循环里要让low一直向后走
else
return mid;//终止条件
}
else if(data[mid]<k)
low=mid+1;
else if(data[mid]>k)
high=mid-1;
}
return -1;//找不到,或错误输入
}
};
哈希表
50 第一个只出现一次的字符
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
法一:利用map
class Solution {
public:
int FirstNotRepeatingChar(string str) {
map<char,int> mp;// 构造map
for(int i=0;i<str.size();i++)
mp[str[i]]++;// 第一遍遍历,更新键值key与value
for(int i=0;i<str.size();i++){
if(mp[str[i]]==1)// 第二遍查找
return i;
}
return -1;
}
};
法二:数组 []
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.length()==0)
return -1;
int hash[256]={0};
// 第一次遍历,key-value
int i=0;
while(str[i]!='\0'){
hash[str[i]]++;
i++;
}
// 第二次查找
i=0;
while(str[i]!='\0'){
if(hash[str[i]]==1){
return i;
}
i++;
}
return -1;
}
};