一、十大排序
1、分类
2、复杂度与稳定性比较
1、比较排序
1-1 初级排序( O ( n 2 ) O(n^2) O(n2))
1、冒泡: 嵌套循环,每次查看相邻元素,如果逆序,则交换。
public void bubbleSort(int[] array) {
int len = array.length;
for (int i=len-1; i>=0; i--) {
int flag = 0;
for (int j=0; j<i; i++) {
if (array[i]>array[j]) {
int temp = array[i]; array[i] = array[j]; array[j] = temp;
flag = 1;
}
}
if (flag == 0) break;
}
}
2、插入: 从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
public insertSort(int[] arr) {
int len = arr.length;
for (int p=1; p<len; p++) {
int temp = arr[p];
for (int i=p; i>0 && A[i-1]>temp; i--) {
arr[i] = arr[i-1];
}
a[i] = temp;
}
}
3、希尔
public static int[] shellSort(int[] arr) {
int len = arr.length;
for (int gap=Math.floor(len/2); gap>0; gap = Math.floor(gap / 2)) {
// 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
for (int pos=gap; pos<len; pos++ ) { /* 插入排序*/
int temp = arr[pos];
for (int i=pos; i>=gap && arr[i-gap]>temp; i-=gap ) {
arr[i] = arr[i-gap];
}
arr[i] = temp;
}
}
}
// 也可自定义增量序列解决,这里只列出一小部分增量
// int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};
// for ( Si=0; Sedgewick[Si]>=N; Si++ );
// for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] )
4、选择: 每次找最小值,然后放到待排序数组的起始位置。
public static int[] selectionSort(int[] arr) {
int len = arr.length;
for(int p = 0; p < len - 1; p++) {
int minIndex = p;
for(int i = p+1; i < len; i++) {
if(arr[i] < arr[minIndex]) { // 寻找最小的数
minIndex = i; // 将最小数的索引保存
}
}
int temp = arr[p]; arr[p] = arr[minIndex]; arr[minIndex] = temp;
}
return arr;
}
1-2 高级排序( O ( n l o g n ) O(nlogn) O(nlogn))
5、快排
版本1:双向扫描
具体动画可以看陈越老师老师课程之子集划分。
public void quickSort( int[] array, int left, int right ) {
int low = left, high = right-1, pivot = array[right-1]; /* pivot: 选基准 */
while (1) { /*将序列中比基准小的移到基准左边,大的移到右边*/
while (array[++low] < pivot) ;
while (array[--high] > pivot) ;
if (low < high) swap(array, low, high);
else break;
}
swap(array, low, right-1); /* 将基准换到正确的位置 */
quickSort(array, left, low-1 ); /* 递归解决左边 */
quickSort(array, low+1, right ); /* 递归解决右边 */
}
private void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
版本2:单向扫描(对单向链表快速排序同样适用)
算法细节理解可参考视频【嘉伦讲算法】快速排序|单向链表|可能是最简洁的代码实现思路 | 给链表排序 | 快排。
private int[] quickSort(int[] arr, int left, int right) {
if (left < right) {
int partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1); /* partitionIndex=less-1==pivot, less-2/less-1均可。 */
quickSort(arr, partitionIndex + 1, right);
}
return arr;
}
private int partition(int[] arr, int left, int right) {
// 选第一个元素作为基准值(pivot)
// 指针初始值 less=pivot+1, more=pivot+1,more跑得快
// 1)arr[more] > arr[pivot], more++
// 2)arr[more] < arr[pivot], swap(num, less, more),less++,more++
int pivot = left;
int less = pivot + 1;
for (int more = pivot + 1; more <= right; more++) {
if (arr[more] < arr[pivot]) {
swap(arr, more, less);
less++;
}
}
swap(arr, pivot, less - 1); /* 处理完结果:left~less-2, pivot=less-1, less~more=right */
return less - 1;
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
拓展:单向链表快速排序
public ListNode sortList(ListNode head) {
quickSort(head, null);
return head;
}
public void quickSort(ListNode head, ListNode tail) {
if (head == null || head.next == null || head == tail) return;
ListNode pivot = head, lessPre = head, less = head.next, more = head.next;
for ( ; more != tail; more = more.next) {
if (more.val < pivot.val) {
swap(more, less);
lessPre = less;
less = less.next;
}
}
swap(pivot, lessPre);
quickSort(head, lessPre);
quickSort(less, more);
}
public void swap(ListNode i, ListNode j) {
int tmp = i.val;
i.val = j.val;
j.val = tmp;
}
6、归并
public static void mergeSort(int[] array, int left, int right) {
if (right <= left) return;
int mid = (left + right) >> 1; // (left+right)/2
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
merge(array, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right-left+1]; // 中间数组
int i = left, j = mid+1, k = 0;
while (i<=mid && j<=right) {
temp[k++] = arr[i]<=arr[j] ? arr[i++] : arr[j++];
}
while (i<=mid) temp[k++] = arr[i++];
while (j<=right) temp[k++] = arr[j++];
for (int p=0; p<temp.length; p++) {
arr[left+p] = temp[p];
}
// 也可以用 System.arraycopy(a, start1, b, start2, length)
}
归并 VS 快排: 具有相似性,但步骤顺序相反。
归并:先排序左右子数组,然后合并两个有序子数组。
快排:先调配出左右子数组,然后对于左右子数组进行排序。
7、堆排序:插入 O ( l o g n ) O(logn) O(logn),取最大/小值 O ( 1 ) O(1) O(1)
S1:数组元素依次建立小顶堆;
S2:依次取堆顶元素,并删除。
版本1–PriorityQueue():
void heapSort(int[] arr) {
int len = arr.length;
PriorityQueue<Integer> q = new PriorityQueue<>();
for (int i=0; i<len; i++) {
q.add(a[i]);
}
for (int i=0; i<len; i++) {
a[i] = q.pop();
}
}
版本2–手动实现:
static void heapify(int[] array, int length, int i) {
int left = 2*i+1, right=2*i+2;
int largest = i;
if (left<length && array[left]>array[largest]) {
largest = leftChild;
}
if (right<length && array[right]>array[largest]) {
largest = right;
}
if (largest != i) {
int temp = array; array[i] = array[largest]; array[largest] = temp;
heapify[array, length, largest);
}
}
public static void heapSort(int[] array) {
if (array.length == 0) return;
int length = array.length;
for (int i = length/2-1; i>=0; i--) {
heapify(array, length, i);
}
for (int i=length-1; i>=0; i--) {
int temp = array[0]; array[0] = array[i]; array = temp;
heapify(array, i, 0);
}
}
2、非比较排序–特殊排序
8、计数排序
public static int[] countSort(int[] arr, int maxValue) {
int sortedIndex = 0;
int[] bucket = new int[maxValue+1],
for(int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
for(int j = 0; j < maxValue+1; j++) {
while(bucket[j] > 0) {
arr[sortedIndex++] = j;
bucket[j]--;
}
}
return arr;
}
9、桶排序
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 输入数据的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i]; // 输入数据的最大值
}
}
// 桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
// 利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
10、基数排序
var counter = [];
function radixSort(arr, maxDigit) {
var mod = 10;
var dev = 1;
for(var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(var j = 0; j < arr.length; j++) {
var bucket = parseInt((arr[j] % mod) / dev);
if(counter[bucket]==null) {
counter[bucket] = [];
}
counter[bucket].push(arr[j]);
}
var pos = 0;
for(var j = 0; j < counter.length; j++) {
var value = null;
if(counter[j]!=null) {
while((value = counter[j].shift()) != null) {
arr[pos++] = value;
}
}
}
}
return arr;
}
二、参考
1、快速排序代码示例
2、十大经典排序算法(动图演示)
3、八个排序的原理、Java 实现以及算法分析
4、Java学习笔记–PriorityQueue(优先队列)(堆)
5、快速排序 – 自实现
7、1.6 快速排序