代码随想录_day01

168 阅读3分钟

数组

704_二分查找

题目链接:704. 二分查找 - 力扣(LeetCode)

给定一个 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

二分查找思路

如果使用二分查找,需要满足下面条件:

  1. 数组是有序的。
  2. 数组中无重复元素。

使用二分查找,需要注意区间问题,区间的定义一般有两种

  1. 定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
  2. 定义 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_移除元素

题目链接:27. 移除元素 - 力扣(LeetCode)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

移除元素思路

在数组中元素的内存地址是连续的,不能单独的删除数组中的某个元素,只能覆盖。

移除元素的两种解法:

  1. 暴力解法:通过两次for循环
    • 第一次for循环找到val的下标i
    • 第二次for循环将下标i后面的元素集体向前覆盖
  2. 双指针法:通过一个快指针和慢指针在一个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)