排序

快速排序

快速排序算法采用分而治之的思想,从数组中选择一个“基准”元素,根据小于或大于基准将其他元素划分为两个子数组,然后递归对子数组进行排序。

 1  public int partition(int[] nums, int l, int r) {
 2      int key = nums[l];
 3      while (l < r) {
 4          while (l < r && nums[r] >= key) {
 5              r--;
 6          }
 7          nums[l] = nums[r];
 8          while (l < r && nums[l] < key) {
 9              l++;
10          }
11          nums[r] = nums[l];
12      }
13      nums[l] = key;
14      return l;
15  }
16
17  public void qk(int nums[], int l, int r) {
18      if (l >= r)
19          return;
20      int pivot = partition(nums, l, r);
21      qk(nums, l, pivot - 1);
22      qk(nums, pivot + 1, r);
23  }

挖坑法

以上代码为“挖坑法”,由两个函数构成,qk 函数负责递归调用,partition函数负责排序并返回排序后基准元素位置。

在左右指针没有对撞的情况下:

  • 先用右指针往左遍历,当找到比基准值小的数时,将该数填入左指针的位置。
  • 左指针往右遍历,找到比基准值大的数时,填入右指针的位置。
  • 若左右指针未对撞,重复以上步骤。

左右指针对撞,排序完成,得到基准数位置。然后对被基准数分开的两个子数组继续使用快速排序。

注意

右指针的数填入左指针前,左指针的数已经被取为基准,因此不会丢失。 左指针的数填入右指针时,右指针的数已经填入左指针遍历前的位置,因此也不会丢失。

堆排序

堆排序(Heapsort)是一种常用的排序算法,它使用了一棵树形数据结构,称为堆(heap),来实现排序。 heap 是一种完全二叉树,其中每个节点都满足某种特定性质(通常是父节点大于或等于左右子节点)。

 1  import java.util.Arrays;
 2
 3  public class HeapSort {
 4      public static void main(String[] args) {
 5          int arr[] = {4, 6, 8, 5, 9};
 6          System.out.println("排序前" + Arrays.toString(arr));
 7          heapSort(arr);
 8          System.out.println("排序后" + Arrays.toString(arr));
 9      }
10
11      public static void heapSort(int arr[]) {
12          int temp = 0;
13
14          for (int i = arr.length / 2 - 1; i >= 0; i--) {
15              adjustHeap(arr, i, arr.length);
16          }
17          /**
18          * 将堆项元素与末尾元素交换,将最大元素"沉"到数组末端;
19          * 重新调整结构,使其满足堆定义,然后继续交换堆项元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
20          */
21
22          for (int j = arr.length - 1; j > 0; j--) {
23              temp = arr[j];
24              arr[j] = arr[0];
25              arr[0] = temp;
26              adjustHeap(arr, 0, j);
27          }
28      }
29
30
31      /**
32      * 将一个数组(二叉树)调整成一个大根堆
33      * 功能:完成将以i对应的非叶子结点的树调整成大顶堆
34      * 举例int arr[]={4, 6,8,5,9};=>i=1=> adjustHeap=>得到{4,9,8,5, 6}
35      * 如果我们再次调用adjustHeap 传入的是i=0=>得到{4,9, 8,5,6}=> {9,6,8,5, 4}
36      *
37      * @param arr    待调整的数组
38      * @param i      表示非叶子结点在数组中索引
39      * @param length 表示对多少个元素继续调整,length 是在逐渐的减少
40      */
41      public static void adjustHeap(int arr[], int i, int length) {
42
43          int temp = arr[i];//先取出当前元素的值,保存在临时变量
44          //开始调整.
45          //说明:k=i*2+1k是i结点的左子结点
46          for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
47              if (k + 1 < length && arr[k] < arr[k + 1]) {
48                  k++;
49              }
50              if (arr[k] > arr[i]) {
51                  arr[i] = arr[k];
52                  i = k;
53              } else {
54                  break;
55              }
56          }
57          arr[i] = temp;
58      }
59  }

二分法插入排序

二分法没有排序,只有查找。所以当找到要插入的位置时。移动必须从最后一个记录开始,向后移动一位,再移动倒数第2位,直到要插入的位置的记录移后一位。
二分插入排序是稳定的与二分查找的复杂度相同; 最好的情况是当插入的位置刚好是二分位置 所用时间为O(log₂n); 最坏的情况是当插入的位置不在二分位置 所需比较次数为O(n),无限逼近线性查找的复杂度。

 1  public static void advanceInsertSortWithBinarySearch(int[] arr) {
 2      for (int i = 1; i < arr.length; i++) {
 3          int temp = arr[i];
 4          int low = 0, high = i - 1;
 5          int mid = -1;
 6          while (low <= high) {
 7              mid = low + (high - low) / 2;
 8              if (arr[mid] > temp) {
 9                  high = mid - 1;
10              } else { // 元素相同时,也插入在后面的位置
11                  low = mid + 1;
12              }
13          }
14          for(int j = i - 1; j >= low; j--) {
15              arr[j + 1] = arr[j];
16          }
17          arr[low] = temp;
18      }
19  }