分治与减治算法实验:题目2 排序中分治法的程序设计

本文介绍了2024年4月实验内容,包括归并排序和快速排序的分治策略,以及它们在C++中的实现。详细讨论了算法的递归过程、合并操作和时间复杂度,指出归并排序为O(nlog2n),快速排序在平均和最好情况下的时间复杂度也是O(nlog2n),但最坏情况为O(n^2)。
摘要由CSDN通过智能技术生成

实验时间

2024年 4 月10日19时至22时

学时数

3

1.实验名称

分治与减治算法实验:题目2 排序中分治法的程序设计

2.实验目的

(1)掌握归并排序和快速排序的划分方法;

(2)掌握归并排序和快速排序的具体分治策略;

(3)在掌握的基础上编程两种排序方法的实现过程。

3.实验内容

给出一个初始序列,分别用归并排序和快速排序两种分治法将所给序列变换为有序序列,输出结果,输出时要求有文字说明。请任选一种语言编写程序实现上述算法,并分析其算法复杂度。

  1. 实验原理

【原理1】应用归并排序方法对一个记录序列进行升序排列。归并排序(merge sort)的分治策略如图所示,描述如下。

(1)划分:将待排序序列r1,r2,…,rn划分为两个长度相等的子序列r1,…,rn/2和rn/2+1,…,rn。

(2)求解子问题:分别对这两个子序列进行排序,得到两个有序子序列。

(3)合并:将这两个有序子序列合并成一个有序序列。

                                                    归并排序的分治思想

 【原理2】应用快速排序方法对一个记录序列进行升序排列。快速排序(quick sort)的分治策略如下,如图所示。

(1)划分:选定一个记录作为轴值,以轴值为基准将整个序列划分为两个子序列r1…ri-1

ri+1rn,轴值的位置i在划分的过程中确定,并且前一个子序列中的记录均小于或等于轴值,后一个子序列中的记录均大于或等于轴值。

(2)求解子问题:分别对划分后的每一个子序列递归处理。

(3)合并:由于对子序列r1…ri-1和ri+1…rn的排序是就地进行的,所以合并不需要执行任何操作。

                                         快速排序的分治思想           

9.实验源代码和伪代码

设对数组r[n]进行升序排序排列,归并排序的递归算法用伪代码描述如下:

算法 归并排序MergeSort

输入:  待排序数组r[n],待排序区间[s,t]

输出:  升序序列r[s]~r[t]

1:  if s == t then

2:          return

3:  end if

4:  else

5:    m ←(s + t) / 2

6:    MergeSort(r, s, m)

7:    MergeSort(r, m + 1, t)  

8:    Merge(r, r1, s, m, t)       

9:  end if

设将两个相邻的有序子序列r[s]~r[m]和r[m+1]~r[t]合并为一个有序序列rl[s]~rl[t],函数Merge实现合并操作。归并排序算法用C++语言描述如下:

#include <iostream>

using namespace std;

void MergeSort(int r[], int s, int t);

void Merge(int r[], int r1[], int s, int m, int t);//函数声明

void MergeSort(int r[], int s, int t) { //对序列r[s]~r[t]进行归并排序

int m, r1[1000]; //数组r1是临时数组,假设最多1000个记录

if (s == t)

return;//递归的边界条件,只有一个记录,已经有序

else {

m = (s + t) / 2; //划分

MergeSort(r, s, m); //求解子问题1,归并排序前半个子序列

MergeSort(r, m + 1, t); //求解子问题2,归并排序后半个子序列

Merge(r, r1, s, m, t); //合并两个有序子序列,结果存在数组r1中

for (int i = s; i <= t; i++) //将有序序列传回数组r中

r[i] = r1[i];

}

}

void Merge(int r[], int r1[], int s, int m, int t) { //合并子序列

int i = s, j = m + 1, k = s;

while (i <= m && j <= t) {

if (r[i] <= r[j])

r1[k++] = r[i++]; //取r[i]和r[j]中较小者放入r1[k]

else

r1[k++] = r[j++];

}

while (i <= m) //若第一个子序列没处理完,则进行收尾处理

r1[k++] = r[i++];

while (j <= t) //若第二个子序列没处理完,则进行收尾处理

r1[k++] = r[j++];

}

int main() {

int r[100], r1[1000], s, t, n, m;

cout << "请输入待排序数组的元素个数:";

cin >> n;

cout << "请输入待排序数组r[" << n << "]:" << endl;

for (int i = 0; i < n; i++) {

cin >> r[i];

}

cout << "请输入待排序区间[s,t]:" << endl;

cin >> s >> t;//数组下标

Merge(r, r1, s, m, t);

MergeSort(r, s, t);

cout << "升序序列r[" << s << "]~r[" << t << "]为:" << endl;

for (int i = s; i <= t; i++) {

cout << r[i] << " ";

}

}

设函数Partition实现对序列r[first]~r[end]进行划分,快速排序对各个子序列的排序是就地进行,不需要合并子问题的解。快速排序算法用C++语言描述如下:

#include <iostream>

using namespace std;

void QuickSort(int r[], int first, int end);

int Partition(int r[], int first, int end);

int Partition(int r[], int first, int end) { //划分

int i = first, j = end;

while (i < j) { //初始化待划分区间

while (i < j && r[i] <= r[j])

j--;//右侧扫描

if (i < j) {

int temp = r[i];

r[i] = r[j];

r[j] = temp; //将较小记录交换到前面

i++;

}

while (i < j && r[i] <= r[j])

i++;//左侧扫描

if (i < j) {

int temp = r[i];

r[i] = r[j];

r[j] = temp; //将较大记录交换到后面

j--;

}

}

return i;//返回轴值记录的位置

}

void QuickSort(int r[], int first, int end) { //快速排序

int pivot;

if (first < end) {

pivot = Partition(r, first, end); //划分,pivot是轴值在序列中的位置

QuickSort(r, first, pivot - 1); //求解子问题1,对左侧子序列进行快速排序

QuickSort(r, pivot + 1, end); //求解子问题2,对右侧子序列进行快速排序

}

}

int main() {

int r[100], first, end, n;

cout << "请输入待排序数组的元素个数:";

cin >> n;

cout << "请输入待排序数组r[" << n << "]:" << endl;

for (int i = 0; i < n; i++) {

cin >> r[i];

}

cout << "请输入待排序区间[first,end]:" << endl;

cin >> first >> end;//数组下标

QuickSort(r, first, end);

Partition(r, first, end);

cout << "升序序列r[" << first << "]~r[" << end << "]为:" << endl;

for (int i = first; i <= end; i++) {

cout << r[i] << " ";

}

}

时间复杂度分析:

归并排序:

设待排序记录个数为n,则执行一趟合并算法的时间复杂性为O(n),所以,归并排序算法存在如下递推式:

根据主定理,归并排序的时间代价是O(nlog2n)。

归并排序的合并步需要将两个相邻的有序子序列合并为一个有序序列,在合并过程中可能会破坏原来的有序序列,所以,合并不能就地进行。归并排序需要与待排序记录序列同样数量的存储空间,以便存放归并结果,因此其空间复杂性为O(n)。

1.如果。等于t,则待排序区间只有一个记录,算法结束;

2.计算划分中点:m=(s+D/2;

3.对前半个子序列r[s]~r[m]进行升序排列:

4.对后半个子序列r[m+1]~r[]进行升序排列;

5.合并两个升序序列r[s]~r[m]和r[m+1]~r[t]:

快速排序:

最好情况下,每次划分对一个记录定位后,该记录的左侧子序列与右侧子序列的长度相同。在具有n个记录的序列中,一次划分需要对整个待划分序列扫描一遍,则所需时间为O(n)。设T(n)是对n个记录的序列进行排序的时间,每次划分后,正好把待划分区间划分为长度相等的两个子序列,则有:

T(n)=2T(n/2)+n=2(2T(n/4)+n/2))+n=4T(n(4)+2n

=4(2T(n/8)+n/4)+2n=8T(n/8)+3n

=nT(1)+nlog2n=O(nlog2n)

最坏情况下,待排序记录序列正序或逆序,每次划分只得到一个比上一次划分少一个记录的子序列(另一个子序列为空)。此时,必须经过n-1次递归调用才能把所有记录定位而且第i趟划分需要经过n-i次比较才能找到第i个记录的位置,因此,时间复杂性为:

n(n-1)=O(n2) 

平均情况下,设轴值记录的关键码第k小(1≤k≤n),则有:

这是快速排序的平均时间性能,可以用归纳法证明,其数量级也为O(nlog2n)。

  1. 实验结论及心得

代码运行截图:

归并排序:

快速排序:

心得:

补充了新的排序方法。

  • 55
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值