Java基础快速入门: 数组高级操作与算法基础

本文纲要

  1. 二分查找
    思路分析
    代码实现
  2. 冒泡排序
    思路分析
    分步实现
    完整实现
  3. 递归
    概念与示例
    求阶乘案例
    递归内存图
  4. 快速排序
    思路分析
    核心代码实现
    完整递归实现
  5. Arrays工具类
    toString
    sort
    binarySearch

二分查找

1 ) 思路分析

二分查找的前提条件:数组元素必须按照大小顺序排列

核心思想:每次去掉一半的查找范围,在剩余的范围中继续查找,直到找到目标元素或范围无效。

  • 定义两个变量 minmax,表示当前查找范围(最小索引和最大索引)。
  • 根据 minmax 计算出中间位置 midmid = (min + max) / 2
  • 判断 arr[mid] 与目标值的关系:
    • 相等:直接返回 mid
    • 目标值较小:说明目标在左半边,更新 max = mid - 1
    • 目标值较大:说明目标在右半边,更新 min = mid + 1
  • 循环条件:min <= max
  • 如果 min > max,说明元素不存在,返回 -1

2 ) 代码实现

项目结构:

src/
└── com/
    └── wb/
        └── arrsearch/
            └── MyBinarySearchDemo.java
package com.wb.arrsearch;
 
public class MyBinarySearchDemo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int number = 11;
 
        // 1. 我现在要干嘛? --- 二分查找
        // 2. 我干这件事情需要什么? --- 数组 元素
        // 3. 我干完了,要不要把结果返回调用者 --- 把索引返回给调用者
        int index = binarySearchForIndex(arr, number);
        System.out.println(index);
    }
 
    private static int binarySearchForIndex(int[] arr, int number) {
        // 1. 定义查找的范围 
        int min = 0;
        int max = arr.length - 1;
        // 2. 循环查找 min <= max
        while (min <= max) {
            // 3. 计算出中间位置 mid
            int mid = (min + max) >> 1;   // 右移1位相当于除以2 
            // mid指向的元素 > number
            if (arr[mid] > number) {
                // 表示要查找的元素在左边 
                max = mid - 1;
            } else if (arr[mid] < number) {
                // mid指向的元素 < number
                // 表示要查找的元素在右边
                min = mid + 1;
            } else {
                // mid指向的元素 == number
                return mid;
            }
        }
        // 如果min大于了max就表示元素不存在,返回-1
        return -1;
    }
}

运行结果:查找 3 返回索引 2;查找 11 返回 -1

冒泡排序

1 ) 思路分析

冒泡排序的核心:相邻元素两两比较,小的放前面,大的放后面,每一轮确定一个最大值放到最右边。

  • 如果有 n 个数据,需要比较 n-1 轮。
  • 每一轮结束后,下一轮比较时就会少一个元素参与(因为最右边的最大值已经确定)。

2 )分步实现

先按轮次拆开,逐步理解每一轮的操作。

package com.wb.mybubble;
 
public class MyBubbleSortDemo {
    public static void main(String[] args) {
        int[] arr = {3, 5, 2, 1, 4};
        // 期望结果: 1 2 3 4 5
 
        // 第一轮:把最大值5找出,并把它放到数组的最右边
        // -1 是为了让索引不超出范围
        for (int i = 0; i < arr.length - 1 - 0; i++) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        printArr(arr);
 
        // 第二轮
        for (int i = 0; i < arr.length - 1 - 1; i++) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        printArr(arr);
 
        // 第三轮
        for (int i = 0; i < arr.length - 1 - 2; i++) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        printArr(arr);
 
        // 第四轮
        for (int i = 0; i < arr.length - 1 - 3; i++) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        printArr(arr);
    }
 
    private static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

完整实现

将四轮循环合并为外层循环控制轮次,内层循环负责实际比较,内层循环的上限为 arr.length - 1 - i

package com.wb.mybubble;
 
public class MyBubbleSortDemo2 {
    public static void main(String[] args) {
        int[] arr = {3, 5, 2, 1, 4};
        bubbleSort(arr);
    }
 
    private static void bubbleSort(int[] arr) {
        // 外层循环控制的是次数,比数组的长度少一次
        for (int i = 0; i < arr.length - 1; i++) {
            // 内层循环就是实际比较的
            // -1 是为了让数组不要越界
            // -i 每一轮结束之后,我们就会少比一个数字
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        printArr(arr);
    }
 
    private static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

递归

1 ) 概念与示例

递归:方法定义中调用方法本身。把复杂问题层层转化为与原问题相似的规模更小的问题。

递归的两个核心:

  1. 递归出口:必须要有结束条件,否则会导致栈内存溢出(StackOverflowError)。
  2. 递归规则:找到将大问题拆分为小问题的规律,且每次递归的参数更靠近出口。

先用循环方式求 1~100 的和,再改为递归方式。

package com.wb.myfactorial;
 
public class MyFactorialDemo1 {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum = sum + i;
        }
        System.out.println(sum);
    }
}

递归实现:

package com.wb.myfactorial;
 
public class MyFactorialDemo2 {
    public static void main(String[] args) {
        int sum = getSum(100);
        System.out.println(sum);
    }
 
    private static int getSum(int i) {
        // 1~100之间的和
        // 100 + (1~99之间的和)
        //     99 + (1~98之间的和)
        //         ...
        //            1
        // 方法的作用: 求 1~i 之间和
        if (i == 1) {
            return 1;
        } else {
            return i + getSum(i - 1);
        }
    }
}

2 ) 求阶乘案例

需求:用递归求 5 的阶乘,并在控制台输出结果。

阶乘定义:5! = 5 × 4 × 3 × 2 × 1

package com.wb.myfactorial;
 
public class MyFactorialDemo3 {
    public static void main(String[] args) {
        int result = getJc(5);
        System.out.println(result);
    }
 
    private static int getJc(int i) {
        // 1. 一定要找到出口 
        if (i == 1) {
            return 1;
        } else {
            // 2. 递归的规则
            return i * getJc(i - 1);
        }
    }
}

3 ) 递归内存图

getJc(5) 为例,方法调用与返回的过程如下:

getJc(1)getJc(2)getJc(3)getJc(4)getJc(5)maingetJc(1)getJc(2)getJc(3)getJc(4)getJc(5)main调用 getJc(5)5 * getJc(4)4 * getJc(3)3 * getJc(2)2 * getJc(1)return 1return 2return 6return 24return 120
  • 方法调用时依次进栈,到达出口 i == 1 时返回 1,然后依次出栈计算结果。
  • 如果没有出口,方法会无限进栈,最终导致 StackOverflowError

快速排序

1 ) 思路分析

快速排序(快排)利用递归,每一次递归以左边第一个数作为 基准数,将比基准数小的放左边,比基准数大的放右边,从而确定基准数在数组中应存入的正确位置。

一次递归的步骤:

  1. 从右边开始找比基准数小的元素。
  2. 从左边开始找比基准数大的元素。
  3. 交换两个元素的位置。
  4. 重复上述过程,直到左右两个指针相遇。
  5. 基准数归位(与相遇位置的元素交换)。

一次递归结束后,基准数左边的元素都比它小,右边的都比它大,但左右两边内部的顺序尚未确定。接下来对左右两部分分别递归进行同样的操作。递归停止条件:left > right

2 ) 核心代码实现(只确定一个基准数的位置)

package com.wb.myquitesort;
 
public class MyQuiteSortDemo {
    public static void main(String[] args) {
        // 1. 从右开始找比基准数小的
        // 2. 从左开始找比基准数大的
        // 3. 交换两个值的位置
        // 4. 红色继续往左找,蓝色继续往右找,直到两个箭头指向同一个索引为止
        // 5. 基准数归位
        int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
 
        quiteSort(arr, 0, arr.length - 1);
 
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
 
    private static void quiteSort(int[] arr, int left, int right) {
        int left0 = left;
        int right0 = right;
 
        // 计算出基准数
        int baseNumber = arr[left0];
 
        while (left != right) {
            // 1. 从右开始找比基准数小的
            while (arr[right] >= baseNumber && right > left) {
                right--;
            }
            // 2. 从左开始找比基准数大的 
            while (arr[left] <= baseNumber && right > left) {
                left++;
            }
            // 3. 交换两个值的位置
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
        // 基准数归位
        int temp = arr[left];
        arr[left] = arr[left0];
        arr[left0] = temp;
    }
}

3 ) 完整递归实现

加入递归停止条件和左右递归调用。

package com.wb.myquitesort;
 
public class MyQuiteSortDemo2 {
    public static void main(String[] args) {
        int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
 
        quiteSort(arr, 0, arr.length - 1);
 
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
 
    private static void quiteSort(int[] arr, int left, int right) {
        // 递归停止条件 
        if (right < left) {
            return;
        }
 
        int left0 = left;
        int right0 = right;
 
        // 计算出基准数
        int baseNumber = arr[left0];
 
        while (left != right) {
            // 1. 从右开始找比基准数小的
            while (arr[right] >= baseNumber && right > left) {
                right--;
            }
            // 2. 从左开始找比基准数大的
            while (arr[left] <= baseNumber && right > left) {
                left++;
            }
            // 3. 交换两个值的位置
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
        // 基准数归位
        int temp = arr[left];
        arr[left] = arr[left0];
        arr[left0] = temp;
 
        // 递归处理左边部分
        quiteSort(arr, left0, left - 1);
        // 递归处理右边部分
        quiteSort(arr, left + 1, right0);
    }
}

运行结果:1 2 3 4 5 6 7 8 9 10

Arrays工具类

java.util.Arrays 类提供了操作数组的静态方法,常用方法:

  • toString(array):将数组内容拼接成字符串返回。
  • sort(array):对数组进行升序排序(底层使用快速排序)。
  • binarySearch(array, key):使用二分查找法查找元素索引,数组必须有序。

代码示例

package com.wb.myutils;
 
import java.util.Arrays;
 
public class MyArraysDemo {
    public static void main(String[] args) {
        int[] arr = {3, 2, 4, 6, 7};
 
        // 1. toString: 将数组转换为字符串
        System.out.println(Arrays.toString(arr)); // [3, 2, 4, 6, 7]
 
        // 2. sort: 排序(升序)
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr)); // [2, 3, 4, 6, 7]
 
        // 3. binarySearch: 二分查找(数组必须有序)
        int[] sortedArr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int index = Arrays.binarySearch(sortedArr, 10);
        System.out.println(index); // 9
 
        // 查找不存在的元素: 返回 -插入点 - 1
        int index2 = Arrays.binarySearch(sortedArr, 11);
        System.out.println(index2); // -11  (-10 - 1)
        int index3 = Arrays.binarySearch(sortedArr, 0);
        System.out.println(index3); // -1   (-0 - 1)
    }
}

注意点:

  1. binarySearch 调用前数组必须有序。
  2. 如果元素存在,返回实际索引。
  3. 如果元素不存在,返回 -插入点 - 1。插入点是指该元素如果存在应该在的索引位置,减1是为了避免与索引0冲突(例如查找0时返回 -1 而不是 0)。

总结

以上内容涵盖了数组高级操作中的二分查找、冒泡排序、递归、快速排序以及 Arrays 工具类的核心知识与代码实现,适合作为 Java 基础阶段的算法入门参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值