一、快速排序 QuickSort
1. 算法思想(分治思想)
- 选基准值:以数组最左侧元素作为基准 base
- 左右双指针遍历:右指针先往左找小于基准的数,左指针往右找大于基准的数,两数交换
- 指针相遇时,基准归位:此时基准左边全部≤基准,右边全部≥基准
- 递归处理基准左侧、右侧子数组,直到区间只有一个元素(递归终止)
2. 完整源码 QuickSort.java
package com.qcby.sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {5,7,4,2,0,3,1,6};
sort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
/**
* 快速排序递归方法
* @param arr 待排序数组
* @param left 左边界下标
* @param right 右边界下标
*/
public static void sort(int[] arr,int left,int right) {
// 递归终止:左边界>=右边界,区间无元素/单个元素无需排序
if(left>=right) {
return;
}
// 选取最左侧作为基准值
int base = arr[left];
int i=left; // 左指针
int j=right; // 右指针
// 左右指针未相遇时循环遍历
while(i!=j) {
// 右指针向左寻找小于base的元素
while(arr[j]>=base&&i!=j) {
j--;
}
// 左指针向右寻找大于base的元素
while(arr[i]<=base&&i!=j) {
i++;
}
// 交换i、j指针位置元素
int temp = arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
// 基准值归位:把相遇位置元素放到原基准位,基准放入中间分割点
arr[left]=arr[i];
arr[i]=base;
// 递归排序左区间 [left, i-1]
sort(arr,left,i-1);
// 递归排序右区间 [i+1, right]
sort(arr,i+1,right);
}
}
3. 运行结果
输入数组:{5,7,4,2,0,3,1,6} 输出:[0, 1, 2, 3, 4, 5, 6, 7]
4. 优缺点总结
- 优点:平均时间复杂度 O (nlogn),实际排序效率极高,是工业最常用排序
- 缺点:最坏 O (n²)(有序数组选首元素做基准);递归深度大时会栈溢出
二、手动模拟 ArrayList 底层实现
需求说明
基于原生数组手动实现简易 ArrayList,实现三大核心功能:
- add () 添加元素,数组满时自动 1.5 倍扩容
- delete () 删除数组中所有匹配值的元素
- toString () 自定义格式化打印集合
1. 自定义集合 ArrayList.java
package com.qcby.array;
public class ArrayList {
// 底层存储数组,初始容量10
int[] arr = new int[10];
// 实际存储元素个数
int size = 0;
/**
* 添加元素,自动扩容
* @param value 待添加数字
*/
public void add(int value) {
// 判断数组是否存满
if(size==arr.length) {
// 扩容为原长度1.5倍
int[] brr = new int[(int) (arr.length*1.5)];
// 拷贝旧数组数据到新数组
for(int i=0;i<arr.length;i++) {
brr[i]=arr[i];
}
// 底层数组指向扩容后的新数组
arr=brr;
}
// 存入元素
arr[size]=value;
// 有效元素数量+1
size++;
}
/**
* 删除数组中所有等于value的元素
* @param value 需要删除的目标值
*/
public void delete(int value) {
// 倒序遍历,防止删除后元素前移导致漏删
for(int i = size-1;i>=0;i--) {
if(arr[i]==value) {
// 当前元素后所有元素向前覆盖一位
for(int j=i+1;j<size;j++) {
arr[j-1]=arr[j];
}
// 有效长度-1
size--;
}
}
}
/**
* 重写toString,格式化输出集合
*/
public String toString() {
String str = "[";
for(int i=0;i<size;i++) {
if(i==size-1) {
str = str+arr[i];
}else {
str = str+arr[i] + ", ";
}
}
str = str+"]";
return str;
}
}
2. 测试类 Test.java
package com.qcby.array;
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// 添加大量测试数据,触发自动扩容
list.add(10);
list.add(5);
list.add(14);
list.add(1);
list.add(26);
list.add(7);
list.add(26);
list.add(19);
list.add(31);
list.add(26);
list.add(26);
list.add(179);
list.add(20);
list.add(26);
list.add(50);
list.add(80);
list.add(90);
System.out.println("原始集合:"+list.toString());
list.delete(1);
System.out.println("删除1后:"+list.toString());
list.delete(26);
System.out.println("删除全部26后:"+list.toString());
}
}
3. 核心知识点解析
(1)自动扩容机制
- 底层数组初始容量:10
- 当
size == arr.length代表数组已满 - 新建长度为原数组 1.5 倍的数组,循环拷贝旧数组元素,替换底层数组
(2)删除设计:倒序遍历
- 正序遍历删除元素时,数组元素前移会跳过下一个待判断元素,出现漏删
- 倒序从尾部向前遍历,删除元素不会影响前面未遍历下标,可删除所有匹配值
(3)自定义 toString
拼接字符串输出标准集合格式 [10, 5, 14...],区分底层数组空余位置,只打印有效 size 范围内元素
三、总结
- 快速排序
- 不要忘记基准归位操作,否则数组排序完全错乱
- 递归终止条件
left>=right不能省略,否则无限递归栈溢出
- 自定义 ArrayList
- 扩容必须替换底层数组引用
arr=brr,否则扩容失效 - 删除必须倒序遍历,否则重复元素无法全部删除
- size 代表有效元素个数,打印、增删全部依赖 size,不能使用数组 length
- 扩容必须替换底层数组引用
四、拓展优化方向
- 快速排序:优化基准(随机基准 / 三数取中法)、小数组改用插入排序、非递归实现避免栈溢出
- 自定义 ArrayList:使用泛型支持任意引用类型、按下标删除、get/set 方法、缩容机制