代码随想录算法训练营第一天 | 704. 二分查找、27. 移除元素
数组理论基础
讲解:数组理论基础
小结:数组是存放在连续内存空间上的相同类型数据的集合,下标从0开始。 相关算法题主要考察对编程语言的掌控能力。
704. 二分查找
题目链接:704. 二分查找
注:题目所给条件为 1.有序数组;2.无重复元素。 这两点是使用二分法的前提。
二分法写法(一)
思路:本题关键点在于区间的定义,即循环不变量。 while循环中每一次边界的处理都要根据已定义的区间进行考虑。写法(一)将target定义在一个左闭右闭区间,即[left, right]。
- 在左闭右闭区间内,left = right 是有意义的,因此while循环中要用 <= ;
- 若 nums[middle] 大于 target,则更新右下标 right 为 middle - 1。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 指定“不变量”为左闭右闭区间,即[left, right]
while(left <= right){
int middle = left + ((right - left) / 2); // 防止溢出
if(nums[middle] < target){
left = middle + 1;
} else if (nums[middle] > target){
right = middle - 1;
} else{
return middle;
}
}
return -1;
}
}
二分法写法(二)
思路:写法(二)将target定义在一个左闭右开区间,即[left, right)。
- 在左闭右开区间内,left = right 是没有意义的,因此while循环中要用 <=;
- 若 nums[middle] 大于 target,则更新右下标 right 为 middle即可,因为是左闭右开区间,所以下一个查询区间不会比较 nums[middle]。
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length; // 与写法(一)不同,此处代表[left, right)
while(left < right){
int middle = left + ((right - left) >> 1); // 防止溢出(右移)
if(nums[middle] < target){
left = middle + 1;
} else if (nums[middle] > target){
right = middle;
} else{
return middle;
}
}
return -1;
}
}
27. 移除元素
题目链接:27. 移除元素
注:题目要求空间复杂度为O(1)。
暴力解法
思路:两个for循环。 第一个遍历数组元素,第二个更新数组。该方法的时间复杂度为O(n2),空间复杂度为O(1)。
class Solution {
public int removeElement(int[] nums, int val) {
int length = nums.length; //记录原数组长度
for(int i = 0; i < length; i++) { // 第一个for循环,寻找等于val的数组元素
if (nums[i] == val){
//第二个for循环,将后续数组集体前移,注意j = i + 1
//若j初始为i,则 nums[j - 1] = nums[j]要改为 nums[j] = nums[j + 1],
//这会导致数组越界
for (int j = i + 1; j < nums.length; j++){
nums[j - 1] = nums[j];
}
i--; //由于下标i以后的数组元素均前移一位,因此i也前移一位,以便下次循环操作
length--; //更新最后返回的数组长度
}
}
return length;
}
}
双指针法(快慢指针法)
思路:定义快指针fast和慢指针slow。快指针指向新数组需要的元素,慢指针代表新数组的下标值。 该方法的时间复杂度为O(n),因为在一个for循环内完成了暴力解法中两个for循环的工作;空间复杂度为O(1)。
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]; //将新数组需要的元素赋值给慢指针指向的位置
slow++;
}
//若快指针遇到了待删除元素,则跳过以上if语句,即停下slow指针,
//而快指针继续前进,旨在之后的循环中逐一覆盖先前跳过的元素
}
return slow; //slow的值即新数组的长度
}
}
小结:双指针法在数组和链表操作中较常见,许多相关面试题均可用此方法。