剑指Offer刷题记录_day4
简单查找
Q1 查找数组重复元素
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
简单思路1:采用空间换时间,开辟数组记录出现次数,出现第一个大于1时则返回;未找到则返回-1;
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int num[100001];
for(int i = 0; i < 10001; i++)
num[i] = 0;
for(int i = 0; i < nums.size(); i++)
{
if(++num[nums[i]] > 1) return nums[i];
}
return -1;
}
};
Better idea : 遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = inum**s[i]=i )。因而,就能通过索引映射对应的值,起到与字典等价的作用。
-
遍历数组 numsnums ,设索引初始值为 i = 0i=0 :
-
若 nums[i] = inums[i]=i : 说明此数字已在对应索引位置,无需交换,因此跳过;
-
若 nums[nums[i]] = nums[i]nums[nums[i]]=nums[i] : 代表索引 nums[i]nums[i] 处和索引 ii 处的元素值都为 nums[i]nums[i] ,即找到 一组重复值,返回此值 nums[i]nums[i] ;
-
否则: 交换索引为 ii 和 nums[i]nums[i] 的元素值,将此数字交换至对应索引位置。
-
若遍历完毕尚未返回,则返回 -1
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int i = 0;
while(i < nums.size()) {
if(nums[i] == i) {
i++;
continue;
}
if(nums[nums[i]] == nums[i])
return nums[i];
swap(nums[i],nums[nums[i]]);
}
return -1;
}
};
Q2统计一个数字在排序数组中出现的次数
有序数组首先该想到是二分查找:两次二分查找确定指定数字数组边界,然后做差
class Solution {
public int search(int[] nums, int target) {
if(nums.length == 0) {
return 0;
}
//初始左右指针位置
int i = 0;
int j = nums.length-1;
//第一次二分:找right边界
while(i <= j) {
int mid = (i+j) >> 1;
if(nums[mid] <= target){
i = mid+1;
}
else{
j = mid-1;
}
}
if(j>=0&&nums[j] != target){
return 0;
}
int right = i; //更新right边界
//重置指针
i = 0;
//第二次二分:找left边界
while(i <= j) {
int mid = (i+j) >> 1;
if(nums[mid] >= target){
j = mid-1;
}
else{
i= mid+1;
}
}
//更新左指针
int left = j;
return right-left-1;
}
}
Q3 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
基本思路:有序数组,整体框架还是使用二分查找;根据题目,若不缺的话数组0-n-1的位置上正好是0-n-1;
而现在是缺少一个数,即从某个数开始,下标和数字就开始不对应了,由此出发可以设置二分查找的判断条件.
class Solution {
public:
int missingNumber(vector<int>& nums) {
int i=0 , j = nums.size()-1;
while(i<=j)
{
int mid = (i+j)/2;
if(mid == nums[mid]) i = mid+1;
else j = mid - 1;
}
return j+1;
}
};