[leetcode]第 4 天 查找算法(简单)
03. 数组中重复的数字
思路
去重,很容易想到用HashSet
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> s = new HashSet<>();
for(int num : nums){
if(s.contains(num)) return num;
s.add(num);
}
return -1;
}
}
其他解法:
数组元素的 索引 和 值 是 一对多 的关系。
因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = inums[i]=i )
class Solution {
public int findRepeatNumber(int[] nums) {
int i = 0;
while(i < nums.length) {
if(nums[i] == i) {
i++;
continue;
}
if(nums[nums[i]] == nums[i]) return nums[i];
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
return -1;
}
}
53.I. 在排序数组中查找数字 I
思路
很简单的题,就是循环比较num和target
class Solution {
public int search(int[] nums, int target) {
int count = 0;
for(int num : nums){
if(num == target) count++;
}
return count;
}
}
遂至评论区看其他解法,然后发现,喔!二分查找
排序数组的搜索问题,首先想到二分法解决
本题要求统计排序数组nums种target的出现次数,可以转化为使用二分法找到左边界left和右边界right,target的数量=right-left - 1
流程:
1. 初始化:左边界i = 0,右边界j = len(nums) - 1
2. 循环二分:闭区间[i, j]无元素时跳出
- 计算中点m = (i + j)/2
- nums[m] < target,则target在闭区间[m + 1, j]中,i = m + 1;
- nums[m] > target,则target在闭区间[i, m - 1]中,j = m - 1;
- nums[m] = target, 右边界right在[m + 1, j]中,left在[i, m - 1]中,分两种情况:
1. 查找右边界right,i = m + 1;
2. 查找左边界left,j = m - 1;
3. 返回:right - left - 1
简化:
由于数组中全为整数,可以二分查找target和target - 1的右边界
class Solution {
public int search(int[] nums, int target) {
return helper(nums, target) - helper(nums, target - 1);
}
int helper(int[] nums, int tar){
int i = 0, j = nums.length - 1;
while(i <= j){
int m = (i + j)/2;
if(nums[m] <= tar) i = m + 1;
else j = m - 1;
}
return i;
}
}
这个简化实在是太迷人了!
53 - II. 0~n-1中缺失的数字 II
思路
二分查找!分左右子组
流程:
1. 初始化:左边界i = 0,右边界j = len(nums) - 1
2. 循环二分:闭区间[i, j]无元素时跳出
- 计算中点m = (i + j)//2
- nums[m] = m,则右子组的首位元素一定在[m + 1, j]中,因此i = m + 1;
- nums[m] ≠ m,则左子组的末位元素一定在[i, m - 1]中,j = m - 1;
3. 返回:最后i指向右子组的首位元素,j指向左子组的末位元素,所以返回i即可
class Solution {
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i <= j){
int m = (i + j)/2;
if(nums[m] == m) i = m + 1;
else j = m - 1;
}
return i;
}
}
好像跟上一题没什么区别……