二分查找

==总结:二分查找的结果:如果能查到则返回值一定是mid,如果查找不到,最后的状态是(eg: 1,2,3,right,left,4,5,6),最后一定是right指针在left指针左边且相邻,此时left指针的位置就是找不到target是需要插入的位置,mid指针应该等于left指针==

注意点

mid=left+(right-left)/2 放置left+right溢出

while(left<right) OR while(left<=right)

边界重新赋值时应为mid或者mid+1

按照自己所定义的区间来判断 [left, right] [left, rigth)

两种写法

[left, right]

此时当left=right时也是有意义的,因此while条件为left<=right

当nums[mid] != target时,需要重新确定区间

显然mid下标的数已经参与过比较,因此确定新的区间时不需要包含mid下标,left=mid+1,right=mid-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (target < nums[mid]) {
right = mid - 1;
} else if (target > nums[mid]) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}

[left, right)

注意,由于右边界是开区间,因此 right = nums.length 而不需要减 1

因为right为开区间,所以left不可能等于right ,因此while的条件为while(left < right)

当nums[mid] != target时,需要重新确定区间

确定新的区间时不需要包含mid下标,而由于区间是右开,因此右边界可以是mid

故确定新区间时 [left, mid) [mid+1, right)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (target < nums[mid]) {
right = mid;
} else if (target > nums[mid]) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}

35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(target < nums[mid]){
right = mid - 1;
}else if (target > nums[mid]){
left = mid + 1;
}else {
return mid;
}
}
//当目标值不存在于数组中时,目标值的大小一定位于相邻两个数之间
//某一时刻目标值位于left和right中间且left和right相邻
//此时mid=(left+right)/2 = left
//则target > nums[mid] 触发 left=mid+1,此时left=mid+1=right
//left=right 继续循环,mid=left=right,target在mid左边,触发right = mid -1,结束循环
//此时插入的目标位置为left或者right+1,返回即可
return left; // OR return right + 1;
}

34.在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]

思路:一次二分法搜索出左边界,再用一次二分法搜索出右边界(或者得到左边界后顺序遍历)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public int[] searchRange(int[] nums, int target) {
int resLeft = -1, resRight = -1;
int lLeft = 0, rLeft = 0;
int lRight = nums.length - 1, rRight = nums.length - 1;
//确定左边界
while (lLeft <= lRight) {
int mid = lLeft + (lRight - lLeft) / 2;
if (target <= nums[mid]) {
lRight = mid - 1;
if (target == nums[mid]) {
resLeft = mid;
}
} else {
lLeft = mid + 1;
}
}
//确定右边界
while (rLeft <= rRight) {
int mid = rLeft + (rRight - rLeft) / 2;
if (target < nums[mid]) {
rRight = mid - 1;
} else {
rLeft = mid + 1;
if (target == nums[mid]) {
resRight = mid;
}
}
}

return new int[]{resLeft, resRight};
}

确定左边界后遍历计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public int[] searchRange(int[] nums, int target) {
int resLeft = -1, resRight = -1;
int lLeft = 0, rLeft = 0;
int lRight = nums.length - 1, rRight = nums.length - 1;
while (lLeft <= lRight) {
int mid = lLeft + (lRight - lLeft) / 2;
if (target <= nums[mid]) {
lRight = mid - 1;
if (target == nums[mid]) {
resLeft = lRight + 1;
}
} else {
lLeft = mid + 1;
}
}
int res = 0;
int i = resLeft;
while (i >= 0 && i< nums.length && nums[i] == target) {
res++;
i++;
}
if(res!=0){
res-=1; //从resLeft开始有res个是为[resLeft , resLeft + res - 1]
}
return new int[]{resLeft, resLeft + res};
}

69.x的平方根

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5

示例 1:

1
2
输入:x = 4
输出:2

示例 2:

1
2
3
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public int mySqrt(int x) {
if (x == 0) {
return 0;
}
if (x <= 3) {
return 1;
}
int left = 2;
int right = x / 2;
while (left <= right) {
int mid = left + (right - left) / 2;
if ((long) mid * mid < x) {
left = mid + 1;
} else if ((long) mid * mid > x) {
right = mid - 1;
} else {
return mid;
}
}
return right;
}

367.有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false

进阶:不要 使用任何内置的库函数,如 sqrt

示例 1:

1
2
输入:num = 16
输出:true

示例 2:

1
2
输入:num = 14
输出:false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean isPerfectSquare(int num) {
if (num == 1) {
return true;
}
int left = 2;
int right = num / 2;
while (left <= right) {
int mid = left + (right - left) / 2;
if ((long) mid * mid > num) {
right = mid - 1;
} else if ((long) mid * mid < num) {
left = mid + 1;
} else {
return true;
}
}
return false;
}

移除元素

==总结:使用快慢双指针==

27. 移除元素

快慢双指针:初始时快慢指针都指向第一个元素,每循环一次,如果快指针不等于val,则赋值给慢指针,如果快指针等于val,则慢指针不需要动,快指针加1

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int removeElement(int[] nums, int val) {
if (nums == null || nums.length == 0) {
return 0;
}
int fastIndex = 0;
int slowIndex = 0;
while(fastIndex < nums.length){
if(nums[fastIndex] != val){
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
fastIndex++;
}
return slowIndex;
}

26.删除排序数组中的重复项

给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4] 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

1
2
3
4
5
6
7
8
9
10
11
12
public int removeDuplicates(int[] nums) {
int fastIndex=1;
int slowIndex=0;
while(fastIndex < nums.length){
if (nums[fastIndex] != nums[slowIndex]) {
slowIndex++;
nums[slowIndex] = nums[fastIndex];
}
fastIndex++;
}
return slowIndex+1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public int removeDuplicates(int[] nums) { 
int n = nums.length;
if (n == 0) {
return 0;
}
int fast = 1, slow = 1;
while (fast < n) {
if (nums[fast] != nums[fast - 1]) {
nums[slow] = nums[fast]; ++slow;
} ++fast;
}
return slow;
}

283.移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

1
2
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

1
2
输入: nums = [0]
输出: [0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void moveZeroes(int[] nums) {
int fastIndex = 0;
int slowIndex = 0;
while (fastIndex < nums.length) {
if (nums[fastIndex] != 0) {
swap(nums, fastIndex, slowIndex);
slowIndex++;
}
fastIndex++;
}
}

public void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}

滑动窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
// 模板
for () {
// 将新进来的右边的数据,计算进来
// 更新数据

// 判断窗口数据是否不满足要求了
while (窗口数据不满要求 && left < arrSize) {
// 移除left数据,更新窗口数据
left++;
}
//更新结果
right++;
}

209.长度最小的子数组