歸併排序

首先先講講分治思想。分治即將原問題分解爲幾個規模較小但類似於原問題的子問題,遞歸求解這些子問題,然後再合併這些子問題的解來建立問題的解。(《算法導論》中的原話)

知道了分治,那麼該如何實現呢。根據上面的描述,我們要解決的幾個主要的問題是:

1.如何分解原問題

2.如何進行遞歸求解。因爲遞歸都要有個盡頭,所以這裏我們還要定義一個最小的子問題

3.如何合併那些已經解決了的子問題。


代碼:

MergeSort.h

#pragma once

class MergeSort
{
private:
	int* data;		// 數據
	int length;		// 數據個數
public:
	MergeSort(int* &d, int size) : data(d), length(size) {}
	~MergeSort(void){
		if(data != nullptr)
		{
			delete data;
			data = nullptr;
		}
	}

	void output() const;
	void sort();
	void swap(int i, int j);

private:
	void mergeSort(int p, int q);
	void merge(int p, int q, int r);
};

MergeSort.cpp

#include "MergeSort.h"
#include <iostream>

void MergeSort::output() const
{
	int i = 0;
	while(i < length)
	{
		std::cout<<data[i]<<" ";
		i++;
	}
	std::cout<<"\n";
}

void MergeSort::sort()
{
	mergeSort(0, length);
}

/*
* 分解原問題,將數組分爲左部分和右部分。那麼我們要分割的最小情況是,左邊爲
* 只有一個元素的數組,右邊也是隻有一個元素的數組。
* @param p 爲左邊的起始索引,包含p
* @param r 爲右邊的終止索引,不包含r
*/
void MergeSort::mergeSort(int p, int r)
{
	if(r - p >= 2)
	{
		std::cout<<p<<":"<<r<<std::endl;
		int q = (p + r) / 2;  // 取中點
		mergeSort(p, q);      // 對左邊進行遞歸
		mergeSort(q, r);  // 對右邊進行遞歸
		merge(p, q, r);       // 合併左右兩邊,則p,r之間的元素已排序好
	}
}

/*
* 寫代碼的時候在意細節也許是好事。比如,我在寫的時候喜歡帶入數字計算q-p是不是
* 就算出正確的數組元素個數。但是這樣即使自己帶入的數字能計算出正確的結果,這也
* 只是讓自己暫時安心。
* 當疑惑時,應該先明確自己定的每個參數表達的意義,然後再去驗證,這樣作可以加深
* 自己對代碼的理解。事倍功半。
*/
void MergeSort::merge(int p, int q, int r)
{
	int n1 = q - p;  // 左邊數組元素的個數
	int n2 = r - q;  // 右邊數組元素的個數

	int* lArr = new int[n1 + 1]; // +1個元素是爲了在數組的末尾增加一個哨兵
	int* rArr = new int[n2 + 1];

	for(int i = 0; i < n1; i++)
	{
		lArr[i] = data[p + i];
	}

	for(int j = 0; j < n2; j++)
	{
		rArr[j] = data[q + j];
	}

	lArr[n1] = RAND_MAX;  // 哨兵來了
	rArr[n2] = RAND_MAX;

	int lIndex = 0, rIndex = 0;
	for(int k = p; k < r; k++)
	{
		if(lArr[lIndex] <= rArr[rIndex])  // 有了哨兵,比較的時候就不用擔心數組越界
		{                                 // 同時簡化了代碼
			data[k] = lArr[lIndex];
			lIndex++;
		}
		else
		{
			data[k] = rArr[rIndex];
			rIndex++;
		}
	}
}

// 數值交換方法,只對整型數據有效
void MergeSort::swap(int i, int j)
{
	data[i] ^= data[j];
	data[j] ^= data[i];
	data[i] ^= data[j];
}

Main.cpp

#include <iostream>
#include <time.h>
#include "MergeSort.h"

// 獲取[1,500]範圍內的隨機數
int proRandNum() 
{
	return rand() % 500 + 1;
}

int main()
{
	using namespace std;
	srand((unsigned)time(NULL)); // 設置隨機數種子

	int size = 20;
	int *data = new int[size];
	for(int i = 0; i < size; i++)
	{
		data[i] = proRandNum();
	}

	MergeSort* sort = new MergeSort(data, size);
	cout<<"before sorted:\n";
	sort->output();
	cout<<"after sorted:\n";
	sort->sort();
	sort->output();
	system("pause");
	return 0;
}


發佈了23 篇原創文章 · 獲贊 14 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章