代码随想录算法训练营二刷第一天| 704. 二分查找,27. 移除元素
文章目录
- 代码随想录算法训练营二刷第一天| 704. 二分查找,27. 移除元素
- 一、704. 二分查找
- 二、35.搜索插入位置
- 三、34. 在排序数组中查找元素的第一个和最后一个位置
- 四、69.x 的平方根
- 五、367. 有效的完全平方数
- 六、27. 移除元素
- 七、26. 删除有序数组中的重复项
- 八、283. 移动零
- 九、844. 比较含退格的字符串
- 十、977.有序数组的平方
一、704. 二分查找
题目链接:
思路:维护好循环不变量。另外一个点注意别让加和超出int类型的限制,可以变为加差。
解法一:左闭右闭
public int search(int[] nums, int target) {int left = 0, right = nums.length - 1, mid = 0;while (left <= right) {mid = left + ((right - left) >> 1);if (target > nums[mid]) {left = mid + 1;} else if (target < nums[mid]) {right = mid - 1;}else {return mid;}}return -1;}
解法二:左闭右开
public int search(int[] nums, int target) {int left = 0, right = nums.length, mid = 0;while (left < right) {mid = left + ((right - left) >> 1);if (target > nums[mid]) {left = mid + 1;} else if (target < nums[mid]) {right = mid;}else {return mid;}}return -1;}
二、35.搜索插入位置
题目链接:
思路:经典二分查找。
public int searchInsert(int[] nums, int target) {int left = 0, right = nums.length - 1, mid = 0;while (left <= right) {mid = left + ((right - left) >> 1);if (target > nums[mid]) {left = mid + 1;} else if (target < nums[mid]) {right = mid - 1;} else {return mid;}}return left;}
三、34. 在排序数组中查找元素的第一个和最后一个位置
题目链接
思路:分别用二分查找查左边界和右边界,查找区间的左边界,要在mid小于等于target时一直记录从左边逼近边界,而右边不做记录,同理求右边界也是如此。
class Solution {public int[] searchRange(int[] nums, int target) {if (nums.length == 0) {return new int[]{-1, -1};}int left = getLeftBorder(nums, target);int right = getRightBorder(nums, target);if (left == -2 || right == -2) {return new int[]{-1, -1};}if (right - left > 1) {return new int[]{left + 1, right - 1};}return new int[]{-1, -1};}int getLeftBorder(int[] nums, int target) {int left = 0, right = nums.length - 1, mid = 0, leftBorder = -2;while (left <= right) {mid = left + ((right - left) >> 1);if (target <= nums[mid]) {right = mid - 1;leftBorder = right;}else {left = mid + 1;}}return leftBorder;}int getRightBorder(int[] nums, int target) {int left = 0, right = nums.length - 1, mid = 0, rightBorder = -2;while (left <= right) {mid = left + ((right - left) >> 1);if (target >= nums[mid]) {left = mid + 1;rightBorder = left;}else {right = mid - 1;}}return rightBorder;}
}
四、69.x 的平方根
题目链接
思路:求平方根有小数的向下取整,这种情况下只要中值计算小于等于mid就一直搜集结果,和上面求边界的思路类似,当然注意防止溢出,要用long
class Solution {public int mySqrt(int x) {int left = 0, right = x, result = 0;while (left <= right) {int mid = left + ((right - left) >> 1);if ((long) mid * mid <= x) {result = mid;left = mid + 1;} else {right = mid - 1;}}return result;}
}
五、367. 有效的完全平方数
题目链接
思路:二分查找搜索即可,但是同样注意乘积使用long。
class Solution {public boolean isPerfectSquare(int num) {int left = 0, right = num, mid = 0;while (left <= right) {mid = left + ((right - left) >> 1);long temp = (long) mid * mid;if (temp == num) {return true;} else if (num < temp) {right = mid - 1;}else {left = mid + 1;}}return false;}
}
六、27. 移除元素
题目链接
思路:用一个变量k计数,记录相等元素的个数,不等则向前移动k个位置,最后返回数组长度减去k,因为k是要删除的元素。
class Solution {public int removeElement(int[] nums, int val) {int k = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] == val) {k++;}else {nums[i-k] = nums[i];}}return nums.length - k;}
}
双指针解法:一个快指针,一个慢指针,快指针向前遍历,快指针指向的元素不等于target,就将元素赋值给慢指针,慢指针再前进一步,这个思路和上面有一个变量记录要移动的距离类似,有异曲同工之妙。
class Solution {public int removeElement(int[] nums, int val) {int slow = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] != val) {nums[slow++] = nums[i];}}return slow;}
}
双向指针法:一个关键点维护好循环不变量,然后从左寻找等于target的值(即要被覆盖的值),从右寻找不等于target的值(即要保留的值),找到之后即覆盖一次。
class Solution {public int removeElement(int[] nums, int val) {int left = 0, right = nums.length - 1;while (left <= right) {while (left <= right && nums[left] != val) {left++;}while (left <= right && nums[right] == val) {right--;}if (left <= right) {nums[left++] = nums[right--];}}return left;}
}
七、26. 删除有序数组中的重复项
题目链接
思路:因为数组是有序的,所以相同的元素都是紧挨着的,只需要比较相邻元素即可,只要当前元素与上一个元素相等就给元素k加1,当不相等时,就要往前移动k个位置,数组的长度即为删除掉K个元素之后的长度。
class Solution {public int removeDuplicates(int[] nums) {if (nums.length == 1) return 1;int k = 0;for (int i = 1; i < nums.length; i++) {if (nums[i] == nums[i-1]) {k++;}else {nums[i-k] = nums[i];}}return nums.length - k;}
}
八、283. 移动零
题目链接
思路:这个移动零和前面删除元素类似,快慢指针然后交换元素即可。
class Solution {public void moveZeroes(int[] nums) {int slow = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] != 0) {int temp = nums[slow];nums[slow++] = nums[i];nums[i] = temp;}}}
}
九、844. 比较含退格的字符串
题目链接
思路:从前往后要拼接字符串,从后往前就简单了,有#就记录,没有就跳过#数量,数量没了就比较是否相等,不等就返回false
class Solution {public boolean backspaceCompare(String s, String t) {int i = s.length() - 1, j = t.length() - 1;int skipS = 0, skipT = 0;while (i >= 0 || j >= 0) {while (i >= 0) {if (s.charAt(i) == '#') {skipS++;i--;} else if (skipS > 0) {skipS--;i--;}else {break;}}while (j >= 0) {if (t.charAt(j) == '#') {skipT++;j--;} else if (skipT > 0) {skipT--;j--;}else {break;}}if (i >= 0 && j >= 0) {if (s.charAt(i) != t.charAt(j)) {return false;}} else {if (i >= 0 || j >= 0) {return false;}}i--;j--;}return true;}
}
十、977.有序数组的平方
题目链接
思路:找到正数负数分界线,然后归并算法
class Solution {public int[] sortedSquares(int[] nums) {int n = nums.length;int negative = -1;for (int i = 0; i < n; ++i) {if (nums[i] < 0) {negative = i;} else {break;}}int[] ans = new int[n];int index = 0, i = negative, j = negative + 1;while (i >= 0 || j < n) {if (i < 0) {ans[index] = nums[j] * nums[j];++j;} else if (j == n) {ans[index] = nums[i] * nums[i];--i;} else if (nums[i] * nums[i] < nums[j] * nums[j]) {ans[index] = nums[i] * nums[i];--i;} else {ans[index] = nums[j] * nums[j];++j;}++index;}return ans;}}