[問題] quicksort原地迴圈的寫法

作者: j0958322080 (Tidus)   2018-12-21 19:43:55
開發平台(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
作者: yeebon   2017-07-22 16:41:00
chx64的1/2悖論真的很經典呢
作者: poyenc (髮箍)   2018-12-21 20:00:00
你要怎麼知道有那些區間是 partition 完但還沒 merge 的?
作者: j0958322080 (Tidus)   2018-12-21 22:41:00
原地排列應該沒有合併的問題吧
作者: poyenc (髮箍)   2018-12-21 23:39:00
簡單說 DaC 還會需要整合結果的步驟, 所以我問 partition完, 你去處理完更小的區間以後, 要怎麼回來處理其他區間?即使是 in-place quicksort 也有回去處理上個 partition剩下來另一半區間的步驟, 也會有整合結果的步驟 (雖然可以不做事), 但就跟遞迴呼叫類似, 你要用同份程式碼執行在不同情境 (context) 下, 先要有辦法把情境資訊給儲存起來
作者: j0958322080 (Tidus)   2018-12-21 23:49:00
這個問題也是我現在正在想的,目前的code只能一直往左或是一直往右。中間的小陣列初步的想法是設定兩個新的變數存新的邊界去跑,不過這邊就是要想想怎麼寫,寫出來還要在看看是不是O(nlogn)不過merge sort就比較好找到迭代版本的code
作者: poyenc (髮箍)   2018-12-22 00:04:00
用 LIFO stack 去存當前區間、pivot、階段就可以了這邊有兩種實作可以對照看 https://bit.ly/2T2HEOH
作者: suhorng ( )   2018-12-22 09:41:00
如果可以接受多 O(log n) 的空間應該還好寫就是了另外其實 divide 有很好懂的寫法, 不一定要左右交換
作者: j0958322080 (Tidus)   2018-12-22 10:40:00
感謝兩位的分享,不過我看網路上資料in place的快排才是最快的,其他都比合併排序慢
作者: yeebon   2017-07-23 00:41:00
chx64的1/2悖論真的很經典呢
作者: poyenc (髮箍)   2018-12-22 04:00:00
你要怎麼知道有那些區間是 partition 完但還沒 merge 的?
作者: j0958322080 (Tidus)   2018-12-22 06:41:00
原地排列應該沒有合併的問題吧
作者: poyenc (髮箍)   2018-12-22 07:39:00
簡單說 DaC 還會需要整合結果的步驟, 所以我問 partition完, 你去處理完更小的區間以後, 要怎麼回來處理其他區間?即使是 in-place quicksort 也有回去處理上個 partition剩下來另一半區間的步驟, 也會有整合結果的步驟 (雖然可以不做事), 但就跟遞迴呼叫類似, 你要用同份程式碼執行在不同情境 (context) 下, 先要有辦法把情境資訊給儲存起來
作者: j0958322080 (Tidus)   2018-12-22 07:49:00
這個問題也是我現在正在想的,目前的code只能一直往左或是一直往右。中間的小陣列初步的想法是設定兩個新的變數存新的邊界去跑,不過這邊就是要想想怎麼寫,寫出來還要在看看是不是O(nlogn)不過merge sort就比較好找到迭代版本的code
作者: poyenc (髮箍)   2018-12-22 08:04:00
用 LIFO stack 去存當前區間、pivot、階段就可以了這邊有兩種實作可以對照看 https://bit.ly/2T2HEOH
作者: suhorng ( )   2018-12-22 17:41:00
如果可以接受多 O(log n) 的空間應該還好寫就是了另外其實 divide 有很好懂的寫法, 不一定要左右交換
作者: j0958322080 (Tidus)   2018-12-22 18:40:00
感謝兩位的分享,不過我看網路上資料in place的快排才是最快的,其他都比合併排序慢
作者: b0920075 (Void)   2018-12-23 00:19:00
stack存還沒排過的區間
作者: suhorng ( )   2018-12-23 07:55:00
還會是 in-place 呀, Quicksort 本來就要 O(log n) 的堆疊空間實際用的 sorting algorithm 記得都是好幾種的混合
作者: b0920075 (Void)   2018-12-22 16:19:00
stack存還沒排過的區間
作者: suhorng ( )   2018-12-22 23:55:00
還會是 in-place 呀, Quicksort 本來就要 O(log n) 的堆疊空間實際用的 sorting algorithm 記得都是好幾種的混合
作者: j0958322080 (Tidus)   2018-12-23 22:50:00
但我看網路上的資料是說不需要另外開陣列給他,而且in place應該也只是陣列內的元素交換而已
作者: poyenc (髮箍)   2018-12-23 23:10:00
那有個問題: 你定義區域變數要不要算進空間複雜度? 你進行遞迴呼叫算不算配置記憶體?
作者: j0958322080 (Tidus)   2018-12-23 23:52:00
我找到的資料。 https://reurl.cc/moQQ7我目前的想法空間複雜度應該只是常數,但我還沒實現
作者: poyenc (髮箍)   2018-12-24 02:32:00
再研究一下 in-place algorithm 指的是哪方面的 in-place吧, 如果 quicksort 空間複雜度可以到常數你就可以得獎了
作者: j0958322080 (Tidus)   2018-12-24 09:17:00
那如果只是單純在原陣列中排序為何還需要其他空間喔看來是我誤會in-place的意思了
作者: suhorng ( )   2018-12-24 13:40:00
那個 O(log n) 是遞迴的堆疊空間, 在原本的 quicksort就有. 手寫堆疊多多少少可以變迴圈的形式, 而且quicksort 的遞迴方式很簡單, 寫出來應該也不會太難懂當然我個人是喜歡寫成遞迴的形式. 遞迴跟歸納法一樣,遞迴呼叫想成 "由歸納假設", 就可以把陣列排好了
作者: j0958322080 (Tidus)   2018-12-24 14:22:00
我看過msort的遞迴確實比迭代簡單,qsort遞迴也蠻好寫的,但是個人是喜歡迭代
作者: poyenc (髮箍)   2018-12-24 15:01:00
O(log N) 不是專指遞迴需要的空間, 而是為了 divide &conquer 需要記住的資訊量, 你用 iterative 因為也要記錄相同資訊, 所以免不了這部分的空間複雜度. recursive 只是把資訊存在 call stack 上, 這樣的分別而已把遞迴跟 divide & conquer 劃上等號就錯了, 應該從了解演算法本身著手. divide 我也可以開好多條 thread 去作, 討論實作根本無助於理解這個 O(log N)hint: O(log N) 跟演算法是 top-down 有關你會覺得 iterative merge sort 實作很好理解的原因在於即使用 bottom-up approach 去實作也很容易, 因為邊界都可以在一開始的時候全算出來, 但對於 top-down 的演算法, 想要把 "分出子問題" 這件事跟 "解決問題本身" 成本相仿, 你如實作出完全迴圈版, 會發現成本都在儲存子問題資訊上面,空間複雜度 O(N log N), 為了蒐集資訊需額外付出時間成本O(N log N)
作者: suhorng ( )   2018-12-24 23:19:00
你說得對
作者: j0958322080 (Tidus)   2018-12-24 06:50:00
但我看網路上的資料是說不需要另外開陣列給他,而且in place應該也只是陣列內的元素交換而已
作者: poyenc (髮箍)   2018-12-24 07:10:00
那有個問題: 你定義區域變數要不要算進空間複雜度? 你進行遞迴呼叫算不算配置記憶體?
作者: j0958322080 (Tidus)   2018-12-24 07:52:00
我找到的資料。 https://reurl.cc/moQQ7我目前的想法空間複雜度應該只是常數,但我還沒實現
作者: poyenc (髮箍)   2018-12-24 10:32:00
再研究一下 in-place algorithm 指的是哪方面的 in-place吧, 如果 quicksort 空間複雜度可以到常數你就可以得獎了
作者: j0958322080 (Tidus)   2018-12-24 17:17:00
那如果只是單純在原陣列中排序為何還需要其他空間喔看來是我誤會in-place的意思了
作者: suhorng ( )   2018-12-24 21:40:00
那個 O(log n) 是遞迴的堆疊空間, 在原本的 quicksort就有. 手寫堆疊多多少少可以變迴圈的形式, 而且quicksort 的遞迴方式很簡單, 寫出來應該也不會太難懂當然我個人是喜歡寫成遞迴的形式. 遞迴跟歸納法一樣,遞迴呼叫想成 "由歸納假設", 就可以把陣列排好了
作者: j0958322080 (Tidus)   2018-12-24 22:22:00
我看過msort的遞迴確實比迭代簡單,qsort遞迴也蠻好寫的,但是個人是喜歡迭代
作者: poyenc (髮箍)   2018-12-24 23:01:00
O(log N) 不是專指遞迴需要的空間, 而是為了 divide &conquer 需要記住的資訊量, 你用 iterative 因為也要記錄相同資訊, 所以免不了這部分的空間複雜度. recursive 只是把資訊存在 call stack 上, 這樣的分別而已把遞迴跟 divide & conquer 劃上等號就錯了, 應該從了解演算法本身著手. divide 我也可以開好多條 thread 去作, 討論實作根本無助於理解這個 O(log N)hint: O(log N) 跟演算法是 top-down 有關你會覺得 iterative merge sort 實作很好理解的原因在於即使用 bottom-up approach 去實作也很容易, 因為邊界都可以在一開始的時候全算出來, 但對於 top-down 的演算法, 想要把 "分出子問題" 這件事跟 "解決問題本身" 成本相仿, 你如實作出完全迴圈版, 會發現成本都在儲存子問題資訊上面,空間複雜度 O(N log N), 為了蒐集資訊需額外付出時間成本O(N log N)
作者: suhorng ( )   2018-12-25 07:19:00
你說得對

Links booklink

Contact Us: admin [ a t ] ucptt.com