private static int[] arr = {8, 3, 5, 55, 7, 22, 32, 99};
複製代碼
循環28次----移動6次
算法
private static void bubbleSort(int arr[]) {
int n = arr.length - 1;
int i, j, t;
for (i = 0; i < n; i++) {//遍歷數組
for (j = i + 1; j <= n; j++) {//內層遍歷,從i的下一個
if (arr[i] > arr[j]) {// 數組i元素大,則交換
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
}
}
複製代碼
i=0,第一趟
j 從 1 開始,arr[0]>arr[1],交換位置,保證大的數在後面,a[0]=3
j +1 = 2 , 比較a[2]和a[0],arr[0]<arr[2],繼續
而後j +1 =3 ,就這樣依次將j右移和a[0]比較,這樣到最後一個可保證a[0]是最小的
複製代碼
i=1,第二趟
j 從 2 開始,arr[1]>arr[2],交換位置,保證大的數在後面,a[1]=5
j +1 = 2 , 比較a[3]和a[1],arr[1]<arr[3],繼續
而後j +1 =3 ,就這樣依次將j右移和a[0]比較,這樣到最後,可保證a[0],a[1]正確排序
複製代碼
而後同理,通過n趟,小的數移到了前面shell
循環28次----移動6次
數組
private static void bubbleSort1(int[] arr) {
int n = arr.length - 1;
int i, j, t;
for (i = 0; i < n; i++) {
for (j = n; j > i; j--) {
if (arr[j - 1] > arr[j]) {// 相鄰元素兩兩對比,若是前者大,交換位置
t = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = t;
}
}
}
}
複製代碼
i=0,第一趟
相鄰兩個元素比較,小的往左走bash
i=1,第二趟
相鄰兩個元素比較,小的往左走測試
循環22次----移動6次
優化
// 平均T複雜度 穩定 最好 最壞 輔助空間
//冒泡排序: O(n^2) YES O(n) O(n^2) O(1)
private static void bubbleSort2(int[] arr) {
int n = arr.length - 1;
int i, j, t;
boolean flag = true;
for (i = 0; i < n && flag; i++) {
//若是退出j循環時flag=false,說明本循環沒有元素交換,說明已排序完成
flag = false;
for (j = n; j > i; j--) {
if (arr[j - 1] > arr[j]) {// 相鄰元素兩兩對比,若是前者大,交換位置
t = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = t;
flag = true;
}
}
}
}
複製代碼
循環28次----移動6次
ui
// 平均T複雜度 穩定 最好 最壞 輔助空間
//選擇排序: O(n^2) YES O(n^2) O(n^2) O(1)
public static void selectSort(int[] arr) {
int n = arr.length - 1;
int i, j, t, min;
for (i = 0; i < n; i++) {
min = i;
for (j = i + 1; j <= n; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (min != i) {//若是min和i相同,不必交換
t = arr[i];//交換
arr[i] = arr[min];
arr[min] = t;
}
}
}
複製代碼
在j循環中記錄發現的最小值所在索引,若是min不是i,交換元素spa
// 平均T複雜度 穩定 最好 最壞 輔助空間
//插入排序: O(n^2) YES O(n) O(n^2) O(1)
public static int[] insertSort(int[] arr) {
int i, j, t;
int n = arr.length;
for (i = 1; i < n; i++) {//從i點開始遍歷數組
t = arr[i];//使用temp變量記錄i索引所在值
for (j = i; j > 0 && t < arr[j - 1]; j--) {//從i點向左遍歷
arr[j] = arr[j - 1];//當遇到比temp大的數,就將前一個元素後推
}
arr[j] = t;//不然j位變成t
}
return arr;
}
複製代碼
第一輪排序
i,j從索引1開始, t=3 ,
t與j前一個元素比較, 3<8 , j處值變成較大值 8 , j 向左指一位
跳出j的for循環後,將j索引置爲剛纔保存的臨時變量t=3,此時前兩個元素排序完成。
複製代碼
第二輪排序
i索引右移一格到2,t= 5 ,j 從索引2開始
t與j前一個元素比較, 5 < 8 , j處值變成較大值 8 , j 向左指一位
t與j前一個元素比較, 5 > 3 , 跳出j循環
將j索引置爲剛纔保存的臨時變量t=3,此時前3個元素排序完成。
複製代碼
第三輪排序
i索引右移一格到3,t= 55 ,j 從索引3開始
t與j前一個元素比較, 55 > 8 , 跳出j循環
將j索引置爲剛纔保存的臨時變量t=55,此時前4個元素排序完成。
複製代碼
第四輪排序
i索引右移一格到4,t= 7 ,j 從索引4開始
t與j前一個元素比較, 7 <55 , j處值變成較大值 55 , j 向左指一位 爲3
t與j前一個元素比較, 7 < 8 , j處值變成較大值 8 , j 向左指一位 爲2
t與j前一個元素比較, 7 > 5 , 跳出j循環
將j索引置爲剛纔保存的臨時變量t=7,此時前5個元素排序完成。
複製代碼
插入排序第n輪排序後將前n+1個元素是順序的3d
試了一下開篇中100W的隨機整數,果真不愧爲N*logN的算法, 0.365秒code
// 平均T複雜度 穩定 最好 最壞 輔助空間
//希爾排序:O(nlogn) NO O(n^1.3) O(n^2) O(1)
public static void shellSort(int[] arr) {
int i, j, t , gap;
int n = arr.length;
for (gap = n / 2; gap > 0; gap /= 2) {
for (i = gap; i < n; i++) {
t = arr[i];
for (j = i; j >= gap && t < arr[j - gap]; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = t;
}
}
}
複製代碼
希爾排序先將數據歸整,最後gap=1時,至關於歸整後的插入排序
開始gap取4,圖中相同顏色進行比較,用的是插入排序交換的套路
而後gap減半=2,圖中相同顏色進行比較,用的是插入排序交換的套路
而後gap減半=1,圖中相同顏色進行比較,用的是插入排序交換的套路
複製代碼
試了一下開篇中100W的隨機整數,果真不愧爲N*logN的算法, 0.286秒
// 平均T複雜度 穩定 最好 最壞 輔助空間
//堆排序:O(nlogn) NO O(nlogn) O(nlogn) O(1)
private static void heapSort(int[] arr) {//左半的元素
for (int i = arr.length / 2 - 1; i >= 0; i--) {
perDownHeap(arr, i, arr.length);//造成堆
}
for (int i = arr.length - 1; i > 0; i--) {
swapRef(arr, 0, i);//交換首
perDownHeap(arr, 0, i);//維持堆形狀
}
}
private static void swapRef(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
private static void perDownHeap(int[] arr, int i, int n) {
int child;
int t;
for (t = arr[i]; leftChild(i) < n; i = child) {
child = leftChild(i);
if (child != n - 1 && arr[child] < arr[child + 1]) {
child++;
}
if (t < arr[child]) {
arr[i] = arr[child];
} else {
break;
}
}
arr[i] = t;
}
複製代碼
第一次執行的是:perDownHeap(arr, 3, arr.length);
private static void perDownHeap(int[] arr, int i, int n) {
int child;//0
int t;//55
for (t = arr[i]; leftChild(i) < n; i = child) {
child = leftChild(i);//7 arr[child]=99 即 55的左子是99
if (child != n - 1 && arr[child] < arr[child + 1]) {//此時條件不知足
child++;
}
if (t < arr[child]) {//走這裏 55<99 條件知足
arr[i] = arr[child];//arr[3]=99
} else {
break;
}
}
arr[i] = t;//跳出循環, i = child ,arr[7]=t=55
}
第二次執行的是:perDownHeap(arr, 2, arr.length);
private static void perDownHeap(int[] arr, int i, int n) {
int child;//0
int t;//5
for (t = arr[i]; leftChild(i) < n; i = child) {
child = leftChild(i);//5 arr[child]=22 即 5的左子是22 ,右子是arr[child + 1]=32
if (child != n - 1 && arr[child] < arr[child + 1]) {//此時條件知足
child++;//child = 6
}
if (t < arr[child]) {//走這裏 5<32 條件知足
arr[i] = arr[child];//arr[2]=32
} else {
break;
}
}
arr[i] = t;//跳出循環, i = child ,arr[6]=t=5
}
>其餘同上...繪圖以下
複製代碼
接下來經過交換根節點和i,再對堆進行沉浮,造成小頂堆
for (int i = arr.length - 1; i > 0; i--) {
swapRef(arr, 0, i);
perDownHeap(arr, 0, i);
}
複製代碼
private static void mergeSort(int[] arr) {
int[] temArr = new int[arr.length];//臨時數組
mergeSort(arr, temArr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int[] temArr, int left, int right) {
if (left < right) {
int center = (left + right) / 2;
mergeSort(arr, temArr, left, center);//左歸併,使得左子序列有序
mergeSort(arr, temArr, center + 1, right);//右歸併,使得右子序列有序
merge(arr, temArr, left, center + 1, right);//兩個子序列合併
}
}
private static void merge(int[] arr, int[] temArr, int leftPos, int rightPos,
int leftEnd = rightPos - 1;//左起點比右終點大1
int tempPos = leftPos;//臨時數組索引
int count = rightEnd - leftPos + 1;//這次合併的元素個數
while (leftPos <= leftEnd && rightPos <= rightEnd) {//說明到頭了
boolean rightBig = arr[leftPos] <= arr[rightPos];//左邊大?
//temArr[tempPos]取較小者,tempPos和較小者索引++
temArr[tempPos++] = rightBig ? arr[leftPos++] : arr[rightPos++];
}
while (leftPos <= leftEnd) {
temArr[tempPos++] = arr[leftPos++];
}
while (rightPos <= rightEnd) {
temArr[tempPos++] = arr[rightPos++];
}
for (int i = 0; i < count; i++, rightEnd--) {//將臨時數組元素放到原數組
arr[rightEnd] = temArr[rightEnd];
}
}
複製代碼
|--- 第一次merge: merge(arr, temArr, 0, 1, 1)
leftPos:0
leftEnd:0
arr[leftPos]:8
rightPos:1
rightEnd:1
arr[rightPos]:3
temArr[0] = arr[leftPos]和arr[rightPos]的較小者:arr[rightPos]=3
temArr[1] = arr[leftPos++] = 8
將臨時數組元素放到原數組
|--- 第二次merge: merge(arr, temArr, 2, 3, 3)
leftPos:2
leftEnd: 2
arr[leftPos]:5
rightPos:3
rightEnd:3
arr[rightPos]:55
temArr[0] = arr[leftPos]和arr[rightPos]的較小者 arr[leftPos]=5
temArr[1] = arr[rightPos++] = 55
將臨時數組元素放到原數組
|--- 第三次merge: merge(arr, temArr, 0, 2, 3)
leftPos:0
leftEnd: 1
arr[leftPos]:3
rightPos:2
rightEnd:3
arr[rightPos]:5
見下圖...
複製代碼
第三次 merge
// 平均T複雜度 穩定 最好 最壞 輔助空間
//快速排序:O(nlogn) NO O(nlogn) O(n^2) O(n)~O(nlogn)
public static void fastSort(int[] arr) {
fastSort(arr, 0, arr.length - 1);
}
public static void fastSort(int[] arr, int start, int end) {
int i, j, key, t;
if (start >= end) {
return;
}
i = start + 1;
j = end;
key = arr[start];//基準位
while (i < j) {//保證i比j大
//當j處值<=key,j向←--走 :說明從右找到到第一個大於key的數
while (key <= arr[j] && i < j) j--;
//當i處值>=key,i向--→走 :說明從右找到到第一個小於key的數
while (key >= arr[i] && i < j) i++;
if (i < j) {//交換
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
arr[start] = arr[i];//交換基準位
arr[i] = key;
fastSort(arr, start, j - 1);//左半
fastSort(arr, j + 1, end);//右半
}
複製代碼
Q1:第一趟sort
此時調用了:
sort(arr, 0, 2);//對左半元素快速排序
複製代碼
Q1.1:Q1左半sort
Q1.1.1:Q1.1左半sort
Q1.1.2:Q1.1右半sort
:一個元素不用排序,直接return
Q1.2:Q1右半sort
:套路同樣,就不分析了
條目 | 平均T複雜度 | 穩定 | 最好 | 最壞 | 輔助空間 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | YES | O(n) | O(n^2) | O(1) |
選擇排序 | O(n^2) | NO | O(n^2) | O(n^2) | O(1) |
插入排序 | O(n^2) | YES | O(n) | O(n^2) | O(1) |
希爾排序 | O(nlogn) | NO | O(n^1.3) | O(n^2) | O(1) |
堆排序 | O(nlogn) | NO | O(nlogn) | O(nlogn) | O(1) |
歸併排序 | O(nlogn) | YES | O(nlogn) | O(nlogn) | O(n) |
快速排序 | O(nlogn) | NO | O(nlogn) | O(n^2) | O(n)~O(nlogn) |
數據量較小時:冒泡排序,選擇排序,插入排序就能夠了
數據量很是大時:最好用O(nlogn)複雜度的算法
若是數據基本是有序的:插入排序
數據隨機性很大:快速排序
須要要求穩定性且O(nlogn):歸併排序是惟一的選擇,代價是須要多消耗一倍空間
若是不想出現最壞狀況,也就是想一切在預期以內:堆排序是你不二的選擇
複製代碼
排序的穩定性:大小相同的元素在排序先後順序有沒有可能改變
設:Ki=Kj (1<=i<=n,1<=j<=n,i!=j)且在排序前ri先於rj(即i<j)
|--- 排序後ri仍先於rj,則排序方法穩定,反之,不穩定
複製代碼
下面用七張圖記錄一下:
ok,就先這樣,之後有想到什麼再補充