原理:每次循環對比找出最小/大值,將最值的元素交換至左側java
思想:直接選擇排序(Straight Select Sort)算法思想:第一趟從n個元素的數據序列中選出關鍵字最小/大的元素並放在最前/後位置,下一趟從n-1個元素中選出最小/大的元素並放在最前/後位置。以此類推,通過n-1趟完成排序算法
案例分析:數組
一、初始的無序數列 {5,8,6,3,1,7}
,但願對其升序排序post
二、按照思路分析:
優化
內層循環通過一輪對比後找到最小值,min = 1
,下標爲 index = 4
;交換位置指針
public static void straightSelectSort(int[] arr){ //i不須要 = 數組最尾部元素,由於後面沒有值對比了 for (int i = 0; i < arr.length - 1; i++) { //設置每次循環的起始點爲最小/大值 int min = arr[i]; //記錄下最小/大值的下標 int index = i; for (int j = i + 1; j < arr.length; j++) { //升序排序>,降序排序< if (min > arr[j]){ min = arr[j]; index = j; } } //一輪對比完成後,將默認的最小值賦予到找到的最值下標位置 arr[index] = arr[i]; //把找到的真實最值放到前面 arr[i] = min; } }
這裏其實有可能出現默認的最小值其實就是真正的最小值,因此一輪內層循環下來,是沒有交換數據,能夠添加哨兵,若是沒有找到最小值,就不進行值的交換,減小交換次數。code
public static void straightSelectSort(int[] arr){ //i不須要 = 數組最尾部元素,由於後面沒有值對比了 for (int i = 0; i < arr.length - 1; i++) { //設置每次循環的起始點爲最小/大值 int min = arr[i]; //記錄下最小/大值的下標 int index = i; //哨兵,記錄是否找到最值,默認false boolean isSwap = false; for (int j = i + 1; j < arr.length; j++) { //升序排序>,降序排序< if (min > arr[j]){ min = arr[j]; index = j; //找到最值,設置爲true isSwap = true; } } if (isSwap){ //一輪對比完成後,將默認的最小值賦予到找到的最值下標位置 arr[index] = arr[i]; //把找到的真實最值放到前面 arr[i] = min; } } }
直接選擇排序算法複雜度分析:blog
若是數組中有**n個元素排序
第1輪循環是arr[0] 和arr[1] ...arr[n-1] 進行比較,次數爲n-1 次,arr[0]放最值。get
第2輪循環是arr[1] 和 arr[2]...arr[n-1] 進行比較,次數爲n-2次,arr[1]放第二個最值。
因此不可貴出它的比較的次數是n-1 + n-2 + n-3 + ....1 = n*(n-1)/2 。
時間複雜度爲 = n^2/2- n/2 = n^2 ,O(n^2)。
直接選擇排序每一次查找只是找出最小值,能夠這麼改進,查找最小值的同時,找到一個最大值,而後將二者分別放在它們應該出現的位置,這樣遍歷的次數就會減小,同時添加哨兵,若是沒有找到最值,不發生交換
以新的無序數列 {5,1,6,3,9,2,7,0}
爲例,按照上面的分析,初始狀態以下:
排序過程以下:
交換最值,將最小值放到arr[left]
,最大值放到arr[right]
,同時left++,right--
;準備下一輪循環,第一輪結果以下:
算法注意點:
(1) 第二輪開始對比前,咱們能夠發現,此時arr[left]
與arr[right]
剛好是此輪的最值,所以應該加上哨兵,對此狀況,內循環走完後,不進行值交換,判斷條件:min == right && max == left
(2) 特別注意的地方:第三輪循環後,能夠發現的點是,left = 2,right = 5
,而結果是min = 5,max = 2
,仔細看你就發現了,left
與min
對應,而max
與right
對應,結果是值反面的的,因此在進行值交換的時候,進行一次就能夠了,不然交換兩次,就變成了巴黎鐵塔翻過來又翻回去了,判斷條件:min == right && max == left
public static void betterSelectSort(int[] arr) { //left指針指向無序邊界起點,right指針指向終點,temp用做臨時變量交換值 int left,right,temp; //默認指向無序列表起點 left = 0; //默認指向無序列表終點 right = arr.length - 1; //記錄每輪找到的最小值的下標 int min = left; //記錄每輪找到的最大值的下標 int max = right; //當right >= left時,列表已經有序 //記錄循環的次數 int index = 0; while(left < right) { min = left; //每輪開始前,默認無序列表起點爲最小值 max = right; //每輪開始前,默認無序列表終點爲最大值 //指針i從左往右掃描,找出最小值,最大值 for (int i=left; i<=right; i++) { if (arr[i]<arr[min]) { min = i; //經過比較,記錄最小值的下標 } if(arr[i]>arr[max]) { max = i; //經過比較,記錄最大值的下標 } } index++; if (min == left && max == right){ System.out.println("第" + index + "輪循環沒有找到最值,無需交換"); }else if (min == right && max == left){ //交換一次便可,交換兩次的話,序列翻轉,至關於沒有交換 temp = arr[left]; arr[left] = arr[min]; arr[min] = temp; }else { //找到最小值,交換 temp = arr[left]; arr[left] = arr[min]; arr[min] = temp; //找到最大值,交換 temp = arr[right]; arr[right] = arr[max]; arr[max] = temp; } //肯定最小/大值,指針向中間移動 left++;right--; } }
優化後代碼雖然有效的減小了外層循環的次數,但其時間複雜度仍然是O(n^2)
文章爲原創,轉載請聲明出處
更多文章,歡迎點贊關注,個人掘金:https://juejin.im/user/1151943919304840/posts