第一章 数组part01 数组理论基础、704. 二分查找、27. 移除元素

第一天| 第一章 数组part01 数组理论基础、704. 二分查找、27. 移除元素

数组理论基础

数组的特点:

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
  • 数组中的元素在内存中是依次紧密排列的,有序的。
  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改
  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  • 数组名中引用的是这块连续空间的首地址。

704. 二分查找

  • 题目链接:https://leetcode.cn/problems/binary-search/

  • 文章讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html

  • 题目介绍:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

    • 使用二分查找的前提是:(1)数组为有序数组;(2)数组中无重复元素

    • 二分查找根据边界范围的划分分为两种解法:

      • 第一种:左闭右闭;

        • 左闭右闭(left <= right)意味着:

          • (1)循环条件为while(left <= right)
          • (2)初次定义的right必须是数组的索引最大值,即int right = nums.length
          • (3)target不管是大于还是小于nums[middle],对应的区间边界的值都需要变化。具体地,如果target < nums[middle],那么right = middle - 1;如果target > nums[middle],那么left = middle + 1。
        • 在循环中写判断条件的时候,先判断nums[middle] == target是否成立,千万不要写成这样:

          •  while (left <= right) {
                 int middle = left + (right - left)/2;
                 if (target > nums[middle]) {
                     left = middle + 1;
                 } else if (target < nums[middle]) {
                     right = middle - 1;
                 } else {
                     return middle;
                 }
            }
            
          • 上面代码问题出在:如果循环条件为left == right时,上面两个判断条件都不符合,所以执行else,返回了middle。这样是错误的。

          • 因为,有可能target不在nums数组最小值和最大值的范围内,所以return middle需要指定条件,必须是target == nums[middle]。

        • 正确的做法是:

          • class Solution {
                public int search(int[] nums, int target) {
                    // 避免当 target 小于nums[0] 或 大于nums[nums.length - 1]时多次循环运算
                    // 这部分可有可无
                    if (target < nums[0] || target > nums[nums.length - 1]) {
                        return -1;
                    }
                    // 以下是关键代码
                    int left = 0, right = nums.length - 1;
                    while (left <= right) {
                        int mid = left + ((right - left) >> 1);
                        if (nums[mid] == target)
                            return mid;
                        else if (nums[mid] < target)
                            left = mid + 1;
                        else if (nums[mid] > target)
                            right = mid - 1;
                    }
                    return -1;
                }
            }
            
            • 这个代码还有一个需要注意的地方:
              • int mid = left + ((right - left) >> 1);
              • 没有采用(left + right) / 2的原因是:防止溢出
      • 第二种:左闭右开。

        • 左闭右开意味着:

          • (1)循环条件为while(left < right)
          • (1)初次定义的right等于nums.length
          • (2)如果target < nums[middle],那么right = middle - 1;如果target > nums[middle],那么left = middle。
        • 正确的做法是:

          • class Solution {
                public int search(int[] nums, int target) {
                    int left = 0, right = nums.length;
                    while (left < right) {
                        int mid = left + ((right - left) >> 1);
                        if (nums[mid] == target)
                            return mid;
                        else if (nums[mid] < target)
                            left = mid + 1;
                        else if (nums[mid] > target)
                            right = mid;
                    }
                    return -1;
                }
            }
            

27. 移除元素

  • 题目链接:https://leetcode.cn/problems/remove-element/

  • 文章讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html

  • 题目介绍:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

  • 注意:

    • 数组占用了内存地址中的连续空间,数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。我们不能对其中的元素进行删除,只能覆盖。
  • 提供两种解法:

    • 第一、暴力求解:

      • 提供两层循环,外层循环遍历数组,一旦数组中的值与目标值相同,执行内层循环,将该位置之后的元素赋给当前位置。

      • 由于数组的length一旦初始化之后就无法更改,所以想要修改它,必须把它赋给一个新值

      • 代码为:

        • class Solution {
              public int removeElement(int[] nums, int val) {
                  int length = nums.length;
                  for (int i = 0; i < length; i++) {
                      if (nums[i] == val) {
                          for(int j = i + 1; j < length; j++) {
                              nums[j - 1] = nums[j];
                          }
                          i--;
                          length--;
                      }
                  }
                  return length;
              }
          }
          
    • 第二:快慢指针:

      • 快指针去寻找新数组中需要的值;

      • 慢指针确定新数组的索引值。

      • 解释一下:就是快指针不会停下来,它一直扫描这个数组,如果快指针扫描到的元素不是目标值,那么就把值赋给慢指针对应的元素,并且跟着快指针一起++;一旦快指针扫描到的元素是目标值,慢指针就停下来(不++了),快指针继续++,找目标值。

        • 图解详见:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E6%80%9D%E8%B7%AF

        • class Solution {
              public int removeElement(int[] nums, int val) {
                  int slowindex = 0;
                  for (int fastindex = 0; fastindex < nums.length; fastindex++) {
                      if (nums[fastindex] != val) {
                          nums[slowindex] = nums[fastindex];
                          slowindex++;
                      }
                  }
                  return slowindex;
              }
          }
          

你可能感兴趣的:(leetcode,算法)