目录
归并排序
将数组 arr[ n ] 分为左、右两部分,左与右再各自分两部分。不断重复二分,直到左与右都仅剩一个数。
对 左半数组 和 右半数组 进行 合并排序。
从分割到长度 1的数组开始,不断往回 合并 左,右数组 并排序。直到恢复原长。
public static void main(String[] args) {
int[] arr = {3,7,1,9,2,88,6,5};
sort(arr,0, (arr.length - 1));
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
// L:左边界,R:右边界
private static void sort(int[] arr, int L, int R) {
if (L == R) {
return;
}
int mid = L + ((R - L) / 2); // 取中点
sort(arr, L, mid);
sort(arr, mid + 1, R);
merge(arr, L, mid, R);
}
//合并 左半数组 和 右半数组
private static void merge(int[] arr, int L, int mid, int R) {
int[] help = new int[R - L + 1]; // 合并后的数组
int i = 0; // help[]初始位置
int p1 = L; // 左半数组 L->mid 初始位置 p1
int p2 = mid + 1; // 右半数组 (mid+1)->R 初始位置 p2
//左与右数组 从初始位置进行比较 : p1 与 p2 位置相比较,谁小,谁插入 help[],并后移
while (p1 <= mid && p2 <= R) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
// 上方for循环 必然越界跳出。 即 : 左 与 右 必然有一个先全部插入后 ++ 越界
// 即,出现 左数组: 1,8,9 . 右数组 2,3,4时
// help[] 已插入 1,2,3,4 . 剩余 8,9 未能插入。就因为 p2 >R 而 循环结束
// 越界情况处理 , 两个 while 仅一个生效
// 右数组完全插入后 越界时 ,左数组始终小于 mid
while (p1 <= mid) {
help[i++] = arr[p1++];
}
// 左数组越界时
while (p2 <= R) {
help[i++] = arr[p2++];
}
// 将 help[] 拷贝到 arr[] 中
for (int x = 0; x < help.length; x++) {
arr[L + x] = help[x];
}
}
public static void swap(int[] arr, int x,int y) {
temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
快速排序
默认将数组末尾当作中间值。如图,中间值为 3
设置大于区域,和小于区域。 我们准备把 大于中间值的,放入大于区域;小于中间值的,放入小于区域。
指针从头开始探测。
当探测到的数,大于中间值 :交换 指针位置和大于区域外侧。大于区域扩大。指针不动
当探测到的数,等于中间值 :指针右移
当探测到的数,小于中间值 :交换 指针位置和小于区域外侧。 小于区域扩大。指针右移
当,指针触碰大于区时,结束。同时,记录 等于区域 范围
分别在小于区、大于区。递归地重复此操作。
public static void main(String[] args) {
int[] arr = {2,3,8,1,3,2,7,4,5,3};
quickSort(arr, 0, arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
public static void quickSort(int[] arr, int L, int R) {
if (L < R) {
int[] p = partition(arr, L, R);
//去除 等于区域,进行递归
quickSort(arr, L, p[0] - 1);
quickSort(arr, p[1] + 1, R);
}
}
private static int[] partition(int arr[], int L, int R) {
int less = L - 1;// 小于区域,从左边界外 -1开始
int more = R + 1; // 大于区域,从 右边界外 R +1 开始
// 默认 数组 最后一个数 arr[R] 作为中间值
int midValue = arr[R];
// 视 L 为探测指针,当碰触大于区域,结束循环
// 仅当 探测值 小于等于 中间值,指针移动。
while (L < more ) {
if (arr[L] < midValue) { //当探测到小于 中间值
SortUtil.swap(arr, ++less, L++); //将 小于区域的下一个位置,与探测到的值 交换
// 小于区域 右移,探测指针右移
}else if (arr[L] > midValue) { // 探测到大于中间值时
SortUtil.swap(arr, L, --more); // 交换 大于区域左侧位置与探测位置,探测指针仍然停留
//注意 !!!!! 探测指针不移动 !!!!! 仅大于区域左移
} else {
L++; // 与中间值相等,指针右移
}
}
return new int[]{less + 1, more-1}; // 返回 等于中间值的区域
}
堆排序
1.构建大根堆
2.将 堆顶 与 堆最后一个元素交换。 减小堆 (剪除最后一个元素)
3.从堆顶开始重构大根堆。(与左右孩子最大值比较,如果比最大值小,则交往两者位置,继续往下判断)
重复 2,3步骤,直到堆大小减为0
被剪除部分已排序完成
public static void main(String[] args) {
int[] arr = {2,3,8,1,3,2,7,4,5,3};
heapSort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
//构建大根堆
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int heapSize = arr.length;
SortUtil.swap(arr, 0, (heapSize-1)); //交换堆顶与最后一个元素位置
heapSize--;//减小堆
while (heapSize > 0) {
heapify(arr, 0, heapSize); // 重构大根堆
SortUtil.swap(arr, 0, (heapSize - 1)); //交换堆顶与最后一个元素位置
heapSize--; //减小堆
}
}
/**
* 构建大根堆
* @param arr
* @param index 待判断节点
*/
private static void heapInsert(int[] arr, int index) {
//若 该节点 大于 其父节点 : 则交换两者位置 ,继续向上 比较父节点
while (arr[index] > arr[(index - 1) / 2]) {
SortUtil.swap(arr, index, (index - 1)/2);
index = (index - 1) / 2;
}
}
/**
* 重构大根堆
* @param arr
* @param heapSize 堆大小 (不断减小)
* @param index 堆顶
*/
private static void heapify(int[] arr, int index,int heapSize) {
int left = index * 2 + 1; //左孩子
while (left < heapSize) {
// 右孩子、左孩子 取最大孩子
int big = (left + 1) < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
// 如果 小于 最大孩子
if (arr[index] < arr[big]) {
SortUtil.swap(arr, index, big);//交换两者位置
}else {
break;
}
// 继续往下判断
index = big;
left = index * 2 + 1;
}
}