歸併排序簡述:
歸併排序是排序算法的一種新的思路,旨在把兩個或以上有序的數列歸併一個有序的數列,是爲歸併。
假如有一個含有n個元素的數組,將其看作n個有序列,然後兩兩歸併,最終得到一個有序列。
排序前的準備:
#define MAXSIZE 10
//順序表結構
template <class T>
struct SqList
{
T s[MAXSIZE + 1] = { NULL, 98, 24, 55, 81, 32, 77, 48, 60, 14 ,8}; //數組s,s[0]用作哨兵
int length = MAXSIZE; //s長度
void PrintArray();
};
typedef SqList<int> SList;
//交換l中的數組中下標爲i和j的值
void Swap(SList *L, int i, int j)
{
int temp = L->s[i];
L->s[i] = L->s[j];
L->s[j] = temp;
}
template <class T>
void SqList<T>::PrintArray()
{
for (int i = 1; i <= MAXSIZE; i++)
{
cout << s[i] << " ";
}
cout << endl;
}
準備了一個默認元素的數組用於排序。
遞歸實現歸併排序:
C++實現代碼如下:
//歸併排序主函數
void MSort(int array[], int res[], int m, int n)
{
int pivot; //臨時變量
int tempArray[MAXSIZE + 1]; //臨時數組 這裏的MAXSIZE默認爲10
if (m == n)
{
res[m] = array[m];
}
else
{
pivot = (m + n) / 2; //中間下標,把數組array平分爲[m...pivot]和[pivot+1...n]
MSort(array, tempArray,m, pivot); //先把數組[m...pivot]遞歸歸併到有序數組tempArray[m,pivot]
MSort(array, tempArray, pivot + 1, n); //把數組[pivot+1,n]遞歸歸併到有序數組tempArray[pivot+1,n]
Merge(tempArray, res, m, pivot, n);//把有序數組tempArray[m,pivot]和有序數組tempArray[pivot+1,n]歸併爲一個新的有序數組
}
}
這個函數的作用在於把一個無序數列,不斷遞歸分割爲兩半,最終得到n個單個元素,然後單個元素分別遞歸歸併成有序數列。
其中的Merge函數實現如下:
//歸併
//兩個有序數組歸併爲一個有序數組
void Merge(int tempArray[], int res[], int m, int pivot, int n)
{
int i, r; //臨時變量用於遍歷
//不斷比較,將較小的數放到目標數組的前面
for (i = pivot + 1,r = m;i <= n && m <= pivot; r++) //i從pivot+1開始遍歷,r一開始等於m並作爲res數組的下標
{
if (tempArray[m]<tempArray[i]) //比較臨時數組的第1位[m]和[pivot+1]位的元素大小
{
res[r] = tempArray[m++]; //小於的話,就把m下標元素放在res第一位,並且m+1
}
else
{
res[r] = tempArray[i++];
}
}
//上述比較後,會有剩餘的數組元素
//把剩餘數組元素複製到res數組
if (m<=pivot)
{
for (int l = 0; l <= pivot-m; l++)
{
res[r + l] = tempArray[m + l];
}
}
if (i<=n)
{
for (int l = 0; l <= n-i; l++)
{
res[r + l] = tempArray[i + l];
}
}
}
最後可以用一個函數來調用主函數,作爲最終的函數方便使用:
//歸併排序
void MergeSort(SList *L)
{
MSort(L->s, L->s, 1, L->length);
}
迭代實現歸併排序:
迭代仍然使用到了上面的Merge函數,用於歸併。
//歸併排序迭代實現
void MergeSort2(SList *L)
{
int *resA = new int[L->length]; //用於存放最終得到的有序表
int pivot = 1; //變量記錄有序表長度
while (pivot < L->length) //不斷循環直到有序表長度大於原表
{
MergeIterate(L->s,resA,pivot,L->length);
pivot *= 2; //自乘兩倍
MergeIterate(resA,L->s,pivot,L->length);
pivot *= 2;
}
}
//將array[]中長度爲pivot的序列,兩兩進行歸併,結果放到res[]
void MergeIterate(int array[], int res[], int pivot, int length)
{
int i = 1,j;
while (i<= length-2*pivot+1)
{
Merge(array, res, i, i + pivot - 1, i + 2 * pivot - 1);
i = i + 2 * pivot;
}
if (i<length - pivot + 1)
{
Merge(array, res, i, i + pivot - 1, length);
}
else
{
for (j = i; j <= length; j++)
{
res[j] = array[j];
}
}
}
歸併排序的時間複雜度:
歸併排序的最好最壞和平均時間複雜度都爲O(nlogn),迭代方法的空間複雜度爲O(n)
迭代方法的效率要比遞歸更高,並且歸併排序是一種穩定的的排序算法。