代码随想录算法训练营第一天 | 704. 二分查找,27. 移除元素

代码随想录算法训练营第一天 | 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的值即新数组的长度
    }
}

小结:双指针法在数组和链表操作中较常见,许多相关面试题均可用此方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值