1. 程式人生 > >算法:快速排序

算法:快速排序

tar 比較 oca lua blog net for 算法實現 pri

算法參考:快速排序(三種算法實現和非遞歸實現)

上面這篇文章對快排講解得不錯。快排概念詳述請點上面鏈接

記錄一下,用lua實現的快排,以及一些註意的地方。

交換函數:

function Swap(tab, i, j)
    local temp = tab[i];
    tab[i] = tab[j];
    tab[j] = temp;
end

一、左右指針法:

-- 左右指針法
-- 最後一個數為樞軸
function PartSort(tab, left, right)

    local key = right;
    while
left < right do -- 必須先由左向右遍歷 while left < right and tab[left] <= tab[key] do left = left + 1; end -- 然後由右向左遍歷 while left < right and tab[right] >= tab[key] do right = right - 1; end if left < right then
Swap(tab, left, right); else Swap(tab, left, key); end end return left; end

以最後一個值為key的話,必須從左邊開始遍歷。為什麽必須從左邊開始遍歷?因為是以最右的元素做樞軸。回答這個問題之前,首先要知道為什麽內層的while的循環還要 left < right 判斷,這是因為不加的話,left就有可能和key重合了(如果key是當前片段最大的數),然後left再加1就越界了。 好,然後當while循環有了這個判斷之後,外層循環的最後,必然會走到left == right。而如果不考慮特殊情況,一遍排序的最後,就是key跳到片段中間把片段分成小於key和大於key的兩個子片段,而從左邊開始,left和right最終停留的地方就是大於key的結點,交換left和key的位置,剛好把大於key的這個值調到了後面的片段。而如果從右開始,則停留在了比key小的地方,交換後就把一個比key小的值調到了片段後面。(這個地方我也只是意會,不曉得怎麽用通俗語言描述出來,望指教)。同理,如果以最左的元素作為樞軸,那就是要從右邊開始遍歷。

二、挖坑法:

-- 挖坑法
function PartSort(tab, left, right)
    local sign = tab[left];
    while left < right do 
        while left < right and tab[right] >= sign do 
            right = right - 1;
        end
        tab[left] = tab[right];
        while left < right and tab[left] <= sign do 
            left = left + 1;
        end
        tab[right] = tab[left];
    end
    tab[left] = sign;

    return left;
end

挖坑法,其實叫成“填坑法”更好理解?把該數據結構想象成一排箱子,先空出第一個箱子(把裏面的值放旁邊做參考),然後從右邊開始查找,有小於參考的就填到空箱中。此時,右邊就多了一個空箱(此空箱的右邊,如果有,都是大於參考的值),再從左邊開始查找,如果有大於參考的值就填到右邊的空箱中,如此反復。到最後,左邊查找的指針和左邊查找的指針相遇了,就把最先提出來的值填到最後的空箱中。

三、前後指針法:

-- 前後指針
-- 以最右元素為key
function PartSort(tab, left, right)
    if left < right then 
        local cur = left;
        local pre = left - 1;
        while cur < right do 
            -- 如果cur比最右的元素大,就不會進入這個if語句,pre就停滯了(停滯在第一個比right大的元素的前一個位置)
            if tab[cur] < tab[right] then
                -- pre + 1 有兩種結果
                -- 1. pre+1 = cur,說明pre 一直跟在cur後面,沒有停滯過,即pre和cur之間,全是小於right的元素
                -- 2. pre+1 < cur, 說明pre 停滯過,沒有跟上cur,也就是說pre和cur之間有大於right的元素,由於是當cur遇到大於right的值pre就停滯了
                --    所以,pre指向的還是小於right的元素,停滯的後一個元素(pre+1)才是大於right的元素。
                --    把cur指向的小於right的元素和pre+1大於right的元素對調。此時的pre=pre+1指向的元素又成了小於right的元素了
                pre = pre + 1;
                if pre ~= cur then 
                    Swap(tab, pre, cur);
                end
            end
            cur = cur + 1;
        end
        -- 由上可知,pre+1是指向大於right的元素,把它和right交換位置
        -- 即把right放到了pre=pre+1的位置,就得到了以right元素為分界的 左邊小於right, 右邊大於right的兩個部分
        pre = pre + 1;
        Swap(tab, pre, right);
        return pre;
    end
    return -1;
end

這種方法比較有趣了。註釋比較多,就不啰嗦了。

快排算法有點忘了,復習一下,感謝開頭提到的文章的博主。整理的不錯。

測試代碼:

--local nums = {8, 1, 7, 6, 5, 13, 1, 3, 6, 25, 87, 9, 3, 6, 8, 17, 22, 102, 7, 31, 6};
local nums = {8, 1, 7, 6, 5, 13};

function QuickSort(tab, left, right)
    if left >= right then 
        return;
    end
    local index = PartSort(tab, left, right);
   -- print(index);
    QuickSort(tab, left, index - 1);
    QuickSort(tab, index + 1, right);
end


QuickSort(nums, 1, #nums);
for _, v in ipairs(nums) do print(v); end

算法:快速排序