算法与数据结构体系课程
什么是算法
解决问题的方法
五大特性
有限性
确定性
可行性
输入
输出
什么是数据结构
研究的是数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据获取修改数据
排序算法
选择排序
先把最小的拿出来,剩下的再把最小的拿出来,可以原地比较交换排序
public class Sort {
public static void mySort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int temp = arr[i];
int index = 0;
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
arr[i] = arr[j];
index = j;
}
}
if (index != 0) {
arr[index] = temp;
}
}
for (int i : arr) {
System.out.println(i);
}
}
public static void sort(int[] arr) {
// arr[0...i) 是有序的;arr[i...n) 是无序的
for (int i = 0; i < arr.length; i++) {
// 选择 arr[i...n) 中的最小值的索引
int minIndex = i;
for (int j = i; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) minIndex = j;
}
swap(arr, i, minIndex);
}
for (int i : arr) {
System.out.println(i);
}
}
private static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int[] a = { 6, 4, 2, 3, 1, 5 };
// mySort(a);
sort(a);
}
}
使用泛型
public class SelectionSort {
private SelectionSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
// arr[0...i) 是有序的;arr[i...n) 是无序的
for (int i = 0; i < arr.length; i++) {
// 选择 arr[i...n) 中的最小值
int minIndex = i;
for (int j = i; j < arr.length; j++) {
if (arr[j].compareTo(arr[minIndex]) < 0) minIndex = j;
}
swap(arr, i, minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
Integer[] arr = { 1, 4, 2, 3, 6, 5 };
SelectionSort.sort(arr);
for (int e : arr) System.out.print(e + " ");
System.out.println();
}
}
对象实现 comparable 接口,使得类对象可以使用选择排序算法
public class SelectionSort {
private SelectionSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
// arr[0...i) 是有序的;arr[i...n) 是无序的
for (int i = 0; i < arr.length; i++) {
// 选择 arr[i...n) 中的最小值
int minIndex = i;
for (int j = i; j < arr.length; j++) {
if (arr[j].compareTo(arr[minIndex]) < 0) minIndex = j;
}
swap(arr, i, minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
Integer[] arr = { 1, 4, 2, 3, 6, 5 };
SelectionSort.sort(arr);
for (int e : arr) System.out.print(e + " ");
System.out.println();
Student[] students = {
new Student("Alice", 98),
new Student("Bobo", 100),
new Student("Charles", 66),
};
SelectionSort.sort(students);
for (Student student : students) System.out.print(student + " ");
System.out.println();
}
}
public class Student implements Comparable<Student> {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student another) {
// if(this.score < another.score)
// return -1;
// else if(this.score == another.score)
// return 0;
// return 1;
// return this.score - another.score;
return another.score - this.score;
}
@Override
public boolean equals(Object student) {
if (this == student) return true;
if (student == null) return false;
if (this.getClass() != student.getClass()) return false;
Student another = (Student) student;
return this.score == another.score;
}
@Override
public String toString() {
return String.format("Student(name: %s, score: %d)", name, score);
}
}
编写验证排序算法
public class SortingHelper {
private SortingHelper() {}
public static <E extends Comparable<E>> boolean isSorted(E[] arr) {
for (int i = 1; i < arr.length; i++) if (
arr[i - 1].compareTo(arr[i]) > 0
) return false;
return true;
}
public static <E extends Comparable<E>> void sortTest(
String sortname,
E[] arr
) {
long startTime = System.nanoTime();
if (sortname.equals("SelectionSort")) SelectionSort.sort(arr);
long endTime = System.nanoTime();
double time = (endTime - startTime) / 1000000000.0;
if (!SortingHelper.isSorted(arr)) throw new RuntimeException(
sortname + " failed"
);
System.out.println(
String.format("%s , n = %d : %f s", sortname, arr.length, time)
);
}
}
public class SelectionSort {
private SelectionSort(){}
public static <E extends Comparable> void sort(E[] arr){
for(int i = 0; i < arr.length; i ++){
// 选择 arr[i...n) 中的最小值
int minIndex = i;
for(int j = i; j < arr.length; j ++){
if(arr[j].compareTo(arr[minIndex]) < 0)
minIndex = j;
}
swap(arr, i, minIndex);
}
}
private static <E> void swap(E[] arr, int i, int j){
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args){
int[] dataSize = {10000, 100000};
for(int n: dataSize){
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
SortingHelper.sortTest("SelectionSort", arr);
}
}
}
import java.util.Random;
public class ArrayGenerator {
private ArrayGenerator(){}
public static Integer[] generateOrderedArray(int n){
Integer[] arr = new Integer[n];
for(int i = 0; i < n; i ++)
arr[i] = i;
return arr;
}
// 生成一个长度为 n 的随机数组,每个数字的范围是 [0, bound)
public static Integer[] generateRandomArray(int n, int bound){
Integer[] arr = new Integer[n];
Random rnd = new Random();
for(int i = 0; i < n; i ++)
arr[i] = rnd.nextInt(bound);
return arr;
}
}
选择排序算法的时间复杂度恒为 O(n^2)
插入排序
每处理一个数据,就把这个数据插入到前面已经排好序的数据中
public class InsertionSort {
private InsertionSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
for (int i = 0; i < arr.length; i++) {
// 将 arr[i] 插入到合适的位置
// for(int j = i; j - 1 >= 0; j --){
// if(arr[j].compareTo(arr[j - 1]) < 0)
// swap(arr, j - 1, j);
// else break;
// }
for (int j = i; j - 1 >= 0 && arr[j].compareTo(arr[j - 1]) < 0; j--) {
swap(arr,j - 1,j);
}
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
private static <E extends Comparable<E>> boolean isSorted(E[] arr) {
for (int i = 1; i < arr.length; i++) if (
arr[i - 1].compareTo(arr[i]) > 0
) return false;
return true;
}
public static void main(String[] args) {
int[] dataSize = { 10000, 100000 };
for (int n : dataSize) {
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
SortingHelper.sortTest("InsertionSort", arr);
}
}
}
优化后
public static <E extends Comparable<E>> void sort2(E[] arr){
for(int i = 0; i < arr.length; i ++){
// 将 arr[i] 插入到合适的位置
E t = arr[i];
int j;
for(j = i; j - 1 >= 0 && t.compareTo(arr[j - 1]) < 0; j --){
arr[j] = arr[j - 1];
}
arr[j] = t;
}
}
对于已排序的数据,插入排序算法时间复杂度退化为 O(n)
冒泡排序
每次比较相邻的两个元素
public class BubbleSort {
private BubbleSort() {}
public static <E extends Comparable<E>> void sort(E[] data) {
for (int i = 0; i + 1 < data.length; i++) {
// arr[n - i, n) 已排好序
// 通过冒泡在 arr[n - i - 1] 位置放上合适的元素
for (int j = 0; j + 1 <= data.length - i - 1; j++) if (
data[j].compareTo(data[j + 1]) > 0
) swap(data, j, j + 1);
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 100000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
SortingHelper.sortTest("BubbleSort", arr);
}
}
如果数组已经是顺序的,加入判断进行跳出
public static <E extends Comparable<E>> void sort2(E[] data){
for(int i = 0; i + 1 < data.length; i ++){
// arr[n - i, n) 已排好序
// 通过冒泡在 arr[n - i - 1] 位置放上合适的元素
boolean isSwapped = false;
for(int j = 0; j < data.length - i - 1; j ++)
if(data[j].compareTo(data[j + 1]) > 0){
swap(data, j, j + 1);
isSwapped = true;
}
if(!isSwapped) break;
}
}
继续优化,第一轮排序后最后一个交换的位置后面已经排序好
public static <E extends Comparable<E>> void sort3(E[] data){
for(int i = 0; i + 1 < data.length; ){
// arr[n - i, n) 已排好序
// 通过冒泡在 arr[n - i - 1] 位置放上合适的元素
int lastSwappedIndex = 0;
for(int j = 0; j < data.length - i - 1; j ++)
if(data[j].compareTo(data[j + 1]) > 0){
swap(data, j, j + 1);
lastSwappedIndex = j + 1;
}
i = data.length - lastSwappedIndex;
}
}
希尔排序
让数组越来越有序,不能只处理相邻的逆序对
不同的步长序列,复杂度分析不同
import java.util.Arrays;
public class ShellSort {
private ShellSort() {}
public static <E extends Comparable<E>> void sort(E[] data) {
int h = data.length / 2;
while (h >= 1) {
for (int start = 0; start < h; start++) {
// 对 data[start, start + h, start + 2h, start + 3h ...], 进行插入排序
for (int i = start + h; i < data.length; i += h) {
E t = data[i];
int j;
for (
j = i;
j - h >= 0 && t.compareTo(data[j - h]) < 0;
j -= h
) data[j] = data[j - h];
data[j] = t;
}
}
h /= 2;
}
}
}
优化成两层循环
import java.util.Arrays;
public class ShellSort {
private ShellSort() {}
public static <E extends Comparable<E>> void sort(E[] data) {
int h = data.length / 2;
while (h >= 1) {
for (int i = h; i < data.length; i++) {
E t = data[i];
int j;
for (
j = i;
j - h >= 0 && t.compareTo(data[j - h]) < 0;
j -= h
) data[j] = data[j - h];
data[j] = t;
}
h /= 2;
}
}
public static void main(String[] args) {
int n = 1000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("ShellSort", arr);
SortingHelper.sortTest("MergeSort", arr2);
}
}
快速排序
选定一个基准元素,通过该元素将数组分成左右两部分
单路快速排序
完全有序的数据退化
引入随机化
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
private QuickSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (l >= r) return;
int p = partition(arr, l, r);
sort(arr, l, p - 1);
sort(arr, p + 1, r);
}
private static <E extends Comparable<E>> int partition(
E[] arr,
int l,
int r
) {
// 生成 [l, r] 之间的随机索引
int p = l + (new Random()).nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...j] < v ; arr[j+1...i] >= v
int j = l;
for (int i = l + 1; i <= r; i++) if (arr[i].compareTo(arr[l]) < 0) {
j++;
swap(arr, i, j);
}
swap(arr, l, j);
return j;
}
// 在 sort2 中,我们只创建一个 Random 类的对象
public static <E extends Comparable<E>> void sort2(E[] arr) {
Random rnd = new Random();
sort2(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort2(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
int p = partition2(arr, l, r, rnd);
sort2(arr, l, p - 1, rnd);
sort2(arr, p + 1, r, rnd);
}
private static <E extends Comparable<E>> int partition2(
E[] arr,
int l,
int r,
Random rnd
) {
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...j] < v ; arr[j+1...i] >= v
int j = l;
for (int i = l + 1; i <= r; i++) if (arr[i].compareTo(arr[l]) < 0) {
j++;
swap(arr, i, j);
}
swap(arr, l, j);
return j;
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 1000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
Integer[] arr3 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("MergeSort", arr);
SortingHelper.sortTest("QuickSort", arr2);
SortingHelper.sortTest("QuickSort2", arr3);
}
}
双路快速排序算法
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
private QuickSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
Random rnd = new Random();
sort(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
int p = partition(arr, l, r, rnd);
sort(arr, l, p - 1, rnd);
sort(arr, p + 1, r, rnd);
}
private static <E extends Comparable<E>> int partition(
E[] arr,
int l,
int r,
Random rnd
) {
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...j] < v ; arr[j+1...i] >= v
int j = l;
for (int i = l + 1; i <= r; i++) if (arr[i].compareTo(arr[l]) < 0) {
j++;
swap(arr, i, j);
}
swap(arr, l, j);
return j;
}
public static <E extends Comparable<E>> void sort2ways(E[] arr) {
Random rnd = new Random();
sort2ways(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort2ways(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
int p = partition2ways(arr, l, r, rnd);
sort2ways(arr, l, p - 1, rnd);
sort2ways(arr, p + 1, r, rnd);
}
private static <E extends Comparable<E>> int partition2ways(
E[] arr,
int l,
int r,
Random rnd
) {
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...i-1] <= v; arr[j+1...r] >= v
int i = l + 1, j = r;
while (true) {
while (i <= j && arr[i].compareTo(arr[l]) < 0) i++;
while (j >= i && arr[j].compareTo(arr[l]) > 0) j--;
if (i >= j) break;
swap(arr, i, j);
i++;
j--;
}
swap(arr, l, j);
return j;
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 100000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
System.out.println("Random Array");
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
System.out.println();
arr = ArrayGenerator.generateOrderedArray(n);
arr2 = Arrays.copyOf(arr, arr.length);
System.out.println("Ordered Array");
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
System.out.println();
arr = ArrayGenerator.generateRandomArray(n, 1);
arr2 = Arrays.copyOf(arr, arr.length);
System.out.println("Same Value Array");
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
System.out.println();
}
}
三路快速排序算法
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
private QuickSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
Random rnd = new Random();
sort(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
int p = partition(arr, l, r, rnd);
sort(arr, l, p - 1, rnd);
sort(arr, p + 1, r, rnd);
}
private static <E extends Comparable<E>> int partition(
E[] arr,
int l,
int r,
Random rnd
) {
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...j] < v ; arr[j+1...i] >= v
int j = l;
for (int i = l + 1; i <= r; i++) if (arr[i].compareTo(arr[l]) < 0) {
j++;
swap(arr, i, j);
}
swap(arr, l, j);
return j;
}
public static <E extends Comparable<E>> void sort2ways(E[] arr) {
Random rnd = new Random();
sort2ways(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort2ways(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
int p = partition2ways(arr, l, r, rnd);
sort2ways(arr, l, p - 1, rnd);
sort2ways(arr, p + 1, r, rnd);
}
private static <E extends Comparable<E>> int partition2ways(
E[] arr,
int l,
int r,
Random rnd
) {
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...i-1] <= v; arr[j+1...r] >= v
int i = l + 1, j = r;
while (true) {
while (i <= j && arr[i].compareTo(arr[l]) < 0) i++;
while (j >= i && arr[j].compareTo(arr[l]) > 0) j--;
if (i >= j) break;
swap(arr, i, j);
i++;
j--;
}
swap(arr, l, j);
return j;
}
public static <E extends Comparable<E>> void sort3ways(E[] arr) {
Random rnd = new Random();
sort3ways(arr, 0, arr.length - 1, rnd);
}
private static <E extends Comparable<E>> void sort3ways(
E[] arr,
int l,
int r,
Random rnd
) {
if (l >= r) return;
/** 三路快速排序的 partition 过程 **/
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l + 1, lt] < v, arr[lt + 1, i - 1] == v, arr[gt, r] > v
int lt = l, i = l + 1, gt = r + 1;
while (i < gt) {
if (arr[i].compareTo(arr[l]) < 0) {
lt++;
swap(arr, i, lt);
i++;
} else if (arr[i].compareTo(arr[l]) > 0) {
gt--;
swap(arr, i, gt);
} else { // arr[i] == v
i++;
}
}
swap(arr, l, lt);
/** 三路快速排序的 partition 过程结束 **/
// 递归调用
sort3ways(arr, l, lt - 1, rnd);
sort3ways(arr, gt, r, rnd);
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 1000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
Integer[] arr3 = Arrays.copyOf(arr, arr.length);
System.out.println("Random Array");
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
SortingHelper.sortTest("QuickSort3Ways", arr3);
System.out.println();
arr = ArrayGenerator.generateOrderedArray(n);
arr2 = Arrays.copyOf(arr, arr.length);
arr3 = Arrays.copyOf(arr, arr.length);
System.out.println("Ordered Array");
SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
SortingHelper.sortTest("QuickSort3Ways", arr3);
System.out.println();
arr = ArrayGenerator.generateRandomArray(n, 1);
arr2 = Arrays.copyOf(arr, arr.length);
arr3 = Arrays.copyOf(arr, arr.length);
System.out.println("Same Value Array");
// SortingHelper.sortTest("QuickSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
SortingHelper.sortTest("QuickSort3Ways", arr3);
System.out.println();
}
}
归并
建立在归并操作上的一种有效的排序算法,该算法是采用分治法
复杂度为 nlogn,对完全有序的数组复杂度为 O(n)
优化 1: 判断是否需要 merge
优化 2: 对小规模数据使用插入排序
优化 3: 只创建一个临时空间
归并排序算法不是原地排序算法
import java.util.Arrays;
public class MergeSort {
private MergeSort() {}
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (l >= r) return;
/* 考虑到l + r相加的时候r的值太大会溢出
* 比如 l = 3, r = 21。
* (l + r) / 2 = (21 + 3) / 2 = 24 / 2 = 12;
* l + (r - l) / 2 = 3 + (21 - 3) / 2 = 3 + 18 / 2 = 3 + 9 = 12;
*/
int mid = l + (r - l) / 2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
if (arr[mid].compareTo(arr[mid + 1]) > 0) merge(arr, l, mid, r);
}
// 合并两个有序的区间 arr[l, mid] 和 arr[mid + 1, r]
private static <E extends Comparable<E>> void merge(
E[] arr,
int l,
int mid,
int r
) {
E[] temp = Arrays.copyOfRange(arr, l, r + 1);
int i = l, j = mid + 1;
// 每轮循环为 arr[k] 赋值
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = temp[j - l];
j++;
} else if (j > r) {
arr[k] = temp[i - l];
i++;
} else if (temp[i - l].compareTo(temp[j - l]) <= 0) {
arr[k] = temp[i - l];
i++;
} else {
arr[k] = temp[j - l];
j++;
}
}
}
public static void main(String[] args) {
int n = 100000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
SortingHelper.sortTest("MergeSort", arr);
}
}
通过内存复用进行优化
import java.util.Arrays;
public class MergeSort {
private MergeSort() {}
public static <E extends Comparable> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
private static <E extends Comparable> void sort(E[] arr, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
sort(arr, l, mid);
sort(arr, mid + 1, r);
if (arr[mid].compareTo(arr[mid + 1]) > 0) merge(arr, l, mid, r);
}
private static <E extends Comparable> void merge(
E[] arr,
int l,
int mid,
int r
) {
E[] temp = Arrays.copyOfRange(arr, l, r + 1);
int i = l, j = mid + 1;
// 每轮循环为 arr[k] 赋值
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = temp[j - l];
j++;
} else if (j > r) {
arr[k] = temp[i - l];
i++;
} else if (temp[i - l].compareTo(temp[j - l]) <= 0) {
arr[k] = temp[i - l];
i++;
} else {
arr[k] = temp[j - l];
j++;
}
}
}
public static <E extends Comparable> void sort2(E[] arr) {
E[] temp = Arrays.copyOf(arr, arr.length);
sort2(arr, 0, arr.length - 1, temp);
}
private static <E extends Comparable> void sort2(
E[] arr,
int l,
int r,
E[] temp
) {
if (l >= r) return;
int mid = l + (r - l) / 2;
sort2(arr, l, mid, temp);
sort2(arr, mid + 1, r, temp);
if (arr[mid].compareTo(arr[mid + 1]) > 0) merge2(arr, l, mid, r, temp);
}
private static <E extends Comparable> void merge2(
E[] arr,
int l,
int mid,
int r,
E[] temp
) {
System.arraycopy(arr, l, temp, l, r - l + 1);
int i = l, j = mid + 1;
// 每轮循环为 arr[k] 赋值
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = temp[j];
j++;
} else if (j > r) {
arr[k] = temp[i];
i++;
} else if (temp[i].compareTo(temp[j]) <= 0) {
arr[k] = temp[i];
i++;
} else {
arr[k] = temp[j];
j++;
}
}
}
public static void main(String[] args) {
int n = 5000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("MergeSort", arr);
SortingHelper.sortTest("MergeSort2", arr2);
}
}
自底向上的归并排序
import java.util.Arrays;
public class MergeSort {
private MergeSort() {}
// 自顶向下的归并排序
public static <E extends Comparable<E>> void sort(E[] arr) {
E[] temp = Arrays.copyOf(arr, arr.length);
sort(arr, 0, arr.length - 1, temp);
}
private static <E extends Comparable<E>> void sort(
E[] arr,
int l,
int r,
E[] temp
) {
if (l >= r) return;
int mid = l + (r - l) / 2;
sort(arr, l, mid, temp);
sort(arr, mid + 1, r, temp);
if (arr[mid].compareTo(arr[mid + 1]) > 0) merge(arr, l, mid, r, temp);
}
// 自底向上的归并排序
public static <E extends Comparable<E>> void sortBU(E[] arr) {
E[] temp = Arrays.copyOf(arr, arr.length);
int n = arr.length;
// 遍历合并的区间长度
for (int sz = 1; sz < n; sz += sz) {
// 遍历合并的两个区间的起始位置 i
// 合并 [i, i + sz - 1] 和 [i + sz, Math.min(i + sz + sz - 1, n - 1)]
for (int i = 0; i + sz < n; i += sz + sz) if (
arr[i + sz - 1].compareTo(arr[i + sz]) > 0
) merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, n - 1), temp);
}
}
private static <E extends Comparable<E>> void merge(
E[] arr,
int l,
int mid,
int r,
E[] aux
) {
System.arraycopy(arr, l, aux, l, r - l + 1);
int i = l, j = mid + 1;
// 每轮循环为 arr[k] 赋值
for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = aux[j];
j++;
} else if (j > r) {
arr[k] = aux[i];
i++;
} else if (aux[i].compareTo(aux[j]) <= 0) {
arr[k] = aux[i];
i++;
} else {
arr[k] = aux[j];
j++;
}
}
}
public static void main(String[] args) {
int n = 10000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("MergeSort", arr);
SortingHelper.sortTest("MergeSortBU", arr2);
}
}
线性数据结构
数组
自定义数组
public class Array {
private int[] data;
private int size;
// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity) {
data = new int[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity=10
public Array() {
this(10);
}
// 获取数组的容量
public int getCapacity() {
return data.length;
}
// 获取数组中的元素个数
public int getSize() {
return size;
}
// 返回数组是否为空
public boolean isEmpty() {
return size == 0;
}
// 向所有元素后添加一个新元素
public void addLast(int e) {
add(size, e);
}
// 在所有元素前添加一个新元素
public void addFirst(int e) {
add(0, e);
}
// 在index索引的位置插入一个新元素e
public void add(int index, int e) {
if (size == data.length) throw new IllegalArgumentException(
"Add failed. Array is full."
);
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Require index >= 0 and index <= size."
);
for (int i = size - 1; i >= index; i--) data[i + 1] = data[i];
data[index] = e;
size++;
}
// 获取index索引位置的元素
public int get(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Get failed. Index is illegal."
);
return data[index];
}
// 修改index索引位置的元素为e
public void set(int index, int e) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Set failed. Index is illegal."
);
data[index] = e;
}
// 查找数组中是否有元素e
public boolean contains(int e) {
for (int i = 0; i < size; i++) {
if (data[i] == e) return true;
}
return false;
}
// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(int e) {
for (int i = 0; i < size; i++) {
if (data[i] == e) return i;
}
return -1;
}
// 从数组中删除index位置的元素, 返回删除的元素
public int remove(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Remove failed. Index is illegal."
);
int ret = data[index];
for (int i = index + 1; i < size; i++) data[i - 1] = data[i];
size--;
return ret;
}
// 从数组中删除第一个元素, 返回删除的元素
public int removeFirst() {
return remove(0);
}
// 从数组中删除最后一个元素, 返回删除的元素
public int removeLast() {
return remove(size - 1);
}
// 从数组中删除元素e
public void removeElement(int e) {
int index = find(e);
if (index != -1) remove(index);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(
String.format("Array: size = %d , capacity = %d\n", size, data.length)
);
res.append('[');
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size - 1) res.append(", ");
}
res.append(']');
return res.toString();
}
}
使用泛型
public class Array<E> {
private E[] data;
private int size;
// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity) {
data = (E[]) new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity=10
public Array() {
this(10);
}
// 获取数组的容量
public int getCapacity() {
return data.length;
}
// 获取数组中的元素个数
public int getSize() {
return size;
}
// 返回数组是否为空
public boolean isEmpty() {
return size == 0;
}
// 在index索引的位置插入一个新元素e
public void add(int index, E e) {
if (size == data.length) throw new IllegalArgumentException(
"Add failed. Array is full."
);
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Require index >= 0 and index <= size."
);
for (int i = size - 1; i >= index; i--) data[i + 1] = data[i];
data[index] = e;
size++;
}
// 向所有元素后添加一个新元素
public void addLast(E e) {
add(size, e);
}
// 在所有元素前添加一个新元素
public void addFirst(E e) {
add(0, e);
}
// 获取index索引位置的元素
public E get(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Get failed. Index is illegal."
);
return data[index];
}
// 修改index索引位置的元素为e
public void set(int index, E e) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Set failed. Index is illegal."
);
data[index] = e;
}
// 查找数组中是否有元素e
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) return true;
}
return false;
}
// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) return i;
}
return -1;
}
// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Remove failed. Index is illegal."
);
E ret = data[index];
for (int i = index + 1; i < size; i++) data[i - 1] = data[i];
size--;
data[size] = null; // loitering objects != memory leak
return ret;
}
// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst() {
return remove(0);
}
// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast() {
return remove(size - 1);
}
// 从数组中删除元素e
public void removeElement(E e) {
int index = find(e);
if (index != -1) remove(index);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(
String.format("Array: size = %d , capacity = %d\n", size, data.length)
);
res.append('[');
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size - 1) res.append(", ");
}
res.append(']');
return res.toString();
}
}
动态数组
public class Array<E> {
private E[] data;
private int size;
// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity) {
data = (E[]) new Object[capacity];
size = 0;
}
// 无参数的构造函数,默认数组的容量capacity=10
public Array() {
this(10);
}
// 获取数组的容量
public int getCapacity() {
return data.length;
}
// 获取数组中的元素个数
public int getSize() {
return size;
}
// 返回数组是否为空
public boolean isEmpty() {
return size == 0;
}
// 在index索引的位置插入一个新元素e
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Require index >= 0 and index <= size."
);
if (size == data.length) resize(2 * data.length);
for (int i = size - 1; i >= index; i--) data[i + 1] = data[i];
data[index] = e;
size++;
}
// 向所有元素后添加一个新元素
public void addLast(E e) {
add(size, e);
}
// 在所有元素前添加一个新元素
public void addFirst(E e) {
add(0, e);
}
// 获取index索引位置的元素
public E get(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Get failed. Index is illegal."
);
return data[index];
}
// 修改index索引位置的元素为e
public void set(int index, E e) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Set failed. Index is illegal."
);
data[index] = e;
}
// 查找数组中是否有元素e
public boolean contains(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) return true;
}
return false;
}
// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e) {
for (int i = 0; i < size; i++) {
if (data[i].equals(e)) return i;
}
return -1;
}
// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Remove failed. Index is illegal."
);
E ret = data[index];
for (int i = index + 1; i < size; i++) data[i - 1] = data[i];
size--;
data[size] = null; // loitering objects != memory leak
if (size == data.length / 2) resize(data.length / 2);
return ret;
}
// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst() {
return remove(0);
}
// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast() {
return remove(size - 1);
}
// 从数组中删除元素e
public void removeElement(E e) {
int index = find(e);
if (index != -1) remove(index);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(
String.format("Array: size = %d , capacity = %d\n", size, data.length)
);
res.append('[');
for (int i = 0; i < size; i++) {
res.append(data[i]);
if (i != size - 1) res.append(", ");
}
res.append(']');
return res.toString();
}
// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) newData[i] = data[i];
data = newData;
}
}
均摊复杂度和防止复杂度的震荡
扩容时扩容一倍
缩容时等于 length/4 时才缩容
// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");
E ret = data[index];
for(int i = index + 1 ; i < size ; i ++)
data[i - 1] = data[i];
size --;
data[size] = null; // loitering objects != memory leak
if(size == data.length / 4 && data.length / 2 != 0)
resize(data.length / 2);
return ret;
}
链表
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node head;
private int size;
public LinkedList() {
head = null;
size = 0;
}
// 获取链表中的元素个数
public int getSize() {
return size;
}
// 返回链表是否为空
public boolean isEmpty() {
return size == 0;
}
// 在链表头添加新的元素e
public void addFirst(E e) {
// Node node = new Node(e);
// node.next = head;
// head = node;
head = new Node(e, head);
size++;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Illegal index."
);
if (index == 0) addFirst(e); else {
Node prev = head;
for (int i = 0; i < index - 1; i++) prev = prev.next;
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
prev.next = new Node(e, prev.next);
size++;
}
}
// 在链表末尾添加新的元素e
public void addLast(E e) {
add(size, e);
}
}
使用虚拟头节点
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList() {
dummyHead = new Node();
size = 0;
}
// 获取链表中的元素个数
public int getSize() {
return size;
}
// 返回链表是否为空
public boolean isEmpty() {
return size == 0;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Illegal index."
);
Node prev = dummyHead;
for (int i = 0; i < index; i++) prev = prev.next;
prev.next = new Node(e, prev.next);
size++;
}
// 在链表头添加新的元素e
public void addFirst(E e) {
add(0, e);
}
// 在链表末尾添加新的元素e
public void addLast(E e) {
add(size, e);
}
}
链表增删查改操作
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList() {
dummyHead = new Node();
size = 0;
}
// 获取链表中的元素个数
public int getSize() {
return size;
}
// 返回链表是否为空
public boolean isEmpty() {
return size == 0;
}
// 在链表的index(0-based)位置添加新的元素e
// 在链表中不是一个常用的操作,练习用:)
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Illegal index."
);
Node prev = dummyHead;
for (int i = 0; i < index; i++) prev = prev.next;
prev.next = new Node(e, prev.next);
size++;
}
// 在链表头添加新的元素e
public void addFirst(E e) {
add(0, e);
}
// 在链表末尾添加新的元素e
public void addLast(E e) {
add(size, e);
}
// 获得链表的第index(0-based)个位置的元素
// 在链表中不是一个常用的操作,练习用:)
public E get(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Get failed. Illegal index."
);
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) cur = cur.next;
return cur.e;
}
// 获得链表的第一个元素
public E getFirst() {
return get(0);
}
// 获得链表的最后一个元素
public E getLast() {
return get(size - 1);
}
// 修改链表的第index(0-based)个位置的元素为e
// 在链表中不是一个常用的操作,练习用:)
public void set(int index, E e) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Set failed. Illegal index."
);
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) cur = cur.next;
cur.e = e;
}
// 查找链表中是否有元素e
public boolean contains(E e) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.e.equals(e)) return true;
cur = cur.next;
}
return false;
}
// 从链表中删除index(0-based)位置的元素, 返回删除的元素
// 在链表中不是一个常用的操作,练习用:)
public E remove(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Remove failed. Index is illegal."
);
Node prev = dummyHead;
for (int i = 0; i < index; i++) prev = prev.next;
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size--;
return retNode.e;
}
// 从链表中删除第一个元素, 返回删除的元素
public E removeFirst() {
return remove(0);
}
// 从链表中删除最后一个元素, 返回删除的元素
public E removeLast() {
return remove(size - 1);
}
// 从链表中删除元素e
public void removeElement(E e) {
Node prev = dummyHead;
while (prev.next != null) {
if (prev.next.e.equals(e)) break;
prev = prev.next;
}
if (prev.next != null) {
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size--;
}
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
Node cur = dummyHead.next;
while (cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
使用递归实现链表
import javafx.util.Pair;
/// 递归实现的LinkedList
/// 类名称中LinkedListR里的R,是Recursion的意思,表示递归实现:)
public class LinkedListR<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
// 在链表的递归实现中,我们不使用虚拟头结点,也能无差异的处理位置0的问题:)
private Node head;
private int size;
public LinkedListR() {
head = null;
size = 0;
}
// 获取链表中的元素个数
public int getSize() {
return size;
}
// 返回链表是否为空
public boolean isEmpty() {
return size == 0;
}
// 在链表的index(0-based)位置添加新的元素e
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException(
"Add failed. Illegal index."
);
head = add(head, index, e);
size++;
}
// 在以node为头结点的链表的index位置插入元素e,递归算法
private Node add(Node node, int index, E e) {
if (index == 0) return new Node(e, node);
node.next = add(node.next, index - 1, e);
return node;
}
// 在链表头添加新的元素e
public void addFirst(E e) {
add(0, e);
}
// 在链表末尾添加新的元素e
public void addLast(E e) {
add(size, e);
}
// 获得链表的第index(0-based)个位置的元素
public E get(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Get failed. Illegal index."
);
return get(head, index);
}
// 在以node为头结点的链表中,找到第index个元素,递归算法
private E get(Node node, int index) {
if (index == 0) return node.e;
return get(node.next, index - 1);
}
// 获得链表的第一个元素
public E getFirst() {
return get(0);
}
// 获得链表的最后一个元素
public E getLast() {
return get(size - 1);
}
// 修改链表的第index(0-based)个位置的元素为e
public void set(int index, E e) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Update failed. Illegal index."
);
set(head, index, e);
}
// 修改以node为头结点的链表中,第index(0-based)个位置的元素为e,递归算法
private void set(Node node, int index, E e) {
if (index == 0) {
node.e = e;
return;
}
set(node.next, index - 1, e);
}
// 查找链表中是否有元素e
public boolean contains(E e) {
return contains(head, e);
}
// 在以node为头结点的链表中,查找是否存在元素e,递归算法
private boolean contains(Node node, E e) {
if (node == null) return false;
if (node.e.equals(e)) return true;
return contains(node.next, e);
}
// 从链表中删除index(0-based)位置的元素, 返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException(
"Remove failed. Index is illegal."
);
Pair<Node, E> res = remove(head, index);
size--;
head = res.getKey();
return res.getValue();
}
// 从以node为头结点的链表中,删除第index位置的元素,递归算法
// 返回值包含两个元素,删除后的链表头结点和删除的值:)
private Pair<Node, E> remove(Node node, int index) {
if (index == 0) return new Pair<>(node.next, node.e);
Pair<Node, E> res = remove(node.next, index - 1);
node.next = res.getKey();
return new Pair<>(node, res.getValue());
}
// 从链表中删除第一个元素, 返回删除的元素
public E removeFirst() {
return remove(0);
}
// 从链表中删除最后一个元素, 返回删除的元素
public E removeLast() {
return remove(size - 1);
}
// 从链表中删除元素e
public void removeElement(E e) {
head = removeElement(head, e);
}
// 从以node为头结点的链表中,删除元素e,递归算法
private Node removeElement(Node node, E e) {
if (node == null) return null;
node.next = removeElement(node.next, e);
if (node.e.equals(e)) {
size--;
return node.next;
}
return node;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
Node cur = head;
while (cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
public static void main(String[] args) {
LinkedListR<Integer> list = new LinkedListR<>();
for (int i = 0; i < 10; i++) list.addFirst(i);
while (!list.isEmpty()) System.out.println("removed " + list.removeLast());
}
}
栈
数组的子集
程序调用的系统栈
撤销
方法子过程调用
使用数组来实现栈
public class ArrayStack<E> implements Stack<E> {
private Array<E> array;
public ArrayStack(int capacity) {
array = new Array<>(capacity);
}
public ArrayStack() {
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack: ");
res.append('[');
for (int i = 0; i < array.getSize(); i++) {
res.append(array.get(i));
if (i != array.getSize() - 1) res.append(", ");
}
res.append("] top");
return res.toString();
}
}
队列
银行排队
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array;
public ArrayQueue(int capacity) {
array = new Array<>(capacity);
}
public ArrayQueue() {
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public void enqueue(E e) {
array.addLast(e);
}
@Override
public E dequeue() {
return array.removeFirst();
}
@Override
public E getFront() {
return array.getFirst();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Queue: ");
res.append("front [");
for (int i = 0; i < array.getSize(); i++) {
res.append(array.get(i));
if (i != array.getSize() - 1) res.append(", ");
}
res.append("] tail");
return res.toString();
}
public static void main(String[] args) {
ArrayQueue<Integer> queue = new ArrayQueue<>();
for (int i = 0; i < 10; i++) {
queue.enqueue(i);
System.out.println(queue);
if (i % 3 == 2) {
queue.dequeue();
System.out.println(queue);
}
}
}
}
循环队列
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size; // 有兴趣的同学,在完成这一章后,可以思考一下:
// LoopQueue中不声明size,如何完成所有的逻辑?
// 这个问题可能会比大家想象的要难一点点:)
public LoopQueue(int capacity) {
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
public int getCapacity() {
return data.length - 1;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public int getSize() {
return size;
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front) resize(getCapacity() * 2);
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
@Override
public E dequeue() {
if (isEmpty()) throw new IllegalArgumentException(
"Cannot dequeue from an empty queue."
);
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) resize(
getCapacity() / 2
);
return ret;
}
@Override
public E getFront() {
if (isEmpty()) throw new IllegalArgumentException("Queue is empty.");
return data[front];
}
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity + 1];
for (int i = 0; i < size; i++) newData[i] = data[(i + front) % data.length];
data = newData;
front = 0;
tail = size;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(
String.format("Queue: size = %d , capacity = %d\n", size, getCapacity())
);
res.append("front [");
for (int i = front; i != tail; i = (i + 1) % data.length) {
res.append(data[i]);
if ((i + 1) % data.length != tail) res.append(", ");
}
res.append("] tail");
return res.toString();
}
public static void main(String[] args) {
LoopQueue<Integer> queue = new LoopQueue<>();
for (int i = 0; i < 10; i++) {
queue.enqueue(i);
System.out.println(queue);
if (i % 3 == 2) {
queue.dequeue();
System.out.println(queue);
}
}
}
}
双端队列
package com.xingyuezhiyun.practice.code;
public class Deque<E> {
private E[] data;
private int front, tail;
private int size;
public Deque(int capacity) {
data = (E[]) new Object[capacity];
front = 0;
tail = 0;
size = 0;
}
public Deque() {
this(10);
}
public int getCapacity() {
return data.length;
}
public boolean isEmpty() {
return size == 0;
}
public int getSize() {
return size;
}
public void addLast(E e) {
if (size == getCapacity()) resize(getCapacity() * 2);
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
public void addFront(E e) {
if (size == getCapacity()) resize(getCapacity() * 2);
front = front == 0 ? data.length - 1 : front - 1;
data[front] = e;
size++;
}
public E removeFront() {
if (isEmpty()) throw new IllegalArgumentException(
"Cannot dequeue from an empty queue."
);
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
if (getSize() == getCapacity() / 4 && getCapacity() / 2 != 0) resize(
getCapacity() / 2
);
return ret;
}
public E removeLast() {
if (isEmpty()) throw new IllegalArgumentException(
"Cannot dequeue from an empty queue"
);
tail = tail == 0 ? data.length - 1 : tail - 1;
E ret = data[tail];
data[tail] = null;
size--;
if (getSize() == getCapacity() / 4 && getCapacity() / 2 != 0) resize(
getCapacity() / 2
);
return ret;
}
public E getFront() {
if (isEmpty()) throw new IllegalArgumentException("Queue is empty");
return data[front];
}
public E getLast() {
if (isEmpty()) throw new IllegalArgumentException("Queue is empty");
int index = tail == 0 ? data.length - 1 : tail - 1;
return data[index];
}
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) newData[i] = data[(i + front) % data.length];
data = newData;
front = 0;
tail = size;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(
String.format(
"Queue: size = %d, capacity = %d\n",
getSize(),
getCapacity()
)
);
res.append("front [");
for (int i = 0; i < size; i++) {
res.append(data[(i + front) % data.length]);
if (i != size - 1) res.append(",");
}
res.append("] tail ");
return res.toString();
}
public static void main(String[] args) {
Deque<Integer> dq = new Deque<>();
for (int i = 0; i < 16; i++) {
if (i % 2 == 0) dq.addLast(i); else dq.addFront(i);
System.out.println(dq);
}
System.out.println();
for (int i = 0; !dq.isEmpty(); i++) {
if (i % 2 == 0) dq.removeFront(); else dq.removeLast();
System.out.println(dq);
}
}
}
哈希表
思想: 空间换时间
哈希表是时间和空间之间的平衡
大整数: 简单的解决办法: 模一个素数
http://planetmath.org/goodhashtableprimes
字符串: 转换成整型处理
hash(code) = (c * B^3 + o * B^2 + d * B^1 + e * B^0) % M
hash(code) = ((((c * B) + o) * B + d) * B + e) % M
hash(code) = ((((c % M) * B + o) % M * B + d) % M * B + e) % M
哈希冲突的处理
seperate chaining
java8 之前是链表,java8 之后,当哈希冲突达到一定程度,会使用 treemap(红黑树)
import java.util.TreeMap;
public class HashTable<K extends Comparable<K>, V> {
private final int[] capacity = {
53,
97,
193,
389,
769,
1543,
3079,
6151,
12289,
24593,
49157,
98317,
196613,
393241,
786433,
1572869,
3145739,
6291469,
12582917,
25165843,
50331653,
100663319,
201326611,
402653189,
805306457,
1610612741,
};
private static final int upperTol = 10;
private static final int lowerTol = 2;
private int capacityIndex = 0;
private TreeMap<K, V>[] hashtable;
private int size;
private int M;
public HashTable() {
this.M = capacity[capacityIndex];
size = 0;
hashtable = new TreeMap[M];
for (int i = 0; i < M; i++) hashtable[i] = new TreeMap<>();
}
private int hash(K key) {
return (key.hashCode() & 0x7fffffff) % M;
}
public int getSize() {
return size;
}
public void add(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];
if (map.containsKey(key)) map.put(key, value); else {
map.put(key, value);
size++;
if (size >= upperTol * M && capacityIndex + 1 < capacity.length) {
capacityIndex++;
resize(capacity[capacityIndex]);
}
}
}
public V remove(K key) {
V ret = null;
TreeMap<K, V> map = hashtable[hash(key)];
if (map.containsKey(key)) {
ret = map.remove(key);
size--;
if (size < lowerTol * M && capacityIndex - 1 >= 0) {
capacityIndex--;
resize(capacity[capacityIndex]);
}
}
return ret;
}
public void set(K key, V value) {
TreeMap<K, V> map = hashtable[hash(key)];
if (!map.containsKey(key)) throw new IllegalArgumentException(
key + " doesn't exist!"
);
map.put(key, value);
}
public boolean contains(K key) {
return hashtable[hash(key)].containsKey(key);
}
public V get(K key) {
return hashtable[hash(key)].get(key);
}
private void resize(int newM) {
TreeMap<K, V>[] newHashTable = new TreeMap[newM];
for (int i = 0; i < newM; i++) newHashTable[i] = new TreeMap<>();
int oldM = M;
this.M = newM;
for (int i = 0; i < oldM; i++) {
TreeMap<K, V> map = hashtable[i];
for (K key : map.keySet()) newHashTable[hash(key)].put(key, map.get(key));
}
this.hashtable = newHashTable;
}
}
查找算法
线性查找
public class LinearSearch {
private LinearSearch() {}
public static int search(int[] data, int target) {
for (int i = 0; i < data.length; i++) if (data[i] == target) return i;
return -1;
}
public static void main(String[] args) {
int[] data = { 24, 18, 12, 9, 16, 66, 32, 4 };
int res = LinearSearch.search(data, 16);
System.out.println(res);
int res2 = LinearSearch.search(data, 666);
System.out.println(res2);
}
}
使用泛型
public class LinearSearch {
private LinearSearch() {}
public static <E> int search(E[] data, E target) {
for (int i = 0; i < data.length; i++) if (data[i].equals(target)) return i;
return -1;
}
public static void main(String[] args) {
Integer[] data = { 24, 18, 12, 9, 16, 66, 32, 4 };
int res = LinearSearch.search(data, 16);
System.out.println(res);
int res2 = LinearSearch.search(data, 666);
System.out.println(res2);
}
}
自定义类重写 equals
public class LinearSearch {
private LinearSearch() {}
public static <E> int search(E[] data, E target) {
for (int i = 0; i < data.length; i++) if (data[i].equals(target)) return i;
return -1;
}
public static void main(String[] args) {
Integer[] data = { 24, 18, 12, 9, 16, 66, 32, 4 };
int res = LinearSearch.search(data, 16);
System.out.println(res);
int res2 = LinearSearch.search(data, 666);
System.out.println(res2);
Student[] students = {
new Student("Alice"),
new Student("Bobo"),
new Student("Charles"),
};
Student bobo = new Student("Bobo");
int res3 = LinearSearch.search(students, bobo);
System.out.println(res3);
}
}
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public boolean equals(Object student) {
if (this == student) return true;
if (student == null) return false;
if (this.getClass() != student.getClass()) return false;
Student another = (Student) student;
return this.name.equals(another.name);
}
}
循环不变量
循环条件限制
算法复杂度分析
表示算法的性能,算法运行的上界
O(1) < O(logn) < O(√n) < O(n) < O(nlogn) < O(n²) < O(2^n) < O(n!)
二分查找
递归实现
public class BinarySearch {
private BinarySearch() {}
public static <E extends Comparable<E>> int search(E[] data, E target) {
return search(data, 0, data.length - 1, target);
}
private static <E extends Comparable<E>> int search(
E[] data,
int l,
int r,
E target
) {
if (l > r) return -1;
int mid = l + (r - l) / 2;
if (data[mid].compareTo(target) == 0) return mid;
if (data[mid].compareTo(target) < 0) return search(
data,
mid + 1,
r,
target
);
return search(data, l, mid - 1, target);
}
}
非递归实现
public static <E extends Comparable<E>> int search(E[] data, E target){
int l = 0, r = data.length - 1;
// 在 data[l, r] 的范围中查找 target
while(l <= r){
int mid = l + (r - l) / 2;
if(data[mid].compareTo(target) == 0)
return mid;
if(data[mid].compareTo(target) < 0)
l = mid + 1;
else
r = mid - 1;
}
return -1;
}
经典树
二分搜索树
二分搜索树是二叉树
二分搜索树的每个节点的值
大于其左子树的所有节点的值
小于其右子树的所有节点的值
每一棵子树也是二分搜索树
二分搜索树实现
插入基本实现
public class BST<E extends Comparable<E>> {
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
private Node root;
private int size;
public BST() {
root = null;
size = 0;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 向二分搜索树中添加新的元素e
public void add(E e) {
if (root == null) {
root = new Node(e);
size++;
} else add(root, e);
}
// 向以node为根的二分搜索树中插入元素e,递归算法
private void add(Node node, E e) {
if (e.equals(node.e)) return; else if (
e.compareTo(node.e) < 0 && node.left == null
) {
node.left = new Node(e);
size++;
return;
} else if (e.compareTo(node.e) > 0 && node.right == null) {
node.right = new Node(e);
size++;
return;
}
if (e.compareTo(node.e) < 0) add(node.left, e); else add(node.right, e); //e.compareTo(node.e) > 0
}
}
递归改进
public class BST<E extends Comparable<E>> {
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
private Node root;
private int size;
public BST() {
root = null;
size = 0;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 向二分搜索树中添加新的元素e
public void add(E e) {
root = add(root, e);
}
// 向以node为根的二分搜索树中插入元素e,递归算法
// 返回插入新节点后二分搜索树的根
private Node add(Node node, E e) {
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0) node.left = add(node.left, e); else if (
e.compareTo(node.e) > 0
) node.right = add(node.right, e);
return node;
}
}
是否包含(递归实现)
// 看以node为根的二分搜索树中是否包含元素e, 递归算法
private boolean contains(Node node, E e){
if(node == null)
return false;
if(e.compareTo(node.e) == 0)
return true;
else if(e.compareTo(node.e) < 0)
return contains(node.left, e);
else // e.compareTo(node.e) > 0
return contains(node.right, e);
}
前序遍历
中序遍历
二分搜索树的中序遍历结果是顺序的
后序遍历
import java.util.Stack;
public class BST<E extends Comparable<E>> {
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
private Node root;
private int size;
public BST() {
root = null;
size = 0;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 向二分搜索树中添加新的元素e
public void add(E e) {
root = add(root, e);
}
// 向以node为根的二分搜索树中插入元素e,递归算法
// 返回插入新节点后二分搜索树的根
private Node add(Node node, E e) {
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0) node.left = add(node.left, e); else if (
e.compareTo(node.e) > 0
) node.right = add(node.right, e);
return node;
}
// 看二分搜索树中是否包含元素e
public boolean contains(E e) {
return contains(root, e);
}
// 看以node为根的二分搜索树中是否包含元素e, 递归算法
private boolean contains(Node node, E e) {
if (node == null) return false;
if (e.compareTo(node.e) == 0) return true; else if (
e.compareTo(node.e) < 0
) return contains(node.left, e); else return contains(node.right, e); // e.compareTo(node.e) > 0
}
// 二分搜索树的前序遍历
public void preOrder() {
preOrder(root);
}
// 前序遍历以node为根的二分搜索树, 递归算法
private void preOrder(Node node) {
if (node == null) return;
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
// 二分搜索树的中序遍历
public void inOrder() {
inOrder(root);
}
// 中序遍历以node为根的二分搜索树, 递归算法
private void inOrder(Node node) {
if (node == null) return;
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
// 二分搜索树的后序遍历
public void postOrder() {
postOrder(root);
}
// 后序遍历以node为根的二分搜索树, 递归算法
private void postOrder(Node node) {
if (node == null) return;
postOrder(node.left);
postOrder(node.right);
System.out.println(node.e);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
generateString(root, 0, res);
return res.toString();
}
// 生成以node为根节点,深度为depth的描述二叉树的字符串
private void generateString(Node node, int depth, StringBuilder res) {
if (node == null) {
res.append(generateDepthString(depth) + "null\n");
return;
}
res.append(generateDepthString(depth) + node.e + "\n");
generateString(node.left, depth + 1, res);
generateString(node.right, depth + 1, res);
}
private String generateDepthString(int depth) {
StringBuilder res = new StringBuilder();
for (int i = 0; i < depth; i++) res.append("--");
return res.toString();
}
}
二分搜索树前序遍历的非递归写法
// 二分搜索树的非递归前序遍历
public void preOrderNR(){
if(root == null)
return;
Stack<Node> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
Node cur = stack.pop();
System.out.println(cur.e);
if(cur.right != null)
stack.push(cur.right);
if(cur.left != null)
stack.push(cur.left);
}
}
二分搜索树的层序遍历
// 二分搜索树的层序遍历
public void levelOrder(){
if(root == null)
return;
Queue<Node> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
Node cur = q.remove();
System.out.println(cur.e);
if(cur.left != null)
q.add(cur.left);
if(cur.right != null)
q.add(cur.right);
}
}
删除任意节点
// 删除掉以node为根的二分搜索树中值为e的节点, 递归算法
// 返回删除节点后新的二分搜索树的根
private Node remove(Node node, E e){
if( node == null )
return null;
if( e.compareTo(node.e) < 0 ){
node.left = remove(node.left , e);
return node;
}
else if(e.compareTo(node.e) > 0 ){
node.right = remove(node.right, e);
return node;
}
else{ // e.compareTo(node.e) == 0
// 待删除节点左子树为空的情况
if(node.left == null){
Node rightNode = node.right;
node.right = null;
size --;
return rightNode;
}
// 待删除节点右子树为空的情况
if(node.right == null){
Node leftNode = node.left;
node.left = null;
size --;
return leftNode;
}
// 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor = minimum(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
return successor;
}
}
堆
堆是一种特殊的树形数据结构,它满足堆积的性质:即每个节点都大于或等于(最大堆)或小于或等于(最小堆)其子节点
二叉堆的性质
最大堆
完全二叉树(非满叉树)
堆中某个节点的值总是不大于其父节点的值
用数组存储二叉堆
添加元素(sift up) 和 取出最大元素(sift down)
public class MaxHeap<E extends Comparable<E>> {
private Array<E> data;
public MaxHeap(int capacity) {
data = new Array<>(capacity);
}
public MaxHeap() {
data = new Array<>();
}
// 返回堆中的元素个数
public int size() {
return data.getSize();
}
// 返回一个布尔值, 表示堆中是否为空
public boolean isEmpty() {
return data.isEmpty();
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
private int parent(int index) {
if (index == 0) throw new IllegalArgumentException(
"index-0 doesn't have parent."
);
return (index - 1) / 2;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
private int leftChild(int index) {
return index * 2 + 1;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
private int rightChild(int index) {
return index * 2 + 2;
}
// 向堆中添加元素
public void add(E e) {
data.addLast(e);
siftUp(data.getSize() - 1);
}
private void siftUp(int k) {
while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {
data.swap(k, parent(k));
k = parent(k);
}
}
// 看堆中的最大元素
public E findMax() {
if (data.getSize() == 0) throw new IllegalArgumentException(
"Can not findMax when heap is empty."
);
return data.get(0);
}
// 取出堆中最大元素
public E extractMax() {
E ret = findMax();
data.swap(0, data.getSize() - 1);
data.removeLast();
siftDown(0);
return ret;
}
private void siftDown(int k) {
while (leftChild(k) < data.getSize()) {
int j = leftChild(k); // 在此轮循环中,data[k]和data[j]交换位置
if (
j + 1 < data.getSize() && data.get(j + 1).compareTo(data.get(j)) > 0
) j++;
// data[j] 是 leftChild 和 rightChild 中的最大值
if (data.get(k).compareTo(data.get(j)) >= 0) break;
data.swap(k, j);
k = j;
}
}
}
replace 和 heapify
replace: 取出最大元素后,放入一个新元素可以直接将推顶元素替换以后 sift down(O(logn))
// 取出堆中的最大元素,并且替换成元素e
public E replace(E e){
E ret = findMax();
data.set(0, e);
siftDown(0);
return ret;
}
heapify: 将任意数组整理成堆的形状, 叶子节点索引往上进行下浮操作
public MaxHeap(E[] arr){
data = new Array<>(arr);
if(arr.length != 1){
for(int i = parent(arr.length - 1) ; i >= 0 ; i --)
siftDown(i);
}
}
堆排序(原地排序)
import java.util.Arrays;
public class HeapSort {
private HeapSort() {}
public static <E extends Comparable<E>> void sort(E[] data) {
MaxHeap<E> maxHeap = new MaxHeap<>();
for (E e : data) maxHeap.add(e);
for (int i = data.length - 1; i >= 0; i--) data[i] = maxHeap.extractMax();
}
public static <E extends Comparable<E>> void sort2(E[] data) {
if (data.length <= 1) return;
for (int i = (data.length - 2) / 2; i >= 0; i--) siftDown(
data,
i,
data.length
);
for (int i = data.length - 1; i >= 0; i--) {
swap(data, 0, i);
siftDown(data, 0, i);
}
}
// 对 data[0, n) 所形成的最大堆中,索引 k 的元素,执行 siftDown
private static <E extends Comparable<E>> void siftDown(
E[] data,
int k,
int n
) {
while (2 * k + 1 < n) {
int j = 2 * k + 1; // 在此轮循环中,data[k]和data[j]交换位置
if (j + 1 < n && data[j + 1].compareTo(data[j]) > 0) j++;
// data[j] 是 leftChild 和 rightChild 中的最大值
if (data[k].compareTo(data[j]) >= 0) break;
swap(data, k, j);
k = j;
}
}
private static <E> void swap(E[] arr, int i, int j) {
E t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
public static void main(String[] args) {
int n = 1000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
Integer[] arr3 = Arrays.copyOf(arr, arr.length);
Integer[] arr4 = Arrays.copyOf(arr, arr.length);
Integer[] arr5 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("MergeSort", arr);
SortingHelper.sortTest("QuickSort2Ways", arr2);
SortingHelper.sortTest("QuickSort3Ways", arr3);
SortingHelper.sortTest("HeapSort", arr4);
SortingHelper.sortTest("HeapSort2", arr5);
}
}
时间复杂的为 O(nlogn)
使用最大堆实现优先队列
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
private MaxHeap<E> maxHeap;
public PriorityQueue() {
maxHeap = new MaxHeap<>();
}
@Override
public int getSize() {
return maxHeap.size();
}
@Override
public boolean isEmpty() {
return maxHeap.isEmpty();
}
@Override
public E getFront() {
return maxHeap.findMax();
}
@Override
public void enqueue(E e) {
maxHeap.add(e);
}
@Override
public E dequeue() {
return maxHeap.extractMax();
}
}
avl
自平衡二叉树
对于任意一个节点,左子树和右子树的高度差不能超过 1
平衡因子,左子树高度减去右子树的高度
import java.util.ArrayList;
public class AVLTree<K extends Comparable<K>, V> {
private class Node {
public K key;
public V value;
public Node left, right;
public int height;
public Node(K key, V value) {
this.key = key;
this.value = value;
left = null;
right = null;
height = 1;
}
}
private Node root;
private int size;
public AVLTree() {
root = null;
size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 判断该二叉树是否是一棵二分搜索树
public boolean isBST() {
ArrayList<K> keys = new ArrayList<>();
inOrder(root, keys);
for (int i = 1; i < keys.size(); i++) if (
keys.get(i - 1).compareTo(keys.get(i)) > 0
) return false;
return true;
}
private void inOrder(Node node, ArrayList<K> keys) {
if (node == null) return;
inOrder(node.left, keys);
keys.add(node.key);
inOrder(node.right, keys);
}
// 判断该二叉树是否是一棵平衡二叉树
public boolean isBalanced() {
return isBalanced(root);
}
// 判断以Node为根的二叉树是否是一棵平衡二叉树,递归算法
private boolean isBalanced(Node node) {
if (node == null) return true;
int balanceFactor = getBalanceFactor(node);
if (Math.abs(balanceFactor) > 1) return false;
return isBalanced(node.left) && isBalanced(node.right);
}
// 获得节点node的高度
private int getHeight(Node node) {
if (node == null) return 0;
return node.height;
}
// 获得节点node的平衡因子
private int getBalanceFactor(Node node) {
if (node == null) return 0;
return getHeight(node.left) - getHeight(node.right);
}
// 对节点y进行向右旋转操作,返回旋转后新的根节点x
// y x
// / \ / \
// x T4 向右旋转 (y) z y
// / \ - - - - - - - -> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
private Node rightRotate(Node y) {
Node x = y.left;
Node T3 = x.right;
// 向右旋转过程
x.right = y;
y.left = T3;
// 更新height
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
// 对节点y进行向左旋转操作,返回旋转后新的根节点x
// y x
// / \ / \
// T1 x 向左旋转 (y) y z
// / \ - - - - - - - -> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
private Node leftRotate(Node y) {
Node x = y.right;
Node T2 = x.left;
// 向左旋转过程
x.left = y;
y.right = T2;
// 更新height
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
// 向二分搜索树中添加新的元素(key, value)
public void add(K key, V value) {
root = add(root, key, value);
}
// 向以node为根的二分搜索树中插入元素(key, value),递归算法
// 返回插入新节点后二分搜索树的根
private Node add(Node node, K key, V value) {
if (node == null) {
size++;
return new Node(key, value);
}
if (key.compareTo(node.key) < 0) node.left =
add(node.left, key, value); else if (
key.compareTo(node.key) > 0
) node.right = add(node.right, key, value); else node.value = value; // key.compareTo(node.key) == 0
// 更新height
node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
// 计算平衡因子
int balanceFactor = getBalanceFactor(node);
// 平衡维护
// LL
if (
balanceFactor > 1 && getBalanceFactor(node.left) >= 0
) return rightRotate(node);
// RR
if (
balanceFactor < -1 && getBalanceFactor(node.right) <= 0
) return leftRotate(node);
// LR
if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// RL
if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}
// 返回以node为根节点的二分搜索树中,key所在的节点
private Node getNode(Node node, K key) {
if (node == null) return null;
if (key.equals(node.key)) return node; else if (
key.compareTo(node.key) < 0
) return getNode(node.left, key); else return getNode(node.right, key); // if(key.compareTo(node.key) > 0)
}
public boolean contains(K key) {
return getNode(root, key) != null;
}
public V get(K key) {
Node node = getNode(root, key);
return node == null ? null : node.value;
}
public void set(K key, V newValue) {
Node node = getNode(root, key);
if (node == null) throw new IllegalArgumentException(
key + " doesn't exist!"
);
node.value = newValue;
}
// 返回以node为根的二分搜索树的最小值所在的节点
private Node minimum(Node node) {
if (node.left == null) return node;
return minimum(node.left);
}
// 从二分搜索树中删除键为key的节点
public V remove(K key) {
Node node = getNode(root, key);
if (node != null) {
root = remove(root, key);
return node.value;
}
return null;
}
private Node remove(Node node, K key) {
if (node == null) return null;
Node retNode;
if (key.compareTo(node.key) < 0) {
node.left = remove(node.left, key);
// return node;
retNode = node;
} else if (key.compareTo(node.key) > 0) {
node.right = remove(node.right, key);
// return node;
retNode = node;
} else { // key.compareTo(node.key) == 0
// 待删除节点左子树为空的情况
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
// return rightNode;
retNode = rightNode;
}
// 待删除节点右子树为空的情况
else if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
// return leftNode;
retNode = leftNode;
}
// 待删除节点左右子树均不为空的情况
else {
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor = minimum(node.right);
//successor.right = removeMin(node.right);
successor.right = remove(node.right, successor.key);
successor.left = node.left;
node.left = node.right = null;
// return successor;
retNode = successor;
}
}
if (retNode == null) return null;
// 更新height
retNode.height =
1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
// 计算平衡因子
int balanceFactor = getBalanceFactor(retNode);
// 平衡维护
// LL
if (
balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0
) return rightRotate(retNode);
// RR
if (
balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0
) return leftRotate(retNode);
// LR
if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
retNode.left = leftRotate(retNode.left);
return rightRotate(retNode);
}
// RL
if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
retNode.right = rightRotate(retNode.right);
return leftRotate(retNode);
}
return retNode;
}
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words = new ArrayList<>();
if (FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
AVLTree<String, Integer> map = new AVLTree<>();
for (String word : words) {
if (map.contains(word)) map.set(word, map.get(word) + 1); else map.add(
word,
1
);
}
System.out.println("Total different words: " + map.getSize());
System.out.println("Frequency of PRIDE: " + map.get("pride"));
System.out.println("Frequency of PREJUDICE: " + map.get("prejudice"));
System.out.println("is BST : " + map.isBST());
System.out.println("is Balanced : " + map.isBalanced());
for (String word : words) {
map.remove(word);
if (!map.isBST() || !map.isBalanced()) throw new RuntimeException();
}
}
System.out.println();
}
}
红黑树
每个节点或者是红色的,或者是黑色的
根节点是黑色的
每一个叶子节点(最后的空节点)是黑色的
如果一个节点是红色的,那么他的孩子节点都是黑色的
从任意一个节点到叶子节点,经过的黑色节点是一样的
2-3 树是一颗绝对平衡的树(不断往上融合)
红黑树相对于 2-3 树来说,所有的红色节点都是左倾斜的
红黑树不是平衡二叉树
总结
对于完全随机的数据,普通的二分搜索树很好用(对于顺序的数据会退化成链表)
对于查询较多的使用情况,avl 树很好用
红黑树牺牲了平衡性,统计性能更优(综合增删改查的操作)
另一种统计性能优秀的树结构(Splay Tree)
红黑树实现(左倾红黑树)
import java.util.ArrayList;
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private class Node {
public K key;
public V value;
public Node left, right;
public boolean color;
public Node(K key, V value) {
this.key = key;
this.value = value;
left = null;
right = null;
color = RED;
}
}
private Node root;
private int size;
public RBTree() {
root = null;
size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 判断节点node的颜色
private boolean isRed(Node node) {
if (node == null) return BLACK;
return node.color;
}
// node x
// / \ 左旋转 / \
// T1 x ---------> node T3
// / \ / \
// T2 T3 T1 T2
private Node leftRotate(Node node) {
Node x = node.right;
// 左旋转
node.right = x.left;
x.left = node;
x.color = node.color;
node.color = RED;
return x;
}
// node x
// / \ 右旋转 / \
// x T2 -------> y node
// / \ / \
// y T1 T1 T2
private Node rightRotate(Node node) {
Node x = node.left;
// 右旋转
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = RED;
return x;
}
// 颜色翻转
private void flipColors(Node node) {
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
// 向红黑树中添加新的元素(key, value)
public void add(K key, V value) {
root = add(root, key, value);
root.color = BLACK; // 最终根节点为黑色节点
}
// 向以node为根的红黑树中插入元素(key, value),递归算法
// 返回插入新节点后红黑树的根
private Node add(Node node, K key, V value) {
if (node == null) {
size++;
return new Node(key, value); // 默认插入红色节点
}
if (key.compareTo(node.key) < 0) node.left =
add(node.left, key, value); else if (
key.compareTo(node.key) > 0
) node.right = add(node.right, key, value); else node.value = value; // key.compareTo(node.key) == 0
if (isRed(node.right) && !isRed(node.left)) node = leftRotate(node);
if (isRed(node.left) && isRed(node.left.left)) node = rightRotate(node);
if (isRed(node.left) && isRed(node.right)) flipColors(node);
return node;
}
// 返回以node为根节点的二分搜索树中,key所在的节点
private Node getNode(Node node, K key) {
if (node == null) return null;
if (key.equals(node.key)) return node; else if (
key.compareTo(node.key) < 0
) return getNode(node.left, key); else return getNode(node.right, key); // if(key.compareTo(node.key) > 0)
}
public boolean contains(K key) {
return getNode(root, key) != null;
}
public V get(K key) {
Node node = getNode(root, key);
return node == null ? null : node.value;
}
public void set(K key, V newValue) {
Node node = getNode(root, key);
if (node == null) throw new IllegalArgumentException(
key + " doesn't exist!"
);
node.value = newValue;
}
// 返回以node为根的二分搜索树的最小值所在的节点
private Node minimum(Node node) {
if (node.left == null) return node;
return minimum(node.left);
}
// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
private Node removeMin(Node node) {
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
// 从二分搜索树中删除键为key的节点
public V remove(K key) {
Node node = getNode(root, key);
if (node != null) {
root = remove(root, key);
return node.value;
}
return null;
}
private Node remove(Node node, K key) {
if (node == null) return null;
if (key.compareTo(node.key) < 0) {
node.left = remove(node.left, key);
return node;
} else if (key.compareTo(node.key) > 0) {
node.right = remove(node.right, key);
return node;
} else { // key.compareTo(node.key) == 0
// 待删除节点左子树为空的情况
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
// 待删除节点右子树为空的情况
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
// 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor = minimum(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
return successor;
}
}
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words = new ArrayList<>();
if (FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
RBTree<String, Integer> map = new RBTree<>();
for (String word : words) {
if (map.contains(word)) map.set(word, map.get(word) + 1); else map.add(
word,
1
);
}
System.out.println("Total different words: " + map.getSize());
System.out.println("Frequency of PRIDE: " + map.get("pride"));
System.out.println("Frequency of PREJUDICE: " + map.get("prejudice"));
}
System.out.println();
}
}
b 类树
高级数据结构
集合和映射
set
基于二分搜索树的实现
public class BSTSet<E extends Comparable<E>> implements Set<E> {
private BST<E> bst;
public BSTSet() {
bst = new BST<>();
}
@Override
public int getSize() {
return bst.size();
}
@Override
public boolean isEmpty() {
return bst.isEmpty();
}
@Override
public void add(E e) {
bst.add(e);
}
@Override
public boolean contains(E e) {
return bst.contains(e);
}
@Override
public void remove(E e) {
bst.remove(e);
}
}
时间复杂度是 O(logn), 最差情况退化成 O(n)
基于链表的集合实现
import java.util.ArrayList;
public class LinkedListSet<E> implements Set<E> {
private LinkedList<E> list;
public LinkedListSet() {
list = new LinkedList<>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void add(E e) {
if (!list.contains(e)) list.addFirst(e);
}
@Override
public boolean contains(E e) {
return list.contains(e);
}
@Override
public void remove(E e) {
list.removeElement(e);
}
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words1 = new ArrayList<>();
if (FileOperation.readFile("pride-and-prejudice.txt", words1)) {
System.out.println("Total words: " + words1.size());
LinkedListSet<String> set1 = new LinkedListSet<>();
for (String word : words1) set1.add(word);
System.out.println("Total different words: " + set1.getSize());
}
System.out.println();
System.out.println("A Tale of Two Cities");
ArrayList<String> words2 = new ArrayList<>();
if (FileOperation.readFile("a-tale-of-two-cities.txt", words2)) {
System.out.println("Total words: " + words2.size());
LinkedListSet<String> set2 = new LinkedListSet<>();
for (String word : words2) set2.add(word);
System.out.println("Total different words: " + set2.getSize());
}
}
}
有序集合中的元素具有顺序性(基于搜索树的实现)
无序集合中的元素具有顺序性(基于哈希表的实现)
map
基于链表的实现
import java.util.ArrayList;
public class LinkedListMap<K, V> implements Map<K, V> {
private class Node {
public K key;
public V value;
public Node next;
public Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
public Node(K key, V value) {
this(key, value, null);
}
public Node() {
this(null, null, null);
}
@Override
public String toString() {
return key.toString() + " : " + value.toString();
}
}
private Node dummyHead;
private int size;
public LinkedListMap() {
dummyHead = new Node();
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
private Node getNode(K key) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.key.equals(key)) return cur;
cur = cur.next;
}
return null;
}
@Override
public boolean contains(K key) {
return getNode(key) != null;
}
@Override
public V get(K key) {
Node node = getNode(key);
return node == null ? null : node.value;
}
@Override
public void add(K key, V value) {
Node node = getNode(key);
if (node == null) {
dummyHead.next = new Node(key, value, dummyHead.next);
size++;
} else node.value = value;
}
@Override
public void set(K key, V newValue) {
Node node = getNode(key);
if (node == null) throw new IllegalArgumentException(
key + " doesn't exist!"
);
node.value = newValue;
}
@Override
public V remove(K key) {
Node prev = dummyHead;
while (prev.next != null) {
if (prev.next.key.equals(key)) break;
prev = prev.next;
}
if (prev.next != null) {
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size--;
return delNode.value;
}
return null;
}
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words = new ArrayList<>();
if (FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
LinkedListMap<String, Integer> map = new LinkedListMap<>();
for (String word : words) {
if (map.contains(word)) map.set(word, map.get(word) + 1); else map.add(
word,
1
);
}
System.out.println("Total different words: " + map.getSize());
System.out.println("Frequency of PRIDE: " + map.get("pride"));
System.out.println("Frequency of PREJUDICE: " + map.get("prejudice"));
}
System.out.println();
}
}
基于二分搜索树的实现
import java.util.ArrayList;
public class BSTMap<K extends Comparable<K>, V> implements Map<K, V> {
private class Node {
public K key;
public V value;
public Node left, right;
public Node(K key, V value) {
this.key = key;
this.value = value;
left = null;
right = null;
}
}
private Node root;
private int size;
public BSTMap() {
root = null;
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
// 向二分搜索树中添加新的元素(key, value)
@Override
public void add(K key, V value) {
root = add(root, key, value);
}
// 向以node为根的二分搜索树中插入元素(key, value),递归算法
// 返回插入新节点后二分搜索树的根
private Node add(Node node, K key, V value) {
if (node == null) {
size++;
return new Node(key, value);
}
if (key.compareTo(node.key) < 0) node.left =
add(node.left, key, value); else if (
key.compareTo(node.key) > 0
) node.right = add(node.right, key, value); else node.value = value; // key.compareTo(node.key) == 0
return node;
}
// 返回以node为根节点的二分搜索树中,key所在的节点
private Node getNode(Node node, K key) {
if (node == null) return null;
if (key.equals(node.key)) return node; else if (
key.compareTo(node.key) < 0
) return getNode(node.left, key); else return getNode(node.right, key); // if(key.compareTo(node.key) > 0)
}
@Override
public boolean contains(K key) {
return getNode(root, key) != null;
}
@Override
public V get(K key) {
Node node = getNode(root, key);
return node == null ? null : node.value;
}
@Override
public void set(K key, V newValue) {
Node node = getNode(root, key);
if (node == null) throw new IllegalArgumentException(
key + " doesn't exist!"
);
node.value = newValue;
}
// 返回以node为根的二分搜索树的最小值所在的节点
private Node minimum(Node node) {
if (node.left == null) return node;
return minimum(node.left);
}
// 删除掉以node为根的二分搜索树中的最小节点
// 返回删除节点后新的二分搜索树的根
private Node removeMin(Node node) {
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
// 从二分搜索树中删除键为key的节点
@Override
public V remove(K key) {
Node node = getNode(root, key);
if (node != null) {
root = remove(root, key);
return node.value;
}
return null;
}
private Node remove(Node node, K key) {
if (node == null) return null;
if (key.compareTo(node.key) < 0) {
node.left = remove(node.left, key);
return node;
} else if (key.compareTo(node.key) > 0) {
node.right = remove(node.right, key);
return node;
} else { // key.compareTo(node.key) == 0
// 待删除节点左子树为空的情况
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
// 待删除节点右子树为空的情况
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
// 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor = minimum(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
return successor;
}
}
public static void main(String[] args) {
System.out.println("Pride and Prejudice");
ArrayList<String> words = new ArrayList<>();
if (FileOperation.readFile("pride-and-prejudice.txt", words)) {
System.out.println("Total words: " + words.size());
BSTMap<String, Integer> map = new BSTMap<>();
for (String word : words) {
if (map.contains(word)) map.set(word, map.get(word) + 1); else map.add(
word,
1
);
}
System.out.println("Total different words: " + map.getSize());
System.out.println("Frequency of PRIDE: " + map.get("pride"));
System.out.println("Frequency of PREJUDICE: " + map.get("prejudice"));
}
System.out.println();
}
}
有序映射中的键具有顺序性(基于搜索树的实现)
无序映射中的键没有顺序性(基于哈希表的实现)
排序算法总结
选择排序法 O(n^2) O(1)
插入排序法 O(n^2) O(1) 完全有序数组 O(n)
冒泡排序法 O(n^2) O(1) 完全有序数组 O(n)
归并排序法 O(nlogn) O(n) 完全有序数组 O(n)
快速排序法 O(nlogn) O(1) 含有相同元素所属组,三路快排时间为 O(n)
堆排序法 O(nlogn) O(1)
希尔排序法 O(nlogn)-O(n^2) O(1)
插入排序和冒泡排序,归并排序是稳定的算法
选择排序,快速排序,堆排序和希尔排序是不稳定的算法
如果元素只有一个域,稳定性没有意义
如果是一个对象,那么要注意是用比较方法里面判断排序依据还是在算法里面实现
线段树
线段树不是完全二叉树,是平衡二叉树
用数组或链表实现,树的节点是一段数据
可以扩展到三维线段树或多维线段树
public interface Merger<E> {
E merge(E a, E b);
}
public class SegmentTree<E> {
private E[] tree;
private E[] data;
private Merger<E> merger;
public SegmentTree(E[] arr, Merger<E> merger) {
this.merger = merger;
data = (E[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++) data[i] = arr[i];
tree = (E[]) new Object[4 * arr.length];
buildSegmentTree(0, 0, arr.length - 1);
}
// 在treeIndex的位置创建表示区间[l...r]的线段树
private void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
// int mid = (l + r) / 2;
int mid = l + (r - l) / 2;
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid + 1, r);
tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
public int getSize() {
return data.length;
}
public E get(int index) {
if (index < 0 || index >= data.length) throw new IllegalArgumentException(
"Index is illegal."
);
return data[index];
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
private int leftChild(int index) {
return 2 * index + 1;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
private int rightChild(int index) {
return 2 * index + 2;
}
// 返回区间[queryL, queryR]的值
public E query(int queryL, int queryR) {
if (
queryL < 0 ||
queryL >= data.length ||
queryR < 0 ||
queryR >= data.length ||
queryL > queryR
) throw new IllegalArgumentException("Index is illegal.");
return query(0, 0, data.length - 1, queryL, queryR);
}
// 在以treeIndex为根的线段树中[l...r]的范围里,搜索区间[queryL...queryR]的值
private E query(int treeIndex, int l, int r, int queryL, int queryR) {
if (l == queryL && r == queryR) return tree[treeIndex];
int mid = l + (r - l) / 2;
// treeIndex的节点分为[l...mid]和[mid+1...r]两部分
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (queryL >= mid + 1) return query(
rightTreeIndex,
mid + 1,
r,
queryL,
queryR
); else if (queryR <= mid) return query(
leftTreeIndex,
l,
mid,
queryL,
queryR
);
E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return merger.merge(leftResult, rightResult);
}
// 将index位置的值,更新为e
public void set(int index, E e) {
if (index < 0 || index >= data.length) throw new IllegalArgumentException(
"Index is illegal"
);
data[index] = e;
set(0, 0, data.length - 1, index, e);
}
// 在以treeIndex为根的线段树中更新index的值为e
private void set(int treeIndex, int l, int r, int index, E e) {
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
// treeIndex的节点分为[l...mid]和[mid+1...r]两部分
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (index >= mid + 1) set(rightTreeIndex, mid + 1, r, index, e); else set( // index <= mid
leftTreeIndex,
l,
mid,
index,
e
);
tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append('[');
for (int i = 0; i < tree.length; i++) {
if (tree[i] != null) res.append(tree[i]); else res.append("null");
if (i != tree.length - 1) res.append(", ");
}
res.append(']');
return res.toString();
}
}
并查集
连接问题,树子节点指向父节点
通过 size 进行优化
/// Leetcode 547. Friend Circles
/// https://leetcode.com/problems/friend-circles/description/
///
/// 课程中在这里暂时没有介绍这个问题
/// 该代码主要用于使用Leetcode上的问题测试我们的UF类
import java.util.TreeSet;
class Solution {
private interface UF {
int getSize();
boolean isConnected(int p, int q);
void unionElements(int p, int q);
}
// 我们的第三版Union-Find
private class UnionFind3 implements UF {
private int[] parent; // parent[i]表示第一个元素所指向的父节点
private int[] sz; // sz[i]表示以i为根的集合中元素个数
// 构造函数
public UnionFind3(int size) {
parent = new int[size];
sz = new int[size];
// 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
for (int i = 0; i < size; i++) {
parent[i] = i;
sz[i] = 1;
}
}
@Override
public int getSize() {
return parent.length;
}
// 查找过程, 查找元素p所对应的集合编号
// O(h)复杂度, h为树的高度
private int find(int p) {
if (p < 0 || p >= parent.length) throw new IllegalArgumentException(
"p is out of bound."
);
// 不断去查询自己的父亲节点, 直到到达根节点
// 根节点的特点: parent[p] == p
while (p != parent[p]) p = parent[p];
return p;
}
// 查看元素p和元素q是否所属一个集合
// O(h)复杂度, h为树的高度
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 合并元素p和元素q所属的集合
// O(h)复杂度, h为树的高度
@Override
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) return;
// 根据两个元素所在树的元素个数不同判断合并方向
// 将元素个数少的集合合并到元素个数多的集合上
if (sz[pRoot] < sz[qRoot]) {
parent[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
} else { // sz[qRoot] <= sz[pRoot]
parent[qRoot] = pRoot;
sz[pRoot] += sz[qRoot];
}
}
}
public int findCircleNum(int[][] M) {
int n = M.length;
UnionFind3 uf = new UnionFind3(n);
for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) if (
M[i][j] == 1
) uf.unionElements(i, j);
TreeSet<Integer> set = new TreeSet<>();
for (int i = 0; i < n; i++) set.add(uf.find(i));
return set.size();
}
}
通过 rank 进行优化
/// Leetcode 547. Friend Circles
/// https://leetcode.com/problems/friend-circles/description/
///
/// 课程中在这里暂时没有介绍这个问题
/// 该代码主要用于使用Leetcode上的问题测试我们的UF类
import java.util.TreeSet;
class Solution {
private interface UF {
int getSize();
boolean isConnected(int p, int q);
void unionElements(int p, int q);
}
// 我们的第四版Union-Find
private class UnionFind4 implements UF {
private int[] rank; // rank[i]表示以i为根的集合所表示的树的层数
private int[] parent; // parent[i]表示第i个元素所指向的父节点
// 构造函数
public UnionFind4(int size) {
rank = new int[size];
parent = new int[size];
// 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
for (int i = 0; i < size; i++) {
parent[i] = i;
rank[i] = 1;
}
}
@Override
public int getSize() {
return parent.length;
}
// 查找过程, 查找元素p所对应的集合编号
// O(h)复杂度, h为树的高度
private int find(int p) {
if (p < 0 || p >= parent.length) throw new IllegalArgumentException(
"p is out of bound."
);
// 不断去查询自己的父亲节点, 直到到达根节点
// 根节点的特点: parent[p] == p
while (p != parent[p]) p = parent[p];
return p;
}
// 查看元素p和元素q是否所属一个集合
// O(h)复杂度, h为树的高度
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 合并元素p和元素q所属的集合
// O(h)复杂度, h为树的高度
@Override
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) return;
// 根据两个元素所在树的rank不同判断合并方向
// 将rank低的集合合并到rank高的集合上
if (rank[pRoot] < rank[qRoot]) parent[pRoot] = qRoot; else if (
rank[qRoot] < rank[pRoot]
) parent[qRoot] = pRoot; else { // rank[pRoot] == rank[qRoot]
parent[pRoot] = qRoot;
rank[qRoot] += 1; // 此时, 我维护rank的值
}
}
}
public int findCircleNum(int[][] M) {
int n = M.length;
UnionFind4 uf = new UnionFind4(n);
for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) if (
M[i][j] == 1
) uf.unionElements(i, j);
TreeSet<Integer> set = new TreeSet<>();
for (int i = 0; i < n; i++) set.add(uf.find(i));
return set.size();
}
}
加入路径压缩优化
/// Leetcode 547. Friend Circles
/// https://leetcode.com/problems/friend-circles/description/
///
/// 课程中在这里暂时没有介绍这个问题
/// 该代码主要用于使用Leetcode上的问题测试我们的UF类
import java.util.TreeSet;
class Solution {
private interface UF {
int getSize();
boolean isConnected(int p, int q);
void unionElements(int p, int q);
}
// 我们的第五版Union-Find
private class UnionFind5 implements UF {
// rank[i]表示以i为根的集合所表示的树的层数
// 在后续的代码中, 我们并不会维护rank的语意, 也就是rank的值在路径压缩的过程中, 有可能不在是树的层数值
// 这也是我们的rank不叫height或者depth的原因, 他只是作为比较的一个标准
private int[] rank;
private int[] parent; // parent[i]表示第i个元素所指向的父节点
// 构造函数
public UnionFind5(int size) {
rank = new int[size];
parent = new int[size];
// 初始化, 每一个parent[i]指向自己, 表示每一个元素自己自成一个集合
for (int i = 0; i < size; i++) {
parent[i] = i;
rank[i] = 1;
}
}
@Override
public int getSize() {
return parent.length;
}
// 查找过程, 查找元素p所对应的集合编号
// O(h)复杂度, h为树的高度
private int find(int p) {
if (p < 0 || p >= parent.length) throw new IllegalArgumentException(
"p is out of bound."
);
while (p != parent[p]) {
parent[p] = parent[parent[p]];
p = parent[p];
}
return p;
}
// 查看元素p和元素q是否所属一个集合
// O(h)复杂度, h为树的高度
@Override
public boolean isConnected(int p, int q) {
return find(p) == find(q);
}
// 合并元素p和元素q所属的集合
// O(h)复杂度, h为树的高度
@Override
public void unionElements(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) return;
// 根据两个元素所在树的rank不同判断合并方向
// 将rank低的集合合并到rank高的集合上
if (rank[pRoot] < rank[qRoot]) parent[pRoot] = qRoot; else if (
rank[qRoot] < rank[pRoot]
) parent[qRoot] = pRoot; else { // rank[pRoot] == rank[qRoot]
parent[pRoot] = qRoot;
rank[qRoot] += 1; // 此时, 我维护rank的值
}
}
}
public int findCircleNum(int[][] M) {
int n = M.length;
UnionFind5 uf = new UnionFind5(n);
for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) if (
M[i][j] == 1
) uf.unionElements(i, j);
TreeSet<Integer> set = new TreeSet<>();
for (int i = 0; i < n; i++) set.add(uf.find(i));
return set.size();
}
}
trie(字典树/前缀树)
trie(多叉树): 每个节点有若干个指向下个节点的指针
import java.util.TreeMap;
public class Trie {
private class Node {
public boolean isWord;
public TreeMap<Character, Node> next;
public Node(boolean isWord) {
this.isWord = isWord;
next = new TreeMap<>();
}
public Node() {
this(false);
}
}
private Node root;
private int size;
public Trie() {
root = new Node();
size = 0;
}
// 获得Trie中存储的单词数量
public int getSize() {
return size;
}
// 向Trie中添加一个新的单词word
public void add(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next.get(c) == null) cur.next.put(c, new Node());
cur = cur.next.get(c);
}
if (!cur.isWord) {
cur.isWord = true;
size++;
}
}
// 查询单词word是否在Trie中
public boolean contains(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next.get(c) == null) return false;
cur = cur.next.get(c);
}
return cur.isWord;
}
// 查询是否在Trie中有单词以prefix为前缀
public boolean isPrefix(String prefix) {
Node cur = root;
for (int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
if (cur.next.get(c) == null) return false;
cur = cur.next.get(c);
}
return true;
}
}
最大的问题,空间
其他相关: 压缩字典树, ternary search trie (三分搜索树), 文件压缩
子串查询: kmp, boyer-moore, rabin-karp
sqrt
使用分块的思想
基本思想: 把一个含有 N 个元素的数组分成 √n 份
import java.util.Arrays;
public class NumArray {
private int[] data, blocks;
private int N; // 元素总数
private int B; // 每组元素个数
private int Bn; // 组数
public NumArray(int[] nums) {
N = nums.length;
if (N == 0) return;
B = (int) Math.sqrt(N);
Bn = N / B + (N % B > 0 ? 1 : 0);
data = Arrays.copyOf(nums, N);
blocks = new int[Bn];
for (int i = 0; i < N; i++) blocks[i / B] += nums[i];
}
public int sumRange(int x, int y) {
if (x < 0 || x >= N || y < 0 || y >= N || x > y) return 0;
int bstart = x / B, bend = y / B;
int res = 0;
if (bstart == bend) {
for (int i = x; i <= y; i++) res += data[i];
return res;
}
for (int i = x; i < (bstart + 1) * B; i++) res += data[i];
for (int b = bstart + 1; b < bend; b++) res += blocks[b];
for (int i = bend * B; i <= y; i++) res += data[i];
return res;
}
public void update(int i, int val) {
if (i < 0 || i >= N) return;
int b = i / B;
blocks[b] -= data[i];
blocks[b] += val;
data[i] = val;
}
}
字符串
非比较排序
不借助 compareTo,主要应用于字符串
计数排序
适合数据量小的计数排序,时间复杂度为 O(n+R)
三个元素的简单实现
class Solution {
public void sortColors(int[] nums) {
// 处理元素取值范围是 [0, R) 的计数排序
int R = 3;
int[] cnt = new int[R];
for(int num: nums)
cnt[num] ++;
// [index[i], index[i + 1]) 的值为 i
int[] index = new int[R + 1];
for(int i = 0; i < R; i ++)
index[i + 1] = index[i] + cnt[i];
for(int i = 0; i + 1 < index.length; i ++)
// [index[i], index[i + 1]) 的值为 i
for(int j = index[i]; j < index[i + 1]; j ++)
nums[j] = i;
}
}
更一般的同样实现,前缀填充
class Solution {
public void sortColors(int[] nums) {
int[] cnt = new int[3];
for(int num: nums)
cnt[num] ++;
int[] index = new int[4];
for(int i = 0; i < 3; i ++)
index[i + 1] = index[i] + cnt[i];
for(int i = 0; i + 1 < index.length; i ++)
// index[i] 到 index[i + 1] 的值为 i
for(int j = index[i]; j < index[i + 1]; j ++)
nums[j] = i;
}
}
索引对应,往后面空出一位填充相同元素
import java.util.Random;
public class Main {
public static void main(String[] args) {
int n = 26 * 26 * 26 * 26;
Student[] students = new Student[n];
int k = 0;
Random rnd = new Random();
for(char c1 = 'a'; c1 <= 'z'; c1 ++)
for(char c2 = 'a'; c2 <= 'z'; c2 ++)
for(char c3 = 'a'; c3 <= 'z'; c3 ++)
for(char c4 = 'a'; c4 <= 'z'; c4 ++) {
students[k] = new Student("" + c1 + c2 + c3 + c4, rnd.nextInt(101));
k ++;
}
// 计数排序过程
int R = 101;
// O(n)
int[] cnt = new int[R];
for(Student student: students)
cnt[student.getScore()] ++;
// O(R)
int[] index = new int[R + 1];
for(int i = 0; i < R; i ++)
index[i + 1] = index[i] + cnt[i];
// O(n)
Student[] temp = new Student[n];
for(Student student: students){
temp[index[student.getScore()]] = student;
index[student.getScore()] ++;
}
// O(n)
for(int i = 0; i < n; i ++)
students[i] = temp[i];
// O(n + R)
// 验证计数排序算法
for(int i = 1; i < n; i ++) {
if (students[i - 1].getScore() > students[i].getScore())
throw new RuntimeException("Sort failed");
if(students[i - 1].getScore() == students[i].getScore()){
if(students[i - 1].getName().compareTo(students[i].getName()) >= 0)
throw new RuntimeException("Non-Stable counting sort!");
}
}
}
}
lsd 基数排序
只适用于等长字符串(车牌号,电话号码)
import java.util.Arrays;
public class LSDSort {
private LSDSort() {}
public static void sort(String[] arr, int W) {
for (String s : arr) if (
s.length() != W
) throw new IllegalArgumentException(
"All Strings' length must be the same."
);
int R = 256;
int[] cnt = new int[R];
String[] temp = new String[arr.length];
int[] index = new int[R + 1];
for (int r = W - 1; r >= 0; r--) {
// O(n)
Arrays.fill(cnt, 0);
for (String s : arr) cnt[s.charAt(r)]++;
// O(R)
for (int i = 0; i < R; i++) index[i + 1] = index[i] + cnt[i];
// O(n)
for (String s : arr) {
temp[index[s.charAt(r)]] = s;
index[s.charAt(r)]++;
}
// O(n)
for (int i = 0; i < arr.length; i++) arr[i] = temp[i];
}
}
public static void main(String[] args) {
String[] arr = { "BCA", "CAB", "ACB", "BAC", "ABC", "CBA" };
LSDSort.sort(arr, 3);
for (String s : arr) System.out.println(s);
}
}
msd 基数排序
import java.util.Arrays;
public class MSDSort {
private MSDSort() {}
public static void sort(String[] arr) {
int N = arr.length;
String[] temp = new String[N];
sort(arr, 0, N - 1, 0, temp);
}
private static void sort(
String[] arr,
int left,
int right,
int r,
String[] temp
) {
if (left >= right) return;
int R = 256;
int[] cnt = new int[R + 1];
int[] index = new int[R + 2];
// O(n)
for (int i = left; i <= right; i++) cnt[r >= arr[i].length()
? 0
: (arr[i].charAt(r) + 1)]++;
// O(R)
for (int i = 0; i < R + 1; i++) index[i + 1] = index[i] + cnt[i];
// O(n)
for (int i = left; i <= right; i++) {
temp[index[r >= arr[i].length() ? 0 : (arr[i].charAt(r) + 1)] + left] =
arr[i];
index[r >= arr[i].length() ? 0 : (arr[i].charAt(r) + 1)]++;
}
// O(n)
for (int i = left; i <= right; i++) arr[i] = temp[i];
for (int i = 0; i < R; i++) sort(
arr,
left + index[i],
left + index[i + 1] - 1,
r + 1,
temp
);
}
public static void main(String[] args) {
String[] arr = { "BCA", "CBAA", "AC", "BADFE", "ABC", "CBA" };
MSDSort.sort(arr);
for (String s : arr) System.out.println(s);
}
}
桶排序
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
public class BucketSort {
private BucketSort() {}
public static void sort(Integer[] arr, int B) {
if (B <= 1) throw new IllegalArgumentException("B must be > 1");
Integer[] temp = new Integer[arr.length];
sort(arr, 0, arr.length - 1, B, temp);
}
private static void sort(
Integer[] arr,
int left,
int right,
int B,
Integer[] temp
) {
if (left >= right) return;
int maxv = Integer.MIN_VALUE, minv = Integer.MAX_VALUE;
for (int i = left; i <= right; i++) {
maxv = Math.max(maxv, arr[i]);
minv = Math.min(minv, arr[i]);
}
if (maxv == minv) return;
int d = (maxv - minv + 1) / B + ((maxv - minv + 1) % B > 0 ? 1 : 0);
int[] cnt = new int[B];
int[] index = new int[B + 1];
// O(n)
for (int i = left; i <= right; i++) cnt[(arr[i] - minv) / d]++;
// O(R)
for (int i = 0; i < B; i++) index[i + 1] = index[i] + cnt[i];
// O(n)
for (int i = left; i <= right; i++) {
int p = (arr[i] - minv) / d;
temp[left + index[p]] = arr[i];
index[p]++;
}
// O(n)
for (int i = left; i <= right; i++) arr[i] = temp[i];
// 递归下去:
sort(arr, left, left + index[0] - 1, B, temp);
for (int i = 0; i < B - 1; i++) sort(
arr,
left + index[i],
left + index[i + 1] - 1,
B,
temp
);
}
// 更简单的桶排序
public static void sort2(Integer[] arr, int c) {
if (c <= 0) throw new IllegalArgumentException("c must be > 0");
int maxv = Integer.MIN_VALUE, minv = Integer.MAX_VALUE;
for (int e : arr) {
maxv = Math.max(maxv, e);
minv = Math.min(minv, e);
}
int range = maxv - minv + 1; // arr 中的数据范围
int B = range / c + (range % c > 0 ? 1 : 0); // 根据数据范围决定桶的个数
LinkedList<Integer>[] buckets = new LinkedList[B];
for (int i = 0; i < B; i++) buckets[i] = new LinkedList<>();
for (int e : arr) buckets[(e - minv) / range].add(e);
for (int i = 0; i < B; i++) Collections.sort(buckets[i]);
int index = 0;
for (int i = 0; i < B; i++) for (int e : buckets[i]) arr[index++] = e;
}
public static void main(String[] args) {
int n = 1000000;
Integer[] arr = ArrayGenerator.generateRandomArray(n, n);
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
SortingHelper.sortTest("BucketSort", arr);
SortingHelper.sortTest("BucketSort2", arr2);
}
}
模式匹配
字符串匹配和字符串哈希
快乐前缀
public class Solution {
public String longestPrefix(String s) {
// s[0...len - 1], s[s.length - len...s.length - 1]
for (int len = s.length() - 1; len >= 1; len--) if (
equal(s, 0, len - 1, s.length() - len, s.length() - 1)
) return s.substring(0, len);
return "";
}
private boolean equal(String s, int l1, int r1, int l2, int r2) {
for (int i = l1, j = l2; i <= r1 && j <= r2; i++, j++) if (
s.charAt(i) != s.charAt(j)
) return false;
return true;
}
public static void main(String[] args) {
System.out.println((new Solution()).longestPrefix("level"));
// l
}
}
使用哈希实现快乐排序
public class Solution2 {
private long MOD = (long) (1e9 + 7);
public String longestPrefix(String s) {
long[] pow26 = new long[s.length()];
pow26[0] = 1;
for (int i = 1; i < pow26.length; i++) pow26[i] = pow26[i - 1] * 26 % MOD;
// prehash[i]: hash(s[0...i])
long[] prehash = new long[s.length()];
prehash[0] = s.charAt(0) - 'a';
for (int i = 1; i < s.length(); i++) prehash[i] =
(prehash[i - 1] * 26 + s.charAt(i) - 'a') % MOD;
// posthash[i]: hash(s[i...s.length - 1])
long[] posthash = new long[s.length()];
posthash[s.length() - 1] = s.charAt(s.length() - 1) - 'a';
for (int i = s.length() - 2; i >= 0; i--) posthash[i] =
(pow26[s.length() - i - 1] * (s.charAt(i) - 'a') + posthash[i + 1]) % MOD;
// s[0...len - 1], s[s.length - len...s.length - 1]
for (int len = s.length() - 1; len >= 1; len--) {
if (
prehash[len - 1] == posthash[s.length() - len] &&
equal(s, 0, len - 1, s.length() - len, s.length() - 1)
) return s.substring(0, len);
}
return "";
}
private boolean equal(String s, int l1, int r1, int l2, int r2) {
for (int i = l1, j = l2; i <= r1 && j <= r2; i++, j++) if (
s.charAt(i) != s.charAt(j)
) return false;
return true;
}
public static void main(String[] args) {
System.out.println((new Solution2()).longestPrefix("level"));
// l
}
}
滚动哈希到 rabin-karp 算法
滚动哈希
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
class Solution2 {
public List<String> findRepeatedDnaSequences(String s) {
if (s.length() < 10) return new ArrayList<String>();
int[] map = new int[256];
map['A'] = 1;
map['C'] = 2;
map['G'] = 3;
map['T'] = 4;
HashSet<Long> seen = new HashSet<>();
HashSet<String> res = new HashSet<>();
long hash = 0, ten9 = (long) 1e9;
for (int i = 0; i < 9; i++) hash = hash * 10 + map[s.charAt(i)];
for (int i = 9; i < s.length(); i++) {
hash = hash * 10 + map[s.charAt(i)];
if (seen.contains(hash)) res.add(
s.substring(i - 9, i + 1)
); else seen.add(hash);
hash -= map[s.charAt(i - 9)] * ten9;
}
return new ArrayList<String>(res);
}
}
rabin-karp
public class SubstringMatch {
private SubstringMatch() {}
// 在 s 中寻找 t,并返回匹配的第一个索引 i; 如果没有找到,返回 -1
public static int bruteforce(String s, String t) {
if (s.length() < t.length()) return -1;
if (t.length() == 0) return 0;
for (int i = 0; i + t.length() <= s.length(); i++) {
int j = 0;
for (; j < t.length(); j++) if (s.charAt(i + j) != t.charAt(j)) break;
if (j == t.length()) return i;
}
return -1;
}
public static int rabinKarp(String s, String t) {
if (s.length() < t.length()) return -1;
if (t.length() == 0) return 0;
long thash = 0, MOD = (long) 1e9 + 7, B = 256;
for (int i = 0; i < t.length(); i++) thash =
(thash * B + t.charAt(i)) % MOD;
long hash = 0, P = 1;
for (int i = 0; i < t.length() - 1; i++) P = P * B % MOD;
for (int i = 0; i < t.length() - 1; i++) hash =
(hash * B + s.charAt(i)) % MOD;
for (int i = t.length() - 1; i < s.length(); i++) {
hash = (hash * B + s.charAt(i)) % MOD;
if (hash == thash && equal(s, i - t.length() + 1, t)) return (
i - t.length() + 1
);
hash = (hash - s.charAt(i - t.length() + 1) * P % MOD + MOD) % MOD;
}
return -1;
}
private static boolean equal(String s, int l, String t) {
for (int i = 0; i < t.length(); i++) if (
s.charAt(l + i) != t.charAt(i)
) return false;
return true;
}
public static void main(String[] args) {
String s1 = "hello, this is liuyubobobo.";
String t1 = "bo";
SubstringMatchHelper.matchTest("bruteforce", s1, t1);
SubstringMatchHelper.matchTest("rk", s1, t1);
String s2 = FileOperation.readFile("pride-and-prejudice.txt");
String t2 = "china";
SubstringMatchHelper.matchTest("bruteforce", s2, t2);
SubstringMatchHelper.matchTest("rk", s2, t2);
SubstringMatchHelper.matchTest("bruteforce", s2, "zyx");
SubstringMatchHelper.matchTest("rk", s2, "zyx");
/// Worst case
int n = 1000000, m = 10000;
StringBuilder s3 = new StringBuilder();
for (int i = 0; i < n; i++) s3.append('a');
StringBuilder t3 = new StringBuilder();
for (int i = 0; i < m - 1; i++) t3.append('a');
t3.append('b');
SubstringMatchHelper.matchTest("bruteforce", s3.toString(), t3.toString());
SubstringMatchHelper.matchTest("rk", s3.toString(), t3.toString());
}
}
获取每一个子串的哈希值进行比较,最差情况下退化为 O(mn)
对于 aaaaaaaaaa 里面寻找 aaa 这种情况对退化成 O(mn)