Day01~704. 二分查找、27. 移除元素

355 阅读5分钟

摘要

本文首先介绍了数组的基础知识,然后通过分析LeetCode上的两道问题,LC-704. 二分查找和LC-27. 移除元素,演示了数组在算法中的灵活应用。

1、数组理论基础

1.1 概念

在Java中,数组是一种基本的数据结构,用于存储相同数据类型的元素集合。以下是关于Java中数组的一些重要信息:

  1. 声明和初始化数组: 你可以通过以下方式声明和初始化数组:

    // 声明一个整数数组
    int[] intArray;
    ​
    // 初始化数组并分配内存
    intArray = new int[5];
    ​
    // 或者一步完成声明和初始化
    int[] intArray = new int[5];
    

    这将创建一个包含5个整数的数组。

  2. 访问数组元素: 你可以使用索引来访问数组中的元素,索引从0开始:

    intArray[0] = 10;  // 设置数组的第一个元素为10
    int element = intArray[1];  // 获取数组的第二个元素
    
  3. 数组长度: 使用 length 属性可以获取数组的长度,即数组中元素的个数:

    int length = intArray.length;
    
  4. 多维数组: Java支持多维数组,例如二维数组,可以看作是数组的数组:

    int[][] twoDArray = new int[3][4];
    

    这将创建一个3行4列的二维数组。

  5. 数组的遍历: 你可以使用循环来遍历数组中的元素,如 forforeach 循环。

  6. 数组的初始化: 你可以在声明数组时初始化它们:

    int[] intArray = {1, 2, 3, 4, 5};
    

    这将创建一个包含1到5的整数的数组。

  7. 动态数组: 如果需要一个可变长度的数组,可以使用Java集合框架中的 ArrayListLinkedList 等。

  8. 数组的限制: Java中的数组有固定的大小,一旦创建,其大小不能更改。这就是为什么有时候使用动态数组更灵活。

  9. 数组的注意事项: 在使用数组时要注意边界检查,以防止数组越界异常。

总之,Java中的数组是一种常见且有用的数据结构,用于存储和处理数据集合。它们具有固定的大小,因此在使用时需要小心管理数组的大小和索引以避免错误。如果需要更灵活的数据结构,可以考虑使用Java的集合类。

1.2 Arrays类常用API

java.util.Arrays 类提供了一系列用于操作数组的静态方法。以下是一些常用的 Arrays 类中的数组操作方法:

  1. sort(T[] a) 对数组进行升序排序。可以用于各种数据类型的数组,例如整数、字符串等。

    int[] intArray = {5, 2, 8, 1, 3};
    Arrays.sort(intArray); // 对 intArray 进行升序排序
    
  2. binarySearch(T[] a, T key) 在排序后的数组中使用二分查找来查找指定元素。如果找到元素,返回其索引;否则返回负数。

    int[] sortedArray = {1, 2, 3, 4, 5};
    int index = Arrays.binarySearch(sortedArray, 3); // 在 sortedArray 中查找 3 的索引,返回 2
    
  3. copyOf(T[] original, int newLength) 复制数组的前 newLength 个元素,生成一个新的数组。

    int[] originalArray = {1, 2, 3, 4, 5};
    int[] copyArray = Arrays.copyOf(originalArray, 3); // 复制 originalArray 的前 3 个元素
    
  4. toString(T[] a) 将数组转换为字符串表示。

    int[] intArray = {1, 2, 3, 4, 5};
    String arrayString = Arrays.toString(intArray); // 转换为字符串表示,例如 "[1, 2, 3, 4, 5]"
    
  5. fill(T[] a, T val) 将数组的所有元素设置为指定的值。

    int[] intArray = new int[5];
    Arrays.fill(intArray, 0); // 将 intArray 的所有元素设置为 0
    
  6. equals(T[] a, T[] b) 比较两个数组是否相等(包含相同的元素并具有相同的顺序)。

    int[] array1 = {1, 2, 3};
    int[] array2 = {1, 2, 3};
    boolean areEqual = Arrays.equals(array1, array2); // 检查两个数组是否相等,返回 true
    

这些方法只是 java.util.Arrays 类中的一部分。根据你的需求,还可以使用其他方法来操作数组。这些方法可以帮助简化数组的处理和操作,提高编码效率。

1.3 数组和List互转

在Java中,可以通过不同的方法进行数组和List之间的转换。

数组转List:

  1. 使用Arrays.asList(T... a)方法,它接受一个变长参数,可以将数组转换为List。

    String[] array = {"apple", "banana", "cherry"};
    List<String> list = Arrays.asList(array);
    
  2. 使用Java 8+的Streams,将数组转换为Stream,然后使用Collectors.toList()将其收集为List。

    String[] array = {"apple", "banana", "cherry"};
    List<String> list = Arrays.stream(array).collect(Collectors.toList());
    

List转数组:

  1. 使用toArray(T[] a)方法,可以将List转换为数组。

    List<String> list = new ArrayList<>();
    list.add("apple");
    list.add("banana");
    list.add("cherry");
    ​
    String[] array = list.toArray(new String[0]);
    
  2. 使用Java 8+的Streams,将List转换为Stream,然后使用toArray()将其转换为数组。

    List<String> list = new ArrayList<>();
    list.add("apple");
    list.add("banana");
    list.add("cherry");
    ​
    String[] array = list.stream().toArray(String[]::new);
    

需要注意的是,使用toArray(T[] a)方法时,如果传入的数组长度小于List的大小,会创建一个新的数组并返回。如果传入的数组长度大于等于List的大小,将会使用传入的数组进行存储。上述代码中的new String[0]String[]::new是创建一个新数组的示例。

另外,需要注意的是,Arrays.asList()返回的List是一个固定大小的List,不支持添加或删除操作。如果需要对List进行修改操作,可以将其转换为new ArrayList<>(Arrays.asList(array)),这样就可以使用addremove等方法了。

2、LC-704. 二分查找

2.1 左闭右闭

思路

双指针,left、right 分别指向头尾节点并逐步向中间移动,直到找到目标值或退出循环为止

  • 左闭右闭,保持循环不变量,
  • 防止溢出,才是常识的体现。
  • target大于mid,left = mid + 1,
  • target小于mid,right = mid - 1。

代码

    // 双指针,leftright 分别指向头尾节点并逐步向中间移动,直到找到目标值或退出循环为止
    public static int search(int[] nums, int target) {
        int left =0, right = nums.length-1;
​
        while(left <= right) {
            int mid = (right - left) / 2 + left;
            if(target > nums[mid]) {
                left = mid + 1;
            } else if(target < nums[mid]) {
                right = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }

2.2 左闭右开

思路

  • 当二分查找要开启, 右边界需规划好(right = nums.length)
  • 左小于右条件成立(left < right)
  • 目标小于中值,右宽松(right = mid)

代码

    // 左闭右开解法
    public static int search2(int[] nums, int target) {
        int left =0, right = nums.length;
​
        while(left < right) {
            int mid = (right - left) / 2 + left;
            if(target > nums[mid]) {
                left = mid + 1;
            } else if(target < nums[mid]) {
                right = mid;
            } else {
                return mid;
            }
        }
        return -1;
    }

3、LC-27. 移除元素

3.1 思路

双指针,newIdx 代表新数组的元素的下标,oldIdx 代表旧数组元素的下标

  • 双指针擅舞动,newIdx和oldIdx,来开路
  • 删除元素,oldIdx前进
  • 正常元素,新数组填充,newIdx、oldIdx齐步走

3.2 代码

    // 双指针,newIdx 代表新数组的元素的下标,oldIdx 代表旧数组元素的下标
    public static int removeElement(int[] nums, int val) {
        int newIdx = 0;
        for (int oldIdx = 0; oldIdx < nums.length; oldIdx++) {
            if (nums[oldIdx] == val) {
                continue;
            }
            nums[newIdx] = nums[oldIdx];
            newIdx++;
        }
        return newIdx;
    }

参考资料

代码随想录-数组理论基础

代码随想录-二分查找

代码随想录-移除元素