日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法之线性时间选择(最坏情况下)

發布時間:2023/12/9 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法之线性时间选择(最坏情况下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自:https://www.cnblogs.com/Not-Famous/p/3653945.html

也可參考:https://www.jianshu.com/p/bf47d7b4a43d 或 https://www.bbsmax.com/A/D854V0EQzE/

線性時間選擇(Linear Select): 這個名字不太好理解,什么叫線性時間選擇?一句話,在線性時間內完成選擇。一般情況下是這樣的,我們想要找出一個數組中的最大值或最小值,那就只需要一次排列,然后輸出第一個或最后一個元素就行了,但如果是要找出一個數組中的第 k 小的元素呢?

在一般情況下,可以用 RandomizedSelect 方法來找出第 k 小的元素,平均時間是 O (n),但在最壞情況下,所用的時間則是 n^2,因此,本文討論的就是在最壞情況下,如何在 O (n) 時間內完成選擇。算法的思路總體有些復雜,但每一步其實不難,下面即給大家介紹最壞情況下的線性時間選擇算法。

(1):將 n 個輸入元素以每組 5 個地劃分,共劃分出 (n/5) 個組,每個組分別進行排列,找出中位數,然后按照每個組的順序,把每個組的中位數與整個數組的前 (n/5) 個數交換;

(2):那么,前 (n/5) 個數就是各組的中位數了,然后,我們通過 select 方法找出這些中位數的中位數,以這個中位數的中位數為基準,調用 partition 方法;

(3):調用了 partition 方法后的基準元素正是處于數組的正確位置(前邊的元素都比基準元素小,后邊的元素都比基準元素大),記下基準元素前邊的元素個數 leftNum,如果 k 小于或等于 leftNum,則在基準位置前的這部分調用 select 方法即可,如果在 k 大于 leftNum,則在基準位置后的這部分調用 select 方法。

下面,我直接把代碼貼出,讀者可以通過我的注釋來理解每一步的意義。
復制代碼

1 private static int select(int[] a,int l,int r,int k){2 if(r - l < 75){3 insertSort(a, l, r); //用快速排序進行排序4 return a[l + k - 1];5 }6 int group = (r-l+5)/5;7 for(int i = 0;i<group;i++){8 int left = l+5*i;9 int right = (l + i * 5 + 4) > r ? r : l + i * 5 + 4; //如果超出右邊界就用右邊界賦值 10 int mid = (left+right)/2; 11 insertSort(a, left, right); 12 swap(a, l + i, mid); // 將各組中位數與前i個 13 } 14 int pivot = select(a,l,l+group-1,(group+1)/2); //找出中位數的中位數 15 int p = partition(a,l,r,pivot); //用中位數的中位數作為基準的位置 16 int leftNum = p - l; //leftNum用來記錄基準位置的前邊的元素個數 17 if (k == leftNum + 1) 18 return a[p]; 19 else if (k <= leftNum) 20 return select(a, l, p - 1, k); 21 else //若k在基準位子的后邊,則要從基準位置的后邊數起,即第(k - leftNum - 1)個 22 return select(a, p + 1, r, k - leftNum - 1); 23 }

復制代碼

到此大家也可以看出,這里的 partition 方法與前邊講到過的快速排序所用到的 partition 方法稍有不同,參數個數都變了,但其實變化只是很小,只是取消了一開始定義基準位置的步驟而已,代碼如下:
復制代碼

1 private static int partition(int[] a,int l,int r,int pivot){ //適用于線性時間選擇的partition方法2 int i = l;3 int j = r;4 while(true){5 while(a[i] <= pivot && i < r)6 ++i; //i一直向后移動,直到出現a[i]>pivot7 while(a[j] > pivot)8 --j; //j一直向前移動,直到出現a[j]<pivot9 if(i >= j) break; 10 swap(a,i,j); 11 } 12 a[l] = a[j]; 13 a[j] = pivot; 14 return j; 15 }

復制代碼

下面是 select 方法中,如果輸入規模小于 75 時用到的插入排序算法代碼:
復制代碼

1 private static void insertSort(int[] a, int law, int high) { //插入排序2 for (int i = law + 1; i <= high; i++) { 3 int key = a[i]; 4 int j = i - 1; 5 while (j >= law && a[j] > key) { 6 a[j + 1] = a[j]; 7 j--; 8 } 9 a[j + 1] = key; 10 } 11 }

復制代碼

適用于數組元素之間的 swap 方法如下:

1 private static void swap(int[] a,int i,int j){ 2 int temp = a[i]; 3 a[i] = a[j]; 4 a[j] = temp; 5 }

各位可能有個疑問,為什么輸入規模不足 75 時調用插入排序而不用線性時間選擇呢?那是因為當輸入規模不足 75 時,因為輸入規模太小,時間復雜度幾乎是一個常量,因此沒有必要用到比較復雜的線性時間選擇算法。

我還看到一個比較好懂的學習線性時間選擇的動畫,能形象地看到線性時間選擇的執行過程,鏈接如下:

http://resource.jingpinke.com/details?uuid=ff808081-22e8911b-0122-e8912643-048d&objectId=oid:ff808081-22e8911b-0122-e8912643-048e

如果有不足之處或者對該算法有更好的建議,請提出!

總結

以上是生活随笔為你收集整理的算法之线性时间选择(最坏情况下)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。