常见的排序(Sort)算法有选择排序、堆排序、归并排序、插入排序和快速排序等。排序算法的稳定性意味着如果列表中的两个元素被定义为相等,那么它们将在排序完成后保持它们的相对顺序。
算法原理
选择排序 O(N^2)
参考博客选择排序算法原理及实现(Java/Python)
归并排序 O(NlogN)
参考博客归并排序算法原理及实现(Java)
堆排序 O(NlogN)
使用最大堆(maxHeap)进行排序;
首先将待排序的元素放入maxHeap中,再将maxHeap中的元素输出到结果数组中。
改进堆排序:就地维护一个maxHeap,节约空间。
插入排序 O(N^2)
从左到右依次遍历数组中的元素,将当前元素通过swap的方式放到左边正确的位置上。
快速排序 O(NlogN)
从数组中随机选择一个元素,将其他小于等于它的元素放在左半部分,将大于等于它的元素放在右半部分;
对左右两部分的元素进行快速排序,直到数组大小为1。
对分区方式的改进:
每次选择数组最左边的元素P;
使用两个指针L和G分别从剩下部分的左边和右边进行遍历;
L遇到大于等于P的元素停止遍历;
G遇到小于等于P的元素停止遍历;
当两个指针都停止遍历时交换两个指针指向的元素,当G指针出现在L指针左边时停止遍历,将P和G的元素进行交换;
对P左右两边的数组分别进行快速排序。
java代码
Sorts.java
public class Sorts {
public int[] heapSort(int[] arr) {
/* 使用maxheap进行排序
* 先将arr中的元素添加到maxHeap中
* 再将maxHeap中的元素存入结果数组
*/
maxHeap nums = new maxHeap();
for (int i = 0; i < arr.length; i++) {
nums.insert(arr[i]);
}
int[] res = new int[arr.length];
for (int i = arr.length - 1; i >= 0; i--) {
res[i] = nums.remove();
}
return res;
}
public int[] heapSortInPlace(int[] arr) {
/* 堆排序的改进版本,使用输入的arr的空间维护maxHeap,并进行排序
* 从arr的末端开始,保持每个节点及其child满足maxHeap的特性
* 将maxHeap的最大元素弹出,放在arr的末尾,直到maxHeap的size为0
*/
maxHeap temp = new maxHeap();
for (int i = arr.length - 1; i >= 0; i--) {
int cur = i;
int next = temp.checkSwap(arr, cur, arr.length);
while (next != -1) {
swap(arr, cur, next);
cur = next;
next = temp.checkSwap(arr, cur, arr.length);
}
}
int curLast = arr.length - 1;
int curFirst = 0;
while (curFirst <= curLast) {
swap(arr, curFirst, curLast);
int nextIdx = temp.checkSwap(arr, curFirst, curLast);
int curIdx = curFirst;
while (nextIdx != -1) {
swap(arr, curIdx, nextIdx);
curIdx = nextIdx;
nextIdx = temp.checkSwap(arr, curIdx, curLast);
}
curLast -= 1;
}
return arr;
}
public int[] insertionSort(int[] arr) {
/* 插入排序
* 记录当前元素的索引cur和前一个元素的索引prev
* 如果cur的值大于等于front的值,cur++
* 否则进入循环,将cur和front的值进行swap,直到大于等于
*/
int cur;
int prev;
for (int i = 0; i < arr.length; i++) {
cur = i;
prev = cur - 1;
while (prev >= 0 && arr[cur] < arr[prev]) {
swap(arr, cur, prev);
cur = prev;
prev = cur - 1;
}
}
return arr;
}
public int[] quickSort(int[] arr) {
if (arr.length < 2) {
return arr;
}
int P = 0;
int L = P + 1;
int G = arr.length - 1;
while (L <= G) {
if (arr[L] < arr[P]) {
L += 1;
}
if (arr[G] > arr[P]) {
G -= 1;
}
if (L <= G && arr[L] >= arr[P] && arr[G] <= arr[P]) {
swap(arr, L, G);
L += 1;
G -= 1;
}
}
swap(arr, P, G);
int leftLen = G;
int rightLen = arr.length - G - 1;
if (leftLen > 0) {
int[] arrLeft = new int[G];
System.arraycopy(arr, 0, arrLeft, 0, leftLen);
System.arraycopy(quickSort(arrLeft), 0, arr, 0, leftLen);
}
if (rightLen > 0) {
int[] arrRight = new int[rightLen];
System.arraycopy(arr, G + 1, arrRight, 0, rightLen);
System.arraycopy(quickSort(arrRight), 0, arr, G + 1, rightLen);
}
return arr;
}
private void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
maxHeap.java
public class maxHeap {
private int[] arr;
private int totalSize;
private int size;
maxHeap() {
totalSize = 100;
arr = new int[totalSize];
size = 0;
}
public void insert(int t) {
/* 将元素t插入maxHeap中
* 将t访日size所指向的数组的尾部,维护maxHeap的complete特性
* 然后将其和parent进行比较,交换,知道满足max特性为止
*/
arr[size] = t;
int curIdx = size;
int parentIdx = getParent(curIdx);
while (parentIdx >= 0 && curIdx != 0) {
if (arr[curIdx] > arr[parentIdx]) {
swap(arr, curIdx, parentIdx);
}
curIdx = parentIdx;
parentIdx = getParent(curIdx);
}
size += 1;
}
private int getParent(int idx) {
/* 获取当前idx对应的parent节点的索引
*/
return (idx - 1) / 2;
}
public void swap(int[] arr, int a, int b) {
/* 交换数组中的两个元素
*/
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public int remove() {
/* 将maxHeap中最大的元素删除并return
* 将最大的元素记录,然后用arr末尾元素替代,删除末尾元素,维护complete特性
* 将当前的头部元素和其child进行比较,交换,知道满足max的特性
*/
int res = arr[0];
arr[0] = arr[size - 1];
size -= 1;
int curIdx = 0;
int nextIdx = checkSwap(arr, curIdx, size);
while (nextIdx != -1) {
swap(arr, curIdx, nextIdx);
curIdx = nextIdx;
nextIdx = checkSwap(arr, curIdx, size);
}
return res;
}
public int checkSwap(int[] arr, int cur, int size) {
/* check当前节点是否有需要swap的child节点
* 如果没有需要swap的节点,则return -1
* 否则return child节点的索引
*/
int leftIdx = getLeft(cur);
int rightIdx = getRight(cur);
if (leftIdx >= size && rightIdx >= size) {
return -1;
} else if (leftIdx >= size) {
if (arr[cur] < arr[rightIdx]) {
return rightIdx;
} else {
return -1;
}
} else if (rightIdx >= size) {
if (arr[cur] < arr[leftIdx]) {
return leftIdx;
} else {
return -1;
}
} else {
return maxInThree(arr, cur, leftIdx, rightIdx);
}
}
private int maxInThree(int[] arr, int a, int b, int c) {
/* 返回三个索引中数组值最大的索引, 如果该索引为a,return -1
*/
if (arr[b] > arr[c]) {
if (arr[a] > arr[b]) {
return -1;
} else {
return b;
}
} else {
if (arr[a] > arr[c]) {
return -1;
} else {
return c;
}
}
}
private int getLeft(int curIdx) {
/* 返回left child的节点索引
*/
return (curIdx + 1) * 2 - 1;
}
private int getRight(int curIdx) {
/* 返回right child的节点索引
*/
return (curIdx + 1) * 2;
}
}