演算法系列(五)排序演算法下篇--如何超越排序演算法下界
概述
在演算法系列(四)排序演算法中篇--歸併排序和快速排序一文中,我們介紹了歸併排序和快速排序,最壞的情況下,最快的排序演算法的時間複雜度是O(nlogn),是否有更好的演算法呢?到目前為止,沒有特殊的規則,O(nlogn)已經是最好的排序演算法了,也就是說通用排序演算法的時間複雜度下界就是O(nlogn)。如果限定一些規則,是可以打破這個下界的。下面說一下盡在O(n)時間內就能實現對陣列排序的演算法。
基於排序的規則
基於什麼樣的規則才能突破排序的下界呢?我們需要分析一下排序消耗的時間。排序需要遍歷,比較,交換。能否省略其中的一些步驟呢?這就是要定義的規則,通過規則減少排序步驟。下面舉一個最簡單的例子。
一組待排序的元素僅有1和2,沒有其它值,對這組數進行排序。
輸入A0,A1,A2,A3......An-1,Ai為1或者2
排序步驟
1、令k=0
2、令i從0到n-1依次取值,如果A[i]=1,k自增1
3、令i從0到k-1依次取值,將A[i]賦值為1
4、令i從k到n-1依次取值,將A[i]賦值為2
這樣我們完成了排序,花費的時間為O(n)
之前我們所說的演算法都是通過比較元素對來確定順序,那種排序叫做比較排序。凡是比較排序,通用下界為O(nlogn)
剛才所說的簡單例子,是一個簡單計數排序。下面詳細說明一下
使用基數排序超越排序下界
簡單例子每個元素僅有兩種可能的取值,擴充套件一下,如果每個元素有m個不同取值,只要取值是m個連續整數之內的整數,演算法是通用的。
首先,通過計算出有多少個元素的排序關鍵字等於某個值,隨後就能就算出有多少個元素的排序關鍵字小於每個可能的排序。
基本思想
計數排序的基本思想是對於給定的輸入序列中的每一個元素x,確定該序列中值小於x的元素的個數(此處並非比較各元素的大小,而是通過對元素值的計數和計數值的累加來確定)。一旦有了這個資訊,就可以將x直接存放到最終的輸出序列的正確位置上。
計數排序演算法詳細描述
該演算法需要三個基本方法
COUNT-KEY-EQUAL(A,n,m)
輸入 A 一個陣列,
n 陣列A中的元素個數
m陣列A中元素的取值範圍
輸出一個陣列equal[0......m],是equal[j]等於陣列A中元素值為j的元素個數
1、建立一個新陣列equal[0......m]
2、令equal陣列每個元素都為0
3、i從0到n-1依次取值,每次將equal[A[i]]的值自增1
4、返回equal
COUNT-KEY-LESS(equal,m)
輸入值 COUNT-KEY-EQUAL方法對應的值equal,m
輸出一個陣列less[0......m],less[j]=equal[0]+equal[1]+......+equal[j-1]
1、建立一個新陣列less[0...m]
2、令less[0]=0
3、j從1取到m,less[j]=less[j-1]+equal[j-1](這是普通的迭代演算法)
4、返回less
REARRANGE(A,less,n,m)
輸入 COUNT-KEY-EQUAL COUNT-KEY-LESS方法對應的A,less,n,m
輸出 陣列B,B中包含A中所有元素,並且已經排好序
1、建立新陣列B[0...n-1],next[0.....m]
2、j從0到m依次取值
令next[j]=less[j]+1
3、令i從0到n-1依次取值
key=A[i];index=next[key],B[index]=A[i],next[key]++
4、返回陣列B
程式碼實現
進行了邏輯整合,基本思路相同
package com.algorithm.sort;
/**
* 計數排序
*
* @author chao
*
*/
public class CountSort {
public static void main(String[] args) {
int[] num = { 1, 1 };
sort(num);
for (int i = 0; i < num.length; i++)
System.out.print(num[i] + " ");
}
/**
* 計數排序
*
* @param num
*/
public static void sort(int[] num) {
int len = num.length;
int[] orign = new int[len];
int max = 0;// 我們只對正整數排序
for (int i = 0; i < len; i++) {
orign[i] = num[i];
if (num[i] > max) {
max = num[i];
}
}
max = max + 1;
int[] count = new int[max];
for (int i = 0; i < max; i++) {
count[i] = 0;
}
for (int i = 0; i < len; i++) {
count[num[i]]++;
}
int t1, t2;
t1 = count[1];
count[0] = count[1] = 0;
for (int i = 2; i < max; i++) {
t2 = count[i];
count[i] = t1 + count[i - 1];
t1 = t2;
}
int key, index;
for (int i = 0; i < len; i++) {
key = orign[i];
index = count[key];
num[index] = orign[i];
count[key]++;
}
}
}
複雜度分析
計數排序的複雜性為O(n),但是有空間代價,如果最大數很大的話,空間代價非常大。
還有一種排序叫做基數排序,是基於計數排序的,有約束條件,時間複雜度為O(n)
桶排序,基數排序跟計數排序類似,不再詳細說明,堆排序(很常用,在樹形演算法分析中會再說明)
程式碼實現可以看github,地址https://github.com/robertjc/simplealgorithm
github程式碼也在不斷完善中,有些地方可能有問題,還請多指教
歡迎掃描二維碼,關注公眾賬號
相關文章
- 排序演算法(五)排序演算法
- 演算法系列(四)排序演算法中篇--歸併排序和快速排序演算法排序
- 【JAVA演算法】排序演算法 -- 快速排序Java演算法排序
- 如何分析排序演算法排序演算法
- #排序演算法#【6】排序演算法總結排序演算法
- 排序演算法__桶排序排序演算法
- 排序演算法__快速排序排序演算法
- 排序演算法__希爾排序排序演算法
- 排序演算法__堆排序排序演算法
- 排序演算法:快速排序排序演算法
- 【排序演算法】- 希爾排序排序演算法
- 排序演算法-堆排序排序演算法
- 排序演算法-快速排序排序演算法
- 排序演算法 - 堆排序排序演算法
- 排序演算法——快速排序排序演算法
- 排序演算法 - 快速排序排序演算法
- 排序演算法:插入排序演算法 PHP 版排序演算法PHP
- 排序演算法-Java實現快速排序演算法排序演算法Java
- 演算法基礎:排序演算法:選擇排序演算法排序
- 演算法系列:計數排序演算法排序
- 演算法系列:煎餅排序演算法排序
- 資料結構和演算法面試題系列—排序演算法之快速排序資料結構演算法面試題排序
- 演算法之常見排序演算法-氣泡排序、歸併排序、快速排序演算法排序
- 排序演算法排序演算法
- 排序演算法之 '快速排序'排序演算法
- 排序演算法–氣泡排序排序演算法
- 排序演算法__氣泡排序排序演算法
- 排序演算法__選擇排序排序演算法
- 排序演算法__歸併排序排序演算法
- 排序演算法__計數排序排序演算法
- 排序演算法__基數排序排序演算法
- 排序演算法:選擇排序排序演算法
- 排序演算法之——桶排序排序演算法
- 排序演算法--氣泡排序排序演算法
- 排序演算法:歸併排序排序演算法
- 歸併排序--排序演算法排序演算法
- 排序演算法之希爾排序排序演算法
- 排序演算法 - 歸併排序排序演算法