数组
704_二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9 输出: 4 解释: 9 出现在 nums 中并且下标为 4 示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2 输出: -1 解释: 2 不存在 nums 中因此返回 -1
二分查找思路
如果使用二分查找,需要满足下面条件:
- 数组是有序的。
- 数组中无重复元素。
使用二分查找,需要注意区间问题,区间的定义一般有两种
- 定义 target 是在一个在左闭右闭的区间里,也就是[left, right] 。
- 定义 target 是在一个在左闭右开的区间里,也就是[left, right) 。
左闭右闭代码实现
public static int search1(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;//定义target的区间为[left,right]
while (left <= right) {//在区间[left,right]中,因为left==right是有意义的,所以使用 <=
int middle = (left + right) / 2;
if (nums[middle] > target) {//此时target在middle的左边,需要更新右边界
right = middle - 1;//因为nums[middle] > target,target在左区间,更新区间[left,middle-1]
} else if (nums[middle] < target) {//此时target在middle的右边,需要更新左边界
left = middle + 1;//因为nums[middle]<target,target在右区间,更新区间[middle+1,right]
} else {//此时nums[middle] = target
return middle;
}
}
return -1;//此时left > right,查找结束并且没有找到target,返回-1
}
左闭右开代码实现
public static int search2(int[] nums, int target) {
int left = 0;
int right = nums.length;//定义target的区间为[left,right)
while (left < right) {//在区间[left,right)中 left = right,是无意义的,所以使用<
int middle = (left + right) / 2;
if (nums[middle] > target) {//此时target在middle的左边,需要更新右边界
right = middle;//因为nums[middle] > target,target在左区间,更新区间[left,middle)
} else if (nums[middle] < target) {//此时target在middle的有边,需要更新左边界
left = middle + 1;//因为nums[middle] < target,target在左区间,更新区间[middle+1,right)
}else {//此时nums[middle] = target
return middle;
}
}
return -1;//此时left > right,查找结束并且没有找到target,返回-1
}
27_移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
移除元素思路
在数组中元素的内存地址是连续的,不能单独的删除数组中的某个元素,只能覆盖。
移除元素的两种解法:
- 暴力解法:通过两次for循环
- 第一次for循环找到val的下标i
- 第二次for循环将下标i后面的元素集体向前覆盖
- 双指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
- 快指针:寻找新数组的元素,不是val的元素
- 慢指针:更新新数组下标的位置
暴力解法代码实现
public int removeElement1(int[] nums, int val) {
int length = nums.length;//数组的初始大小
for (int i = 0; i < length; i++) {//遍历数组,找到val的位置
if (nums[i] == val) {//此时val的下标为i
for (int j = i + 1; j < length; j++) {//将数组的元素集体向前移动
nums[j - 1] = nums[j];
}
i--;//因为下标i后的元素集体向前移动1位,所以i也需要向前移动一位
length--;//移除元素后,数组大小-1
}
}
return length;
}
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
双指针法代码实现
public int removeElement2(int[] nums, int val) {
int length = nums.length;//数组的初始大小
int slow = 0;//慢指针:新数组的下标
for (int fast = 0; fast < length; fast++) {//遍历老数组,寻找新数组元素的下标
if (nums[fast] != val) {//新数组元素:不是val的元素为新数组的元素
nums[slow++] = nums[fast];//将找到的新数组元素排列
}
}
return slow;//此时的slow就是新数组下标+1,也就是新数组的长度
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)