07. 归并排序
1. 基本思想:
是将两个或两个以上有序表合并成一个新的有序表。假设初始序列含有n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列;在此基础上,再进行两两归并,如此重复,直至得到一个长度为n的有序序列为止。这种方法被称作2-路归并排序。
采用了分治法的思想,分而治之,每个子问题的最优解最终组成大问题的最优解。
2. 归并操作:
把两个已排序的序列从第一个元素开始比较,小的放到新的序列表,把指针移动到下一个元素,如此比较直到一个序列指针指向空(一个序列已经全部加入新序列),把另一个序列的剩余元素加入到新序列尾部。
例如:序列{5,3,9,7,6,1,0}
初始状态:5,3,9,7,1,6,0
第一次归并:{3,5}{7,9}{1,6}{0} 比较3次
第二次归并:{3,5,7,9}{0,1,6} 比较3次
第三次归并:{0,1,3,5,6,7,9} 比较5次
总比较次数:N = 3+3+5 = 11次
3. 算法描述
- 先申请一个可以放置两个序列大小的空间,作为这两个序列合并后的新序列。
- 用两个指针分别指向这两个序列的第一个元素。
- 比较两个指针指的元素的值,把值小的添加到新序列中,并把该指针向后移动一个。
- 重复第3步,直到一个指针指向空(即一个序列已经全部添加),把另一个序列的剩余元素逐个添加到新序列末尾。
4. Java实现代码
public class MergeSort {
// 自定义实现一次归并排序的函数
void merge(Node[] r, Node[] s, int x1, int x2, int x3) {
int i, j, k;
i = x1; // 第一部分的开始位置
j = x2 + 1; // 第二部分的开始位置
k = x1;
while (i <= x2 && j <= x3) {
// 当i和j都在两个要合并的部分中时
if (r[i].key <= r[j].key) {
// 筛选两部分中较小的元素放到数组s中
s[k] = r[i];
i++;
k++;
} else {
s[k] = r[j];
j++;
k++;
}
}
// 将x1~x2范围内未比较的数顺次加到数组s中
while (i <= x2) {
s[k++] = r[i++];
}
// 将x2+1~x3范围内未比较的数顺次加到数组s中
while (j <= x3) {
s[k++] = r[j++];
}
}
// 归并排序主函数
void mergeSort(Node[] r, Node[] s, int m, int n) {
int p = 0;
Node[] t = new Node[20];
if (m == n) {
s[m] = r[m];
} else {
p = (m + n) / 2;
// 递归调用mergeSort方法将r[m]~r[p]归并成有序的t[m]~t[p]
mergeSort(r, t, m, p);
// 递归调用mergeSort方法将r[p+1]~r[n]归并成有序的t[p+1]~t[n]
mergeSort(r, t, p + 1, n);
// 调用merge方法将前两部分归并到s[m]~s[n]
merge(t, s, m, p, n);
}
}
}
5. 算法分析
时间效率:O(nlogn)
空间复杂度:T(n)
稳 定 性: 稳 定
网友评论