day1 数组&二分查找
数组理论
参考
https://programmercarl.com/%E6%95%B0%E7%BB%84%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html
注意数组的内存空间地址(是否连续?)
二分查找
参考代码随想录文章 https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
二分查找的核心是由区间的定义决定边界条件(while循环结束条件)和边界点的变化!!!
- 左闭右闭区间用 while (left <= right) 要使用 <= ,因为定义left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
- 相反的,左闭右开区间用 while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
704.二分查找
https://leetcode.cn/problems/binary-search/
class Solution {
public int search(int[] nums, int target) {
if (target < nums[0] || target > nums[nums.length - 1])
return -1;
int l = 0, r = nums.length - 1;
while (l <= r) {
int m = l + (r - l >> 1);
if (nums[m] > target) r = m - 1;
else if (nums[m] < target) l = m + 1;
else return m;
}
return -1;
}
}
35.搜索插入位置
https://leetcode.cn/problems/search-insert-position/description/
解法1:找>=目标值的最左位置
class Solution {
public int searchInsert(int[] nums, int target) {
// 找不到的话ans插入位置是右边界
int l = 0, r = nums.length - 1, ans = nums.length;
while (l <= r) {
int m = l + (r - l >> 1);
if (nums[m] >= target) {
ans = m;
r = m - 1;
} else l = m + 1;
}
return ans;
}
}
解法2:普通二分,处理所有四种情况
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
// 定义target在左闭右闭的区间,[low, high]
int low = 0;
int high = n - 1;
while (low <= high) { // 当low==high,区间[low, high]依然有效
int mid = low + (high - low) / 2; // 防止溢出
if (nums[mid] > target) {
high = mid - 1; // target 在左区间,所以[low, mid - 1]
} else if (nums[mid] < target) {
low = mid + 1; // target 在右区间,所以[mid + 1, high]
} else {
// 1. 目标值等于数组中某一个元素 return mid;
return mid;
}
}
// 2.目标值在数组所有元素之前 3.目标值插入数组中 4.目标值在数组所有元素之后 return right + 1;
return high + 1;
}
}
34.在排序数组中查找元素的第一个和最后一个位置
https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
class Solution {
public int[] searchRange(int[] nums, int target) {
int n = nums.length;
int l = 0, r = n - 1, ans = 0;
int[] res = new int[]{-1, -1};
// 防止数组越界
if (n == 0) return res;
while (l <= r) {
int m = l + (r - l >> 1);
if (nums[m] >= target) {
ans = m;
r = m - 1;
} else l = m + 1;
}
if (nums[ans] != target) return res;
else res[0] = ans;
l = 0;
r = n - 1;
ans = 0;
while (l <= r) {
int m = l + (r - l >> 1);
if (nums[m] <= target) {
ans = m;
l = m + 1;
} else r = m - 1;
}
res[1] = ans;
return res;
}
}
数组中移除元素
27.移除元素
https://leetcode.cn/problems/remove-element/
解法1:暴力两层循环
class Solution {
public int removeElement(int[] nums, int val) {
int size = nums.length;
for (int i = 0; i < size; i++) {
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--;
size--;
}
}
return size;
}
}
解法2:双指针
快慢指针:
- 快指针:寻找新数组中的元素,即不含有目标数的元素
- 慢指针:新数组下标(想象指向一个虚拟的新数组)
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val)
nums[slow++] = nums[fast];
}
return slow;
}
}
26.删除有序数组中的重复项
快慢指针
-
比较 slow 和 fast 位置的元素是否相等。
-
如果相等,fast 后移 1 位
-
如果不相等,将 fast 位置的元素复制到 slow + 1 位置上,slow 后移 1 位,fast 后移 1 位
重复上述过程,直到 fast 等于数组长度。 -
返回 slow + 1,即为新数组长度。与上一题27题不一样的原因是,27题是 slow 更新后++,所以 slow 就是数组长度,26题是 slow 先后移 1 位再更新,所以返回值要 + 1(slow++与++slow的区别)
class Solution {
public int removeDuplicates(int[] nums) {
int slow = 0;
for (int fast = 1; fast < nums.length; ) {
if (nums[slow] == nums[fast])
fast++;
else if (nums[slow] != nums[fast])
nums[++slow] = nums[fast++];
}
return slow + 1;
}
}