附:Java標準庫已經內置了排序功能Arrays.sort(ns);
1.冒泡排序
思想:像氣泡一樣,小的冒上去
每一輪循環後,最小的一個數被交換到開始,因此,下一輪循環就可以“刨除”最開始的數,每一輪循環都比上一輪循環的結束位置靠後一位。
代碼:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
int tmp;
// 排序前:
System.out.println(Arrays.toString(ns));
for(int i = 0; i < ns.length-1; i++) {//一共要進行n-1次比較
for(int j = ns.length-1; j > i; j--) {//每次要比較第ns.length-1到i的數
if(ns[j] < ns[j-1]){
tmp = ns[j];
ns[j] = ns[j-1];
ns[j-1] = tmp;
}
}
}
//排序後
System.out.println(Arrays.toString(ns));
}
}
算法分析:
- 平均時間複雜度:o(n^2),嵌套雙循環
- 最好時間複雜度:o(n),如果數組有序,循環一次就可以了
- 最壞時間複雜度:o(n^2)
- 空間複雜度:o(1),只使用了常數空間
- 穩定性:穩定(相同數字比較過程不會發生交換)
優化: 數據排好序後,冒泡算法仍會進行下一輪的比較,直到ns.length-1次結束。後面的比較是沒有意義的。
可以設置標誌位flag,如果發生了交換就設置flag爲true,否則爲false。
如果一輪交換後flag仍然爲false說明數組已經有序,不需要再進行下去了。
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
int tmp;
System.out.println(Arrays.toString(ns));
for(int i = 0; i < ns.length-1; i++) {
boolean flag = false;//優化的地方[1]
for(int j = ns.length-1; j > i; j--) {
if(ns[j] < ns[j-1]){
tmp = ns[j];
ns[j] = ns[j-1];
ns[j-1] = tmp;
flag = true;
}
}
if(!flag) {//優化的地方[2]
System.out.println("數組在第"+(i+1)+"次排序後有序");
break;
}
}
System.out.println(Arrays.toString(ns));
}
}
2.快速排序
思想(分治法):1.設置一個基準值(一般設置爲數組第一個數),標籤low和high。
2.從high開始,當high位置的書比基準值大,high--;比基準值小,把high位置的書放到low的位置,low++
low位置的數比基準值小,low++;比基準值大,把low 位置的數放到high,high--
直到low=high
(最後比基準值小的書都在它左邊,比基準值大的數都在它右邊)
3.對左右兩個小數列重複第二步,直到各個區間都只有一個數
Java代碼:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
// 排序前:
System.out.println(Arrays.toString(ns));
//排序
QuickSort(0,ns.length-1,ns);
//排序後
System.out.println(Arrays.toString(ns));
}
public static void QuickSort(int low, int high, int[] ns) {
while(low < high) { //所以有傳參的函數一定要先有個判斷條件!!!!!
int pivo = ns[low];//將第一個值作爲基準賦給pivo暫存
int slow = low;//記錄本次排序的初始值(後面會發生改變)
int shigh = high;
while(low < high) {
while(low < high && ns[high] >= pivo) {//左邊
high--;
}
ns[low] = ns[high];
while(low < high && ns[low] <= pivo) {//右邊
low++;
}
ns[high] = ns[low];
}
ns[low] = pivo;
// System.out.println("初始slow是 = "+slow+" , shigh是 = "+shigh+" ,基準值 = ns[" +slow+"] = "+pivo);
// System.out.println("排序一趟後的基準位置low=high = ns["+low+"] = "+ns[high]);
// System.out.println("最終排序結果: "+Arrays.toString(ns)+"\n");
// System.out.println("上次排序結果: "+Arrays.toString(ns));
QuickSort(slow,low-1,ns);
QuickSort(low+1,shigh,ns);
}
}
}
Python代碼:
def partition(arr,low,high):
pivot = arr[low]
while (low < high):
while arr[high] > pivot and low < high:
high -=1
arr[low] = arr[high]
while arr[low] < pivot and low < high:
low += 1
arr[high] = arr[low]
arr[high] = pivot
return high
def quicksort(arr, low, high):
if low < high:
next = partition(arr, low, high);
quicksort(arr, low, next-1)
quicksort(arr, next+1, high)
arr = [8,6,1,9,4,5,10,14,3]
length = len(arr)-1
quicksort(arr, 0, length)
print(arr)
算法分析:
先說主定理:
(a>=1,b>1) 其中a是劃分後的子問題數,b是劃分後問題的規模。對於快速排序,a=b=2
f(n)=O(n)是劃分問題的時間複雜度,快速排序爲n
所以快速排序公式然後迭代計算,隨後第次劃分時 = 後的哪一項只有一個T(1)=1了,此時
所以平均時間複雜度爲
最好的情況:快速排序的性能取決於快速排序遞歸樹。遞歸算法執行情況可以用遞歸樹來描述。
下圖是數組{50,10,90,30, 70,40,80,60,20}在快速排序的遞歸過程,樞軸值是50正好遞歸樹是平衡的,性能比較好。
在最優情況下,Partition每次都劃分得很均勻,如果排序n個關鍵字,其遞歸樹的深度就爲.log2n.+1
最壞情況:
在最壞的情況下,待排序的序列爲正序或者逆序,每次劃分只得到一個比上一次劃分少一個記錄的子序列,注意另一個爲空。如果遞歸樹畫出來,它就是一棵斜樹。此時需要執行n‐1次遞歸調用,且第i次劃分需要經過n‐i次關鍵字的比較才能找到第i個記錄,也就是樞軸的位置,因此比較次數爲 ,最終其時間複雜度爲O(n2)。
另一種說法:T[n] = T[n-1] + T[1] + O(n),
問題來了,這一次的劃分白玩了,劃分之後一邊是一個,一邊是n-1個,這種極端情況的時間複雜度就是O(n2).
3.堆排序
思想:
代碼:
算法分析:
優化: