歸並排序(Merge sort)


很多的算法都是遞歸的結構,遞歸的目的呢,是在自己調用自己的時候,將問題分解成更小的問題,這個過程也叫做divide-and-conquer,通過把原來的問題的一個大問題,分解成一個更小的問題,再把更小的問題分解成微不足道的問題,再一一解決所有所有的問題。
devide-and-conquer一般是這樣來解決問題的:
Divide:將問題分解成更小的,但是相似的問題
Conquer:遞歸解決這些小問題,如果小問題已經足夠小,直接解決;否則可以重復Divide的過程
Combine:將解決小問題的方案合並和大的解決方案中,從而解決更大的問題
今天要說的歸並排序,就是這樣的一種模式。

歸並排序算法

Divide:將n個要排序的元素分成兩半,各占n/2
Conquer:用歸並排序分別對兩個n/2的元素排序
Combine:將兩個排序的結果合並(Merge),產出最終結果
這是一個遞歸的過程,那遞歸到什么時候是個頭呢?
當被分割的序列長度是1了,就該結束了,一個元素就不用排序了吧。
設想一個我們有
MERGE(A, p, q, r)
A就是我們要排序的序列
p,q,r都是A的index,滿足條件p<=q<r,我們假設A[p..q]和A[q+1..r]是已經排序排好的,是有序的數組,於是我們總共有n個元素需要排序n = r - p + 1。
還是用打牌的觀點來說,假如我們現在把一副牌洗亂,然后分成兩堆,左右各一半。然后分別將左右兩堆按從小到大排序,牌面均向上。
現在,我們想把這兩堆牌合並在一起,並且是有序疊放的。就可以這樣做:

  • 比較左邊和右邊的牌,取小的那一張,拿出來,扣在桌子上。
  • 再次比較兩堆牌,取較小的一張,拿出來,扣在桌子上
  • 重復上面的動作,知道所有的牌都合並在一起。
    這就是歸並排序。
    可是這里面有個小問題,但某些情況下,可以左邊已經沒有牌了,右邊還有一堆。這時候去比較左右兩邊的時候,是不是要先檢查一下是否還有牌呢?
    舉個例子,左邊的牌是
    1,3,4,5
    右邊的牌是
    2,6,7,8
    我們來做歸並排序:
  1. 比較左右兩堆,取出最小的1,扣在桌上
  2. 比較左右兩堆,取出最小的2,扣在桌上
  3. 比較左右兩堆,取出最小的3,扣在桌上
  4. 比較左右兩堆,取出最小的4,扣在桌上
  5. 比較左右兩堆,取出最小的5,扣在桌上
    現在呢,1,2,3,4,5都排好序了,左邊牌堆沒有牌了。右邊還有3張。那比較左右兩邊的時候,左邊不就是null了?
    這個時候呢,可以考慮弄個正無窮∞出來,兩個牌堆是
    1,3,4,5,∞和2,6,7,8,∞那就不用判斷了。
    根據這個思想,得出我們的偽代碼。

偽代碼

Merge(left, right)
	let result[1..left.length+right.length] be new arrays
	n = 0
	m = 0
	while n < left.length and m < right.length
		if left[n] <= right [m]
			result.add(left[n])
			n = n + 1
		else
			result.add(right[m])
			m = m + 1
	result.add(left[n..left.length])//如果還有剩下,加入左邊剩余的部分
	result.add(right[m..right.length])//如果右邊還有剩下,加入右邊部分
return result

但是,怎么體現遞歸呢?
這兒還得有一段

Merge-Sort(seq, p, r)
	if seq.length <=1
		return seq
	middle = seq.length / 2
	left = Merge-Sort(seq[1..middle])
	right = Merge-Sort(seq[middle..seq.length])
	return Merge-Sort(left, right)

Python實現

def merge(left, right):
	result = []
	n, m = 0, 0
	while n < len(left) and m < len(right):
		if left[n] <= right[m]:
			result.append(left[n])
			n += 1
		else:
			result.append(right[m])
			m += 1
	
	result += left[n:]
	result += right[m:]
	return result

def sort(seq):
	if len(seq) <= 1:
		return seq

	middle = int(len(seq) / 2)
	left = sort(seq[:middle])
	right = sort(seq[middle:])
	return merge(left, right)

源碼:Github-Syler-Fun-Merge-Sort-Python

Java實現

		public static void sort(int[] seq, int left, int right) {
        if (left < right) {
            int middle = (left + right) / 2;
            sort(seq, left, middle);
            sort(seq, middle + 1, right);
            merge(seq, left, right);
        }
    }

    public static void merge(int[] seq, int l, int r) {
        int mid = (l + r) / 2;
        int i = l;
        int j = mid + 1;
        int count = 0;
        int temp[] = new int[r - l + 1];
        while (i <= mid && j <= r) {
            if (seq[i] < seq[j]) {
                temp[count++] = seq[i++];
            } else {
                temp[count++] = seq[j++];
            }
        }
        while (i <= mid) {
            temp[count++] = seq[i++];
        }
        while (j <= r) {
            temp[count++] = seq[j++];
        }
        count = 0;
        while (l <= r) {
            seq[l++] = temp[count++];
        }
    }

源碼:Github-Syler-Fun-Merge-Sort-Java


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM