算法和数据结构学习

本文详细介绍了算法与数据结构的基本概念,包括排序算法(如选择排序、快速排序、归并排序、堆排序)、线性数据结构(如数组、链表)、查找算法(线性查找、二分查找)和经典树结构。同时,探讨了哈希表、二分搜索树、AVL树、红黑树等高级数据结构,并总结了各种排序算法的稳定性和适用场景。
摘要由CSDN通过智能技术生成

算法与数据结构体系课程

什么是算法

  解决问题的方法
    五大特性
      有限性
      确定性
      可行性
      输入
      输出

什么是数据结构

  研究的是数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据获取修改数据

排序算法

  选择排序
先把最小的拿出来,剩下的再把最小的拿出来,可以原地比较交换排序

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)

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

星月IWJ

曾梦想杖键走天涯,如今加班又挨

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

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

打赏作者

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

抵扣说明:

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

余额充值