704.二分查找和27. 移除元素
704.二分查找
题目链接为:
要求:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
解答:
二分查找是针对于排好序的数组,如果数组没有排好序,则不能用二分查找算法,二分查找的时间复杂度为 O(n)。
二分查找的难点在于边界条件,边界如何确定关键在于你是选择的什么区间,你是选择的左闭右开,还是左闭右闭呢?选择不同的区间,写法会稍微有点不同。
当选择左闭右闭的时候,首先要确定的是 left = 0, right = nums.length - 1
,因为两边都是有效值,所以在 while 条件中,left ≤ right
,当判断如果 nums[middle] > target
, right = middle - 1
还是等于 middle
呢?还是看右边界,因为我们现在是右闭的,所以 middle 已经进行了判断,这个时候就是 right = middle - 1
。
当选择左闭右闭的时候,因为右侧是无效值,也就是永远取不到,所以 left = 0, right = nums.length
,并且在 while 条件中,left ≠ right
,所以是left < right
,当判断如果 nums[middle] > target
, right = middle - 1
还是等于 middle
呢?还是看右边界,因为我们现在是右开的,所以就是 right = middle
。
/**
* 题目要求使用二分查找,二分查找最关键的就是边界问题,先使用左闭右闭的写法
* 我这边的想法是 left 为 0, right 为最大索引,然后取中间值,因为题目中已经排好序,所以这边不用重复排序
* 根据中间值判断就有三中情况:
* 1. 相等,则返回索引
* 2. 大于,说明目标值在左半部分,所以 right = middle - 1,为什么不是 middle,因为 middle 已经判断过了,下面是相同的道理
* 3. 小于,说明目标值在右半部分,所以 left = middle + 1
* @param nums 有序数组
* @param target 目标值
* @return 目标值的索引
*/
public static int search(int[] nums, int target) {
// 不同点 1
int left = 0, right = nums.length - 1;
// 不同点 2
while (left <= right) {
int middle = (left + right) / 2;
if (nums[middle] == target) {
return middle;
}
if (nums[middle] < target) {
left = middle + 1;
} else {
// 不同点 3
right = middle - 1;
}
}
return -1;
}
/**
* 左闭右开 的写法
*/
public static int search2(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int middle = (left + right) / 2;
if (nums[middle] == target) {
return middle;
}
if (nums[middle] < target) {
left = middle + 1;
} else {
right = middle;
}
}
return -1;
}
27. 移除元素
题目链接为:
要求:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
解答:
这道题目就是原地删除,如果不考虑题目要求,我们可以使用另外一个集合,然后在遍历原数组的时候把不相等的放入另外一个集合,现在有了要求,不是用额外的空间,并且不需要考虑数组中超出新长度后面的元素,那我们可以用双指针的写法,思路就是两个指针,碰到不等于 target 的,就把左指针的元素替换为右指针,如果等于 target,则将右指针元素 + 1,下面右两种不同的写法,后一种更为简单。
public static int removeElement(int[] nums, int val) {
int left = -1, right = 0;
while (right < nums.length) {
if (nums[right] == val && left == -1) {
left = right;
}
if (nums[right] != val && left != -1) {
nums[left ++] = nums[right];
}
right ++;
}
return left == -1 ? nums.length : left;
}
/**
* 更简单的写法
* @param nums 原数组
* @param val 目标元素
* @return 移除后数组的新长度
*/
public static int removeElement2(int[] nums, int val) {
int left = 0, right = 0;
for (int num : nums) {
if (num == val) {
right ++;
} else {
nums[left ++] = nums[right ++];
}
}
return left;
}