作者j0958322080 (Tidus)
看板C_and_CPP
標題[問題] quicksort原地迴圈的寫法
時間Fri Dec 21 19:43:55 2018
開發平台(Platform): (Ex: Win10, Linux, ...) win10
編譯器(Ex: GCC, clang, VC++...)+目標環境(跟開發平台不同的話需列出) gcc 4.8.1
額外使用到的函數庫(Library Used): (Ex: OpenGL, ...) no
問題(Question): 想要寫一個quicksort不用遞迴且不用另外開矩陣的寫法,
不過選pivot後第一次都拆成兩個小陣列結束後就不知道開如何做了。
餵入的資料(Input): 預期的正確結果(Expected Output): 錯誤結果(Wrong Output): 程式碼(Code):(請善用置底文網頁, 記得排版,禁止使用圖檔) void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
void quicksort(int a[], int array_length){
int left_bound = 0, right_bound = array_length-1;
int right = right_bound, left = left_bound, pivot = (left+right)/2;
int i, j;
int new_left_bound, new_right_bound;
int pivot_zero, pivot_one;
while(left < right)
{
if(a[right] > a[pivot])
{
if (a[left] < a[pivot])
{
right--;
left++;
}
else right--;
}
else
{
if (a[left] < a[pivot]) left++;
else
{
swap(&a[left],&a[right]);
right--;
left++;
}
}
if(left == right)
{
new_left_bound = right+1;
new_right_bound = left;
left = left_bound;
right = new_right_bound;
pivot = (left+right)/2;
}
if(left == pivot)break;
printf("%d %d %d\n",right,left,pivot);
}
}
補充說明(Supplement): 感覺這種divide and conquer用迴圈做比較難,
網路上找的版本也幾乎都是遞迴的版本,
除了遞迴比較好寫外有其他原因嗎??
--
!!!!!!!!!!!!!簽名檔破740000點擊率啦!!!!!!!!!!!!!!!
Fw: [問卦] 電影:決勝21點的機率問題
https://goo.gl/2BpbB7 #1MfN3FgZ (joke) → yeebon: chx64的1/2悖論真的很經典呢07/22 16:41
!!!!!!!!!!!!!!簽名檔破740000點擊率啦!!!!!!!!!!!!!!
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 39.9.136.149
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1545392640.A.7A6.html
→ poyenc: 你要怎麼知道有那些區間是 partition 完但還沒 merge 的? 12/21 20:00
→ j0958322080: 原地排列應該沒有合併的問題吧 12/21 22:41
→ poyenc: 簡單說 DaC 還會需要整合結果的步驟, 所以我問 partition 12/21 23:39
→ poyenc: 完, 你去處理完更小的區間以後, 要怎麼回來處理其他區間? 12/21 23:40
→ poyenc: 即使是 in-place quicksort 也有回去處理上個 partition 12/21 23:43
→ poyenc: 剩下來另一半區間的步驟, 也會有整合結果的步驟 (雖然可以 12/21 23:44
→ poyenc: 不做事), 但就跟遞迴呼叫類似, 你要用同份程式碼執行在不 12/21 23:45
→ poyenc: 同情境 (context) 下, 先要有辦法把情境資訊給儲存起來 12/21 23:46
→ j0958322080: 這個問題也是我現在正在想的,目前的code只能一直往 12/21 23:49
→ j0958322080: 左或是一直往右。中間的小陣列初步的想法是設定兩個 12/21 23:49
→ j0958322080: 新的變數存新的邊界去跑,不過這邊就是要想想怎麼寫 12/21 23:49
→ j0958322080: ,寫出來還要在看看是不是O(nlogn) 12/21 23:49
→ j0958322080: 不過merge sort就比較好找到迭代版本的code 12/21 23:50
→ poyenc: 用 LIFO stack 去存當前區間、pivot、階段就可以了 12/22 00:04
→ suhorng: 如果可以接受多 O(log n) 的空間應該還好寫就是了 12/22 09:41
→ suhorng: 另外其實 divide 有很好懂的寫法, 不一定要左右交換 12/22 09:42
→ j0958322080: 感謝兩位的分享,不過我看網路上資料in place的快排 12/22 10:40
→ j0958322080: 才是最快的,其他都比合併排序慢 12/22 10:40
推 b0920075: stack存還沒排過的區間 12/22 16:19
推 suhorng: 還會是 in-place 呀, Quicksort 本來就要 O(log n) 的 12/22 23:55
→ suhorng: 堆疊空間 12/22 23:55
→ suhorng: 實際用的 sorting algorithm 記得都是好幾種的混合 12/23 00:00
→ j0958322080: 但我看網路上的資料是說不需要另外開陣列給他,而且i 12/23 22:50
→ j0958322080: n place應該也只是陣列內的元素交換而已 12/23 22:50
→ poyenc: 那有個問題: 你定義區域變數要不要算進空間複雜度? 你進行 12/23 23:10
→ poyenc: 遞迴呼叫算不算配置記憶體? 12/23 23:10
→ j0958322080: 我目前的想法空間複雜度應該只是常數,但我還沒實現 12/23 23:54
→ poyenc: 再研究一下 in-place algorithm 指的是哪方面的 in-place 12/24 02:32
→ poyenc: 吧, 如果 quicksort 空間複雜度可以到常數你就可以得獎了 12/24 02:34
→ j0958322080: 那如果只是單純在原陣列中排序為何還需要其他空間 12/24 09:17
→ j0958322080: 喔看來是我誤會in-place的意思了 12/24 09:25
推 suhorng: 那個 O(log n) 是遞迴的堆疊空間, 在原本的 quicksort 12/24 13:40
→ suhorng: 就有. 手寫堆疊多多少少可以變迴圈的形式, 而且 12/24 13:41
→ suhorng: quicksort 的遞迴方式很簡單, 寫出來應該也不會太難懂 12/24 13:41
→ suhorng: 當然我個人是喜歡寫成遞迴的形式. 遞迴跟歸納法一樣, 12/24 13:42
→ suhorng: 遞迴呼叫想成 "由歸納假設", 就可以把陣列排好了 12/24 13:42
→ j0958322080: 我看過msort的遞迴確實比迭代簡單,qsort遞迴也蠻好 12/24 14:22
→ j0958322080: 寫的,但是個人是喜歡迭代 12/24 14:22
→ poyenc: O(log N) 不是專指遞迴需要的空間, 而是為了 divide & 12/24 15:01
→ poyenc: conquer 需要記住的資訊量, 你用 iterative 因為也要記錄 12/24 15:02
→ poyenc: 相同資訊, 所以免不了這部分的空間複雜度. recursive 只是 12/24 15:03
→ poyenc: 把資訊存在 call stack 上, 這樣的分別而已 12/24 15:03
→ poyenc: 把遞迴跟 divide & conquer 劃上等號就錯了, 應該從了解演 12/24 15:14
→ poyenc: 算法本身著手. divide 我也可以開好多條 thread 去作, 討 12/24 15:16
→ poyenc: 論實作根本無助於理解這個 O(log N) 12/24 15:16
→ poyenc: hint: O(log N) 跟演算法是 top-down 有關 12/24 15:37
→ poyenc: 你會覺得 iterative merge sort 實作很好理解的原因在於即 12/24 16:26
→ poyenc: 使用 bottom-up approach 去實作也很容易, 因為邊界都可以 12/24 16:27
→ poyenc: 在一開始的時候全算出來, 但對於 top-down 的演算法, 想要 12/24 16:27
→ poyenc: 把 "分出子問題" 這件事跟 "解決問題本身" 成本相仿, 你如 12/24 16:30
→ poyenc: 實作出完全迴圈版, 會發現成本都在儲存子問題資訊上面, 12/24 16:31
→ poyenc: 空間複雜度 O(N log N), 為了蒐集資訊需額外付出時間成本 12/24 16:32
→ poyenc: O(N log N) 12/24 16:32
→ suhorng: 你說得對 12/24 23:19