八大內部排序演算法總結
插入排序:直接插入排序
思想:將資料分為兩組,一組為已排序序列,另一組為待排序序列,不斷的將待排序序列加入已排序序列。最終得到的是已排序序列。
public class InsertionSort implements Sort{ public void sort(int a[]){ for(int i = 1; i < a.length; i++){ int t = a[i], j = i-1; while(j >= 0 && a[j] > t){ a[j+1] = a[j]; j--; } a[j+1] = t; } SortUtils.print(a); } public static void main(String[] args) { new InsertionSort().sort(SortUtils.generate(10)); } }
插入排序:Shell排序
思想:帶有間隔的插入排序,插入排序每次的消耗都在比較當前元素在已排序元素中的位置,所以希爾排序要減少這種比較,它使用帶有間隔的插入排序方式,每次選取n/2作為間隔,然後跳著比較資料,直到間隔為1結束。
選擇排序:直接選擇排序public class ShellSort implements Sort{ public static void shellNSort(int a[], int k) { for (int t = 0; t < k; t++) { for (int i = t + k; i < a.length; i += k) { int p = a[i], j = i - k; while (j >= 0 && p < a[j]) { a[j + k] = a[j]; j -= k; } a[j + k] = p; } } } public void sort(int a[]) { int k = a.length / 2; while (k >= 1) { shellNSort(a, k); k /= 2; } SortUtils.print(a); } public static void main(String[] args) { new ShellSort().sort(new int[] { 49, 38, 65, 97, 76, 13, 27, 48, 55, 4 }); } }
思想:每次選擇最大或者最小的元素,直到資料結尾,之後再交換最大元素和末尾元素,這之後按照同樣的方法找到次小元素,直到只剩下一個元素為止。
public class SelectionSort implements Sort{ public void sort(int a[]){ for(int i = 1; i < a.length;i++){ int max = 0; for(int j = 1; j <= a.length-i;j++){ if(a[j] > a[max]){ max = j; } } SortUtils.swap(a, max, a.length-i); //SortUtils.print(a); } SortUtils.print(a); } public static void main(String[] args) { new SelectionSort().sort(SortUtils.generate(4091,10)); } }
選擇排序:堆排序
思想:構建堆資料結構,每次從堆中選出堆頂元素放到堆的末尾,這樣當所有的堆頂元素都被放到末尾過一次,整體就是有序的。此外如果是大頂堆,則會是從小到大的順序,如果是小頂堆,則會是從大到小的順序。
public class HeapSort implements Sort{
private static int left(int i) {
return 2 * i + 1;
}
private static int right(int i) {
return 2 * i + 2;
}
public static void maxHeapIFY(int a[], int size, int index) {
int l = left(index), r = right(index);
int largest = index;
if (l < size && a[l] > a[index]) {
largest = l;
}
if (r < size && a[r] > a[largest]) {
largest = r;
}
if (largest != index) {
SortUtils.swap(a, index, largest);
maxHeapIFY(a, size, largest);
}
}
public static void buildMaxHeap(int a[]) {
for (int i = a.length / 2; i >= 0; i--) {
maxHeapIFY(a, a.length, i);
}
}
public void sort(int a[]) {
buildMaxHeap(a);
for (int i = a.length - 1; i > 0; i--) {
SortUtils.swap(a, 0, i);
maxHeapIFY(a, i, 0);
}
SortUtils.print(a);
}
public static void main(String[] args) {
new HeapSort().sort(new int[] { 9, 9, 7, 6, 5, 4, 3, 2, 1 });
}
}
交換排序:氣泡排序
思想:每次比較相鄰的元素,如果第一個元素比第二個大,那麼交換它們的順序,依次如此,直到末尾,下一次對從頭到倒數第二個元素做這些操作,直到最後只剩下一個元素為止。
public class BubbleSort implements Sort {
public void sort(int a[]) {
for (int i = a.length - 1; i > 0; i--) {
boolean flag = false;
for (int j = 0; j < i; j++) {
if (a[j] > a[j + 1]) {
SortUtils.swap(a, j, j + 1);
flag = true;
}
}
if (!flag) {
break;
}
}
SortUtils.print(a);
}
public static void main(String[] args) {
new BubbleSort().sort(new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 });
}
}
交換排序:快速排序
思想:從資料中選出一個元素,每次找到這個元素應該在的位置,使得這個元素左邊都比它小,右邊都比它大,然後將資料分兩組,這個元素左邊一組,右邊一組,按照這個方式,每次都能找到一個元素應該在的位置,最終所有元素就在自己的位置了。
public class QuickSort implements Sort{
public static int partition(int a[], int low, int high) {
int p = a[low];
while (low < high) {
while (low < high && a[high] >= p) high--;
a[low] = a[high];
while (low < high && a[low] <= p) low++;
a[high] = a[low];
}
a[low] = p;
return low;
}
public static void quicksort(int a[], int low, int high) {
if (low < high) {
int p = partition(a, low, high);
quicksort(a, low, p - 1);
quicksort(a, p + 1, high);
}
}
public void sort(int a[]) {
quicksort(a, 0, a.length - 1);
SortUtils.print(a);
}
public static void main(String[] args) {
new QuickSort().sort(new int[] { 4, 1, 2, 3, 5, 7, 8, 5, 9 });
}
}
歸併排序
思想:每次將資料分為兩部分,左邊一部分右邊一部分,分別做歸併排序,然後將兩部分合並起來,遞迴的做這個操作,最終整個陣列就是已排序序列。public class MergeSort implements Sort{
public static void merge(int a[], int low, int p, int high){
int lenC = p-low+1;
int lenD = high-p;
int c[] = new int[lenC+1];
int d[] = new int[lenD+1];
for(int i = 0; i < lenC;i++){
c[i] = a[low + i];
}
for(int i = 0; i < lenD;i++){
d[i] = a[p + 1 + i];
}
c[lenC] = d[lenD] = Integer.MAX_VALUE;
int indexC = 0, indexD = 0;
for(int i = low; i <= high; i++){
if(c[indexC] < d[indexD]){
a[i] = c[indexC++];
} else {
a[i] = d[indexD++];
}
}
}
public static void mergesort(int a[], int low, int high){
if(low < high){
int mid = (low + high) / 2;
mergesort(a, low, mid);
mergesort(a, mid+1, high);
merge(a, low, mid, high);
}
}
public void sort(int a[]){
mergesort(a, 0, a.length-1);
SortUtils.print(a);
}
public static void main(String[] args) {
new MergeSort().sort(new int[]{4,3,2,1,9,8,7,6,5});
}
}
基數排序(這個演算法寫的不太好,效率不高)
思想:按照每個位的資料大小排序,如個位,按照大小排序,按十位的大小排序,按百位的大小排序(注意必須使用穩定的排序演算法,也就是說排序過程中元素的相對順序不變),這樣排序後,從頭到尾取出所有的元素即排序好的序列。
public class RadixSort implements Sort{
public static class Entry {
int value;
Entry next;
Entry pre;
public Entry(int value) {
this(value, null, null);
}
public Entry(int value, Entry pre, Entry next) {
this.value = value;
this.pre = pre;
this.next = next;
}
public void addNext(Entry e) {
e.next = next;
e.pre = this;
if (next != null)
next.pre = e;
next = e;
}
public void addPre(Entry e) {
e.next = this;
e.pre = pre;
if (pre != null)
pre.next = e;
pre = e;
}
public Entry remove() {
if (pre != null)
pre.next = next;
if (next != null)
next.pre = pre;
pre = null;
Entry n = next;
next = null;
return n;
}
public void clear() {
while (next != null) {
next = next.remove();
}
}
public void swap(Entry e) {
if (e == null)
return;
value = (e.value + value) - (e.value = value);
}
public String toString() {
return String.valueOf(value);
}
}
private static Entry bucketsA[] = new Entry[10];
private static Entry bucketsB[] = new Entry[10];
static {
for (int i = 0; i < 10; i++) {
bucketsA[i] = new Entry(0);
bucketsB[i] = new Entry(0);
}
}
public static void addToList(Entry head, Entry e, int t) {
Entry p = head;
while (p.next != null) {
if ((p.next.value / t) % 10 > (e.value / t) % 10) {
p.next.addPre(e);
break;
}
p = p.next;
}
if (p.next == null && (p.value / t) % 10 <= (e.value / t) % 10) {
p.addNext(e);
}
}
public static int[] CollectResult(int a[]) {
int index = 0;
for (int j = 0; j < bucketsA.length; j++) {
Entry p = bucketsA[j].next;
while (p != null) {
a[index++] = p.value;
p = p.next;
}
}
return a;
}
public void sort(int a[], int radix) {
for (int j = 0; j < bucketsA.length; j++) {
bucketsA[j].clear();
}
for (int i = 0; i < a.length; i++) {
addToList(bucketsA[a[i] % 10], new Entry(a[i]), 1);
}
int t = 10;
for (int i = 1; i < radix; i++) {
Entry[] bucketsC = bucketsA;// swap
bucketsA = bucketsB;
bucketsB = bucketsC;
for (int j = 0; j < bucketsA.length; j++) {
bucketsA[j].clear();
}
for (int j = 0; j < bucketsB.length; j++) {
while (bucketsB[j].next != null) {
Entry e = bucketsB[j].next;
bucketsB[j].next.remove();
addToList(bucketsA[(e.value / t) % 10], e, t);
}
}
t *= 10;
}
CollectResult(a);
SortUtils.print(a);
}
public void sort(int a[]){
sort(a, 3);
}
public static void main(String[] args) {
new RadixSort().sort(new int[] { 329, 457, 657, 839, 436, 720, 355 }, 3);
}
}
這裡使用了兩組桶,其效果可能會比正常的基數排序要差,但是基數的期望複雜度是O(n)。
總結
類別 |
排序方法 |
時間複雜度 |
空間複雜度 |
穩定性 |
||
平均 |
最好 |
最壞 |
||||
插入排序 |
直接插入 |
O(n2) |
O(n) |
O(n2) |
O(1) |
穩定 |
Shell排序 |
O(n1.3) |
O(n) |
O(n2) |
O(1) |
不穩定 |
|
選擇排序 |
直接選擇 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
不穩定 |
堆排序 |
O(nlgn) |
O(nlgn) |
O(nlgn) |
O(1) |
不穩定 |
|
交換排序 |
氣泡排序 |
O(n2) |
O(n) |
O(n2) |
O(1) |
穩定 |
快速排序 |
O(nlgn) |
O(nlgn) |
O(n2) |
O(nlgn) |
不穩定 |
|
歸併排序 |
O(nlgn) |
O(nlgn) |
O(nlgn) |
n |
穩定 |
|
基數排序 |
O(d(r+n)) |
O(d(n+rd)) |
O(d(r+n)) |
O(rd+n) |
穩定 |
r表示關鍵字的基數,d表示長度,n表示關鍵字個數。
穩定排序包括:直接插入、氣泡排序、歸併排序、基數排序;基數排序依賴於穩定的排序方式。
附:
public interface Sort {
public void sort(int a[]);
}
public class SortUtils {
public static void swap(int a[], int i, int j){
a[i] = (a[i] + a[j])-(a[j]=a[i]);
}
public static void print(int a[]){
System.out.println(Arrays.toString(a));
}
public static Random static_rand = new Random();
public static Random rand = new Random(37);
public static int[] generate(int seed, int len){
static_rand.setSeed(seed);
int a[] = new int[len];
for(int i =0 ; i < len; i++){
a[i] = static_rand.nextInt(1000);
}
//System.out.print("Generated: ");
print(a);
return a;
}
public static int[] generate(int len){
int a[] = new int[len];
for(int i =0 ; i < len; i++){
a[i] = rand.nextInt(1000);
}
//System.out.print("Generated: ");
print(a);
return a;
}
}