本文纲要
- 二分查找
思路分析
代码实现 - 冒泡排序
思路分析
分步实现
完整实现 - 递归
概念与示例
求阶乘案例
递归内存图 - 快速排序
思路分析
核心代码实现
完整递归实现 - Arrays工具类
toString
sort
binarySearch
二分查找
1 ) 思路分析
二分查找的前提条件:数组元素必须按照大小顺序排列。
核心思想:每次去掉一半的查找范围,在剩余的范围中继续查找,直到找到目标元素或范围无效。
- 定义两个变量
min和max,表示当前查找范围(最小索引和最大索引)。 - 根据
min和max计算出中间位置mid:mid = (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 ) 概念与示例
递归:方法定义中调用方法本身。把复杂问题层层转化为与原问题相似的规模更小的问题。
递归的两个核心:
- 递归出口:必须要有结束条件,否则会导致栈内存溢出(
StackOverflowError)。 - 递归规则:找到将大问题拆分为小问题的规律,且每次递归的参数更靠近出口。
先用循环方式求 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) 为例,方法调用与返回的过程如下:
- 方法调用时依次进栈,到达出口
i == 1时返回1,然后依次出栈计算结果。 - 如果没有出口,方法会无限进栈,最终导致
StackOverflowError。
快速排序
1 ) 思路分析
快速排序(快排)利用递归,每一次递归以左边第一个数作为 基准数,将比基准数小的放左边,比基准数大的放右边,从而确定基准数在数组中应存入的正确位置。
一次递归的步骤:
- 从右边开始找比基准数小的元素。
- 从左边开始找比基准数大的元素。
- 交换两个元素的位置。
- 重复上述过程,直到左右两个指针相遇。
- 基准数归位(与相遇位置的元素交换)。
一次递归结束后,基准数左边的元素都比它小,右边的都比它大,但左右两边内部的顺序尚未确定。接下来对左右两部分分别递归进行同样的操作。递归停止条件: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)
}
}
注意点:
binarySearch调用前数组必须有序。- 如果元素存在,返回实际索引。
- 如果元素不存在,返回
-插入点 - 1。插入点是指该元素如果存在应该在的索引位置,减1是为了避免与索引0冲突(例如查找0时返回-1而不是0)。
总结
以上内容涵盖了数组高级操作中的二分查找、冒泡排序、递归、快速排序以及 Arrays 工具类的核心知识与代码实现,适合作为 Java 基础阶段的算法入门参考

4079

被折叠的 条评论
为什么被折叠?



