北邮数据结构实验 第三次实验 排序
北邮计算机网络实践第三次实验报告
计算机网络技术实践实验报告实验名称RIP和OSPF路由协议的配置及协议流程姓名___(-…-)_____________实验日期:2014年4月11日学号_ 242_______实验报告日期:2014年4月16日报告退发:( 订正、重做)一.环境(详细说明运行的操作系统,网络平台,网络拓扑图)1.运行操作系统由于本人电脑上的操作系统是WIN7旗舰版,尝试直接安装Dynamips模拟器,但一直没有成功。
于是在电脑上安装了VMware Workstation,并安装WINXP虚拟操作系统。
在WINXP虚拟操作系统上安装Dynamips模拟器,才顺利完成了实验环境的搭建。
2.网络平台Dynamips模拟器3.网络拓扑图PC2二.实验目的✧复习和进一步掌握实验一二的内容。
✧学会设计较复杂的网络物理拓扑和逻辑网段。
✧掌握路由器上RIP协议的配置方法,能够在模拟环境中进行路由器上RIP协议的配置,并能通过debug信息来分析RIP协议的工作过程,并观察配置水平分割和没有配置水平分割两种情况下RIP协议工作过程的变化。
✧掌握路由器上OSPF协议的配置方法,能够在模拟环境中上进行路由器上OSPF协议的配置,并能够通过debug信息分析OSPF协议的工作工程。
三.实验内容及步骤(包括主要配置流程,重要部分需要截图)1.实验前的基础设置(实验一、二)2.物理拓扑设计修改.net 文件,设计物理拓扑,修改后的.net文件见附录。
3.逻辑网段设计3.1.用list命令列出所有网络成员,如下图所示。
3.2.输入命令start/all启动所有网络成员配置路由器以及PC的idle-pc value并保存。
3.3.同过telnet登陆到主机或路由器打开八个控制台窗口,每个窗口都使用telnet登陆到路由器或主机上。
例如登陆到PC1 的命令为telnet 127.0.0.1 3001,登陆到RT1的命令为telnet 127.0.0.1 30023.4.配置路由器之间的串口并启动串口配置从底层向高层配置,先配置物理层的时钟信息,再配置数据链路层协议,最后配置网络层的IP协议,并开启串口。
北邮数据结构实验报告
北邮数据结构实验报告北京邮电大学信息与通信工程学院2009级数据结构实验报告实验名称:实验三哈夫曼编/解码器的实现学生姓名:陈聪捷日期:2010年11月28日1.实验要求一、实验目的:了解哈夫曼树的思想和相关概念;二、实验内容:利用二叉树结构实现哈夫曼编/解码器1.初始化:能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树。
2.建立编码表:利用已经建好的哈夫曼树进行编码,并将每个字符的编码输出。
3.编码:根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4.译码:利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。
5.打印:以直观的方式打印哈夫曼树。
6.计算输入的字符串编码前和编码后的长度,并进行分析,讨论哈夫曼编码的压缩效果。
7.用户界面可以设计成“菜单”方式,能进行交互,根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2.程序分析2.1存储结构二叉树templateclassBiTree{public:BiTree();//构造函数,其前序序列由键盘输入~BiTree(void);//析构函数BiNode*Getroot();//获得指向根结点的指针protected:BiNode*root;//指向根结点的头指针};//声明类BiTree及定义结构BiNodeData:二叉树是由一个根结点和两棵互不相交的左右子树构成data:HCode*HCodeTable;//编码表inttSize;//编码表中的总字符数二叉树的节点结构templatestructBiNode//二叉树的结点结构{Tdata;//记录数据Tlchild;//左孩子Trchild;//右孩子Tparent;//双亲};编码表的节点结构structHCode{chardata;//编码表中的字符charcode[100];//该字符对应的编码};待编码字符串由键盘输入,输入时用链表存储,链表节点为structNode{charcharacter;//输入的字符unsignedintcount;//该字符的权值boolused;//建立树的时候该字符是否使用过Node*next;//保存下一个节点的地址};示意图:2.2关键算法分析1.初始化函数(voidHuffmanTree::Init(stringInput))算法伪代码:1.初始化链表的头结点2.获得输入字符串的第一个字符,并将其插入到链表尾部,n=1(n 记录的是链表中字符的个数)3.从字符串第2个字符开始,逐个取出字符串中的字符3.1将当前取出的字符与链表中已经存在的字符逐个比较,如果当前取出的字符与链表中已经存在的某个字符相同,则链表中该字符的权值加1。
北邮数据结构实验3哈夫曼编码
数据结构实验报告实验名称:实验3——哈夫曼编码学生姓名:班级:班内序号:学号:日期:2013年11月24日1.实验要求利用二叉树结构实现赫夫曼编/解码器。
基本要求:1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树2、建立编码表(CreateTable):利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):以直观的方式打印赫夫曼树(选作)6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
2. 程序分析2.1存储结构:struct HNode{char c;//存字符内容int weight;int lchild, rchild, parent;};struct HCode{char data;char code[100];}; //字符及其编码结构class Huffman{private:HNode* huffTree; //Huffman树HCode* HCodeTable; //Huffman编码表public:Huffman(void);void CreateHTree(int a[], int n); //创建huffman树void CreateCodeTable(char b[], int n); //创建编码表void Encode(char *s, string *d); //编码void Decode(char *s, char *d); //解码void differ(char *,int n);char str2[100];//数组中不同的字符组成的串int dif;//str2[]的大小~Huffman(void);};结点结构为如下所示:三叉树的节点结构:struct HNode//哈夫曼树结点的结构体{ int weight;//结点权值int parent;//双亲指针int lchild;//左孩子指针int rchild;//右孩子指针char data;//字符};示意图为:int weight int parent int lchild int rchild Char c 编码表节点结构:struct HCode//编码表结构体{char data;//字符char code[100];//编码内容};示意图为:基本结构体记录字符和出现次数:struct node{int num;char data;};示意图为:2.关键算法分析(1).初始化:伪代码:1.输入需要编译的文本内容2.将输入的内容保存到数组str1中3.统计出现的字符数目,并且保存到变量count中4.统计出现的不同的字符,存到str2中,将str2的大小存到dif中时间复杂度O(n!)(2).创建哈夫曼树算法伪代码:1.创建一个长度为2*n-1的三叉链表2.将存储字符及其权值的链表中的字符逐个写入三叉链表的前n个结点的data域,并将对应结点的孩子域和双亲域赋为空3.从三叉链表的第n个结点开始,3.1从存储字符及其权值的链表中取出两个权值最小的结点x,y,记录其下标x,y。
数据结构实验三实验报告
数据结构实验三实验报告数据结构实验三实验报告一、实验目的本次实验的目的是通过实践掌握树的基本操作和应用。
具体来说,我们需要实现一个树的数据结构,并对其进行插入、删除、查找等操作,同时还需要实现树的遍历算法,包括先序、中序和后序遍历。
二、实验原理树是一种非线性的数据结构,由结点和边组成。
树的每个结点都可以有多个子结点,但是每个结点只有一个父结点,除了根结点外。
树的基本操作包括插入、删除和查找。
在本次实验中,我们采用二叉树作为实现树的数据结构。
二叉树是一种特殊的树,每个结点最多只有两个子结点。
根据二叉树的特点,我们可以使用递归的方式实现树的插入、删除和查找操作。
三、实验过程1. 实现树的数据结构首先,我们需要定义树的结点类,包括结点值、左子结点和右子结点。
然后,我们可以定义树的类,包括根结点和相应的操作方法,如插入、删除和查找。
2. 实现插入操作插入操作是将一个新的结点添加到树中的过程。
我们可以通过递归的方式实现插入操作。
具体来说,如果要插入的值小于当前结点的值,则将其插入到左子树中;如果要插入的值大于当前结点的值,则将其插入到右子树中。
如果当前结点为空,则将新的结点作为当前结点。
3. 实现删除操作删除操作是将指定的结点从树中移除的过程。
我们同样可以通过递归的方式实现删除操作。
具体来说,如果要删除的值小于当前结点的值,则在左子树中继续查找;如果要删除的值大于当前结点的值,则在右子树中继续查找。
如果要删除的值等于当前结点的值,则有三种情况:- 当前结点没有子结点:直接将当前结点置为空。
- 当前结点只有一个子结点:将当前结点的子结点替代当前结点。
- 当前结点有两个子结点:找到当前结点右子树中的最小值,将其替代当前结点,并在右子树中删除该最小值。
4. 实现查找操作查找操作是在树中寻找指定值的过程。
同样可以通过递归的方式实现查找操作。
具体来说,如果要查找的值小于当前结点的值,则在左子树中继续查找;如果要查找的值大于当前结点的值,则在右子树中继续查找。
数据结构实验报告-排序
数据结构实验报告-排序一、实验目的本实验旨在探究不同的排序算法在处理大数据量时的效率和性能表现,并对比它们的优缺点。
二、实验内容本次实验共选择了三种常见的排序算法:冒泡排序、快速排序和归并排序。
三个算法将在同一组随机生成的数据集上进行排序,并记录其性能指标,包括排序时间和所占用的内存空间。
三、实验步骤1. 数据的生成在实验开始前,首先生成一组随机数据作为排序的输入。
定义一个具有大数据量的数组,并随机生成一组在指定范围内的整数,用于后续排序算法的比较。
2. 冒泡排序冒泡排序是一种简单直观的排序算法。
其基本思想是从待排序的数据序列中逐个比较相邻元素的大小,并依次交换,从而将最大(或最小)的元素冒泡到序列的末尾。
重复该过程直到所有数据排序完成。
3. 快速排序快速排序是一种分治策略的排序算法,效率较高。
它将待排序的序列划分成两个子序列,其中一个子序列的所有元素都小于等于另一个子序列的所有元素。
然后对两个子序列分别递归地进行快速排序。
4. 归并排序归并排序是一种稳定的排序算法,使用分治策略将序列拆分成较小的子序列,然后递归地对子序列进行排序,最后再将子序列合并成有序的输出序列。
归并排序相对于其他算法的优势在于其稳定性和对大数据量的高效处理。
四、实验结果经过多次实验,我们得到了以下结果:1. 冒泡排序在数据量较小时,冒泡排序表现良好,但随着数据规模的增大,其性能明显下降。
排序时间随数据量的增长呈平方级别增加。
2. 快速排序相比冒泡排序,快速排序在大数据量下的表现更佳。
它的排序时间线性增长,且具有较低的内存占用。
3. 归并排序归并排序在各种数据规模下都有较好的表现。
它的排序时间与数据量呈对数级别增长,且对内存的使用相对较高。
五、实验分析根据实验结果,我们可以得出以下结论:1. 冒泡排序适用于数据较小的排序任务,但面对大数据量时表现较差,不推荐用于处理大规模数据。
2. 快速排序是一种高效的排序算法,适用于各种数据规模。
北邮数据结构实验报告
北邮数据结构实验报告摘要:本报告基于北邮数据结构实验,通过实际操作和实验结果的分析,总结和讨论了各实验的目的、实验过程、实验结果以及相关的问题和解决方法。
本报告旨在帮助读者了解数据结构实验的基本原理和应用,并为今后的学习和研究提供参考。
1. 实验一:线性表的操作1.1 实验目的本实验旨在掌握线性表的基本操作以及对应的算法实现,包括插入、删除、查找、修改等。
1.2 实验过程我们使用C++语言编写了线性表的相关算法,并在实际编程环境下进行了测试。
通过插入元素、删除元素、查找元素和修改元素的操作,验证了算法的正确性和效率。
1.3 实验结果经过测试,我们发现线性表的插入和删除操作的时间复杂度为O(n),查找操作的时间复杂度为O(n),修改操作的时间复杂度为O(1)。
这些结果与预期相符,并反映了线性表的基本特性。
1.4 问题与解决方法在实验过程中,我们遇到了一些问题,例如插入操作的边界条件判断、删除操作时的内存释放等。
通过仔细分析问题,我们优化了算法的实现,并解决了这些问题。
2. 实验二:栈和队列的应用2.1 实验目的本实验旨在掌握栈和队列的基本原理、操作和应用,并进行实际编程实现。
2.2 实验过程我们使用C++语言编写了栈和队列的相关算法,并在实际编程环境下进行了测试。
通过栈的应用实现表达式求值和逆波兰表达式的计算,以及队列的应用实现图的广度优先遍历,验证了算法的正确性和效率。
2.3 实验结果经过测试,我们发现栈的应用可以实现表达式的求值和逆波兰表达式的计算,队列的应用可以实现图的广度优先遍历。
这些结果证明了栈和队列在实际应用中的重要性和有效性。
2.4 问题与解决方法在实验过程中,我们遇到了一些问题,例如中缀表达式转后缀表达式的算法设计、表达式求值的优化等。
通过查阅资料和与同学的讨论,我们解决了这些问题,并完善了算法的实现。
3. 实验三:串的模式匹配3.1 实验目的本实验旨在掌握串的基本操作和模式匹配算法,并进行实际编程实现。
北京邮电大学 数据结构 实验三 简单数组排序的设计
数据结构实验报告实验名称:实验3——简单数组实现排序学生姓名:XXXXNB班级:XXXXXXXX班内序号:学号:XXXXXXXX日期:2016年XXXXXXX1.实验要求使用简单数组实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、希尔排序3、冒泡排序4、快速排序5、简单选择排序6、堆排序(选作)7、归并排序(选作)8、基数排序(选作)9、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
//3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试线性表的正确性。
2.程序分析在题目中要求测试不同的数据,可以手动输入待排序元素。
我在编程时为了界面的简洁易于监控,便选择了在主程序中把数组写好放入主程序里。
同时为了方便每种算法的比较,我将所有的函数包装为一个类,方便运行并监控。
比较遗憾的是,我没有做算法运行时间的相关程序,为了弥补这种缺陷,我选择了将界面做的更人性化一些。
2.1 存储结构采用数组进行顺序存储结构示意图如下:2.2 关键算法分析核心算法思想:1. 利用教材讲述的基本算法思想,实现其中五种排序算法,统计其运行相关数据。
2. 将五种排序函数封装到一个类中,使得程序代码可读性、结构更加优化。
排序算法分析:1.插入排序算法插入排序的思想是:每次从无序区取一元素将其添加到有序区中。
a.直接插入排序C++描述如下,其中形参a[]为待排序数组,n为待排序元素个数同时添加两个变量move,compare用于记录整个排序过程中的比较次数以及移动次数void Sort::DirectInsertSort(int a[], int n)//直接插入排序{int k;int move = 0;int compare=0;for (int i = 2; i <= n; i++)//第一个定然为有序,出现两个数据时开始进行排序{if (a[i] < a[i - 1]){a[0] = a[i];move++;//元素赋值移动+1for (int j = i - 1; j > 0 && a[0] < a[j]; j-- ){a[j + 1] = a[j];move++;//移动+1compare++;//for条件中比较+1k = j;//记录查找出的位置}a[k] = a[0];move++;//元素赋值移动+1}compare++;//if比较n大小+1}cout << "移动次数" << move << endl;cout << "比较次数" << compare << endl;}b.希尔排序希尔排序,设待排序对象序列有n个元素,先取d<n,比如d=n/2,作为间隔,将全部对象分为d个子序列,对每一个子序列分别进行直接插入排序,然后缩小间隔d,即减少子序列个数例如取d=d/2,重复子序列的划分和排序工作,直到取d=1,仅有一个子序列为止(ps:本质上仍为直接插入排序的改进)C++描述如下,其中形参a[]为待排序数组,n为待排序元素个数同时添加两个变量move,compare用于记录整个排序过程中的比较次数以及移动次数void Sort::ShellInsert(int a[], int n)//希尔排序,本质上为直接插入排序的改进{int k;int move = 0;int compare = 0;for (int d = n / 2; d >= 1; d = d / 2){for (int i = d + 1; i <= n; i++)//前d个元素是每个子序列头部,为初始有序区,从d + 1开始循环{if (a[i] < a[i - d])//并不是单独分别对各个子集分别排序,而是依次从每个子集第二个元素排起{a[0] = a[i];move++;//赋值移动+1for (int j = i - d; j > 0 && a[j] > a[0]; j = j - d){a[j + d] = a[j];move++;//元素后移+1compare++;//for循环中条件比较+1k = j;}a[k] = a[0];move++;//元素赋值移动+1}compare++;//if中比较+1}}cout << "移动次数" << move << endl;cout << "比较次数" << compare << endl;}//希尔排序利用了直接排序的两个特点:1.基本有序直接插入最快 2.记录个数很少的无序序列,直接插入也很快。
北邮数据结构实验第三次实验排序
数据结构实验报告1.实验要求(1)实验目的通过选择下面两个题目之一,学习、实现、对比各种排序算法,掌握各种排序算法的优劣,以及各种算法使用的情况。
(2)实验内容使用简单数组实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、希尔排序3、冒泡排序4、快速排序5、简单选择排序6、堆排序(选作)7、归并排序(选作)8、基数排序(选作)9、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试排序算法的正确性。
2. 程序分析2.1 存储结构顺序表:示意图:2.2 关键算法分析(1)测试数据的产生:正序、逆序、随机数据用两个数组实现乱序、顺序以及逆序数据的排序。
基本思想为:随机序列产生一个指定长度的乱序序列,然后通过memcpy()函数拷贝到第二个数组里,第二个数组作为乱序序列的保存数组,每次对第一个数组进行排序,之后拷贝第二个数组中的乱序序列到第一个数组,实现各次乱序排列。
只要算法正确(第一步可以检验),之后顺序排列只需反复对第一个数组进行操作即可,再后用第二个数组保存逆序数组,然后同样在每次排序之后复制第二数组存储的乱序序列到第一组,对第一组反复排序即可。
<1> pRandom1=new long int[Max+1];pRandom2=new long int[Max+1];<2> srand((unsigned)time(NULL)); for(int i = 1; i <= Max;i++ ) pRandom2[i]=rand();<3> memcpy(obj.pRandom1,obj.pRandom2,(Max+1)*sizeof(long int));(2)排序算法:<1>插入排序:依次将待排序的序列中的每一个记录插入到先前排序好的序列中,直到全部记录排序完毕。
北邮数据结构第三次实验-实验报告
数据结构实验报告实验名称:实验三——栈和队列学生姓名:班级:班内序号:学号:日期:1.实验要求1.1 实验目的通过选择下面两个题目之一进行实现,掌握如下内容:➢掌握二叉树基本操作的实现方法➢了解赫夫曼树的思想和相关概念➢学习使用二叉树解决实际问题的能力1.2 实验内容根据二叉树的抽象数据类型的定义,使用二叉链表实现一个二叉树。
二叉树的基本功能:1、二叉树的建立2、前序遍历二叉树3、中序遍历二叉树4、后序遍历二叉树5、按层序遍历二叉树6、求二叉树的深度7、求指定结点到根的路径8、二叉树的销毁9、其他:自定义操作编写测试main()函数测试线性表的正确性2. 程序分析2.1 二叉链表2.2 二叉树的二叉链表存储示意图2.3 关键算法分析2.3.1算法1:void create(Binode<T> *&R, T data[], int i);[1] 算法功能:创建一个二叉树[2] 算法基本思想:通过构造函数创建一个二叉树,构造函数通过调用函数create()创建二叉树,关于函数create()的伪代码:1.定义根指针,输入节点储存的data,若输入“#”,则该节点为空;2.申请一个新节点,判断它的父结点是否不为空,如果不为空在判断其为左或者右孩子,并把地址付给父结点,把data写入。
[3] 算法空间、时间复杂度:O(n)[4] 代码逻辑(可用伪代码描述):if(data[i-1]!=0){R = new Binode<T>;R->data= data[i-1];R->lch = R->rch = NULL;create(R->lch, data,2*i);create(R->rch, data, 2*i+1);}2.3.2算法2:void Destroy(Binode<T> *R);[1] 算法功能:二叉树的销毁[2] 算法基本思想:采用后序遍历的方法,释放节点。
数据结构第三次实验报告概况
2013-2014 学年第 1 学期 任课老师: 刘安丰 课程名称 实验名称 实验环境 C++ 实验目的和内容要求 数据结构 班级 学号 实验时间 姓名 12 月 5 号
实验三
图的操作算法
作算法
实现图的常用操作算法:包括建立图的存储结构、深度优先搜索和广度优先搜索,求图 的最小生成树、拓扑排序、最短路径等。 二、实验目的 1.掌握图的基本存储方法。 2.掌握有关图的操作算法并用高级语言实现。 3.熟练掌握图的两种搜索路径的遍历方法。 4. 掌握图的有关应用。
G.arcs[j][i].adj = G.arcs[i][j].adj; } return OK; } int LocateVex(MGraph G,char ch) //确定节点 ch 在图 G.vexs 中的位置 { int a ; for(int i=0; i<G.vexnum; i++) { if(G.vexs[i] == ch) a=i; } return a; } //typedef struct Pnode //用于普利姆算法 //{ // char adjvex; //节点 // double lowcost; //权值 //}Pnode,Closedge[MAX_VERTEX_NUM]; //记录顶点集 U 到 V-U 的代价最小的边的辅助数组定义 void MiniSpanTree_PRIM(MGraph G,char u)//普利姆算法求最小生成树 { int i,j,k; Closedge closedge; k = LocateVex(G,u); for(j=0; j<G.vexnum; j++) { if(j != k) { closedge[j].adjvex = u; closedge[j].lowcost = G.arcs[k][j].adj; } } closedge[k].lowcost = 0; for(i=1; i<G.vexnum; i++) { k = Minimum(G,closedge); cout<<"("<<closedge[k].adjvex<<","<<G.vexs[k]<<","<<closedge[k].lowcost<<")"<<endl; closedge[k].lowcost = 0; for(j=0; j<G.vexnum; ++j) { if(G.arcs[k][j].adj < closedge[j].lowcost) { closedge[j].adjvex = G.vexs[k]; closedge[j].lowcost= G.arcs[k][j].adj; } } }
《数据结构》实验报告——排序
《数据结构》实验报告排序实验题目:输入十个数,从插入排序,快速排序,选择排序三类算法中各选一种编程实现。
实验所使用的数据结构内容及编程思路:1.插入排序:直接插入排序的基本操作是,将一个记录到已排好序的有序表中,从而得到一个新的,记录增一得有序表。
一般情况下,第i趟直接插入排序的操作为:在含有i-1个记录的有序子序列r[1..i-1]中插入一个记录r[i]后,变成含有i个记录的有序子序列r[1..i];并且,和顺序查找类似,为了在查找插入位置的过程中避免数组下标出界,在r[0]处设置哨兵。
在自i-1起往前搜索的过程中,可以同时后移记录。
整个排序过程为进行n-1趟插入,即:先将序列中的第一个记录看成是一个有序的子序列,然后从第2个记录起逐个进行插入,直至整个序列变成按关键字非递减有序序列为止。
2.快速排序:基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
假设待排序的序列为{L.r[s],L.r[s+1],…L.r[t]},首先任意选取一个记录(通常可选第一个记录L.r[s])作为枢轴(或支点)(pivot),然后按下述原则重新排列其余记录:将所有关键字较它小的记录都安置在它的位置之前,将所有关键字较大的记录都安置在它的位置之后。
由此可以该“枢轴”记录最后所罗的位置i作为界线,将序列{L.r[s],…,L.r[t]}分割成两个子序列{L.r[i+1],L.[i+2],…,L.r[t]}。
这个过程称为一趟快速排序,或一次划分。
一趟快速排序的具体做法是:附设两个指针low和high,他们的初值分别为low和high,设枢轴记录的关键字为pivotkey,则首先从high所指位置起向前搜索找到第一个关键字小于pivotkey的记录和枢轴记录互相交换,然后从low所指位置起向后搜索,找到第一个关键字大于pivotkey的记录和枢轴记录互相交换,重复这两不直至low=high为止。
北邮数据结构实验—链表排序
数据结构实验报告实验名称:________链表排序___________ 学生姓名:_____________________班级:________________班内序号:_________________________ 学号:________________日期:_______________1.实验要求使用链表实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、冒泡排序3、快速排序4、简单选择排序5、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试排序算法的正确性2. 程序分析2.1 存储结构双向链表2.2 程序流程(或程序结构、或类关系图等表明程序构成的内容,一般为流程图等)2.3 关键算法分析定义节点:struct Node{int data;Node* next;Node* period;};插入排序:void InsertionSort(Node*q,int n){Node*first=q;Node*teq=q;Node*p=q->next;Node*point=p;int w,time=0,compar=0;while(p!=NULL){compar+=1;if(p->data<p->period->data){w=p->data;teq=p->period;while(teq!=NULL){compar+=1;if(teq->data>w){teq->next->data=teq->data;time+=1;if(teq->period==NULL){teq->data=w;time+=1;}}else{teq->next->data=w;time+=1;break;}teq=teq->period;}compar-=1;}p=p->next;}cout<<"插入排序后序列:";while(first!=NULL){cout<<first->data<<" ";first=first->next;}cout<<"\n插入排序比较次数为:"<<compar<<endl;cout<<"插入排序移动次数为:"<<time<<endl;}将链表分为有序区和无序区,分别将无序区的每一个元素插入到有序区的相应位置。
北邮信通院数据结构实验报告三哈夫曼编码器
数据结构实验报告实验名称:实验三树——哈夫曼编/解码器学生姓名:班级:班内序号:学号:日期:2014年12月11日1.实验要求利用二叉树结构实现赫夫曼编/解码器。
基本要求:1、初始化(Init):能够对输入得任意长度得字符串s进行统计,统计每个字符得频度,并建立赫夫曼树2、建立编码表(CreateTable):利用已经建好得赫夫曼树进行编码,并将每个字符得编码输出。
3、编码(Encoding):根据编码表对输入得字符串进行编码,并将编码后得字符串输出。
4、译码(Decoding):利用已经建好得赫夫曼树对编码后得字符串进行译码,并输出译码结果。
5、打印(Print):以直观得方式打印赫夫曼树(选作)6、计算输入得字符串编码前与编码后得长度,并进行分析,讨论赫夫曼编码得压缩效果。
测试数据:I lovedata Structure, I loveputer。
I willtrymy best tostudy data Structure、提示:1、用户界面可以设计为“菜单”方式:能够进行交互。
2、根据输入得字符串中每个字符出现得次数统计频度,对没有出现得ﻩ字符一律不用编码。
2、程序分析2、1存储结构Huffman树给定一组具有确定权值得叶子结点,可以构造出不同得二叉树,其中带权路径长度最小得二叉树称为Huffman树,也叫做最优二叉树。
weightlchildrchild parent2-1-1-15-1-1-16-1-1-17-1-1-19-1-1-1weight lchild rchildparent 2-1-155-1-156-1-167-1-169-1-17701713238165482967-12、2 关键算法分析(1)计算出现字符得权值利用ASCII码统计出现字符得次数,再将未出现得字符进行筛选,将出现得字符及頻数存储在数组a[]中。
void Huffman::Init(){ﻩintnNum[256]= {0};//记录每一个字符出现得次数int ch = cin、get();int i=0;ﻩwhile((ch!='\r') && (ch!='\n'))ﻩ{ﻩﻩnNum[ch]++; //统计字符出现得次数ﻩstr[i++] = ch; //记录原始字符串ﻩch = cin、get(); //读取下一个字符ﻩ}str[i]='\0';n = 0;for ( i=0;i<256;i++)ﻩ{ﻩﻩif(nNum[i]>0) //若nNum[i]==0,字符未出现ﻩ{l[n] = (char)i;ﻩa[n] = nNum[i];n++;ﻩ}}}时间复杂度为O(1);(2)创建哈夫曼树:算法过程:Huffman树采用顺序存储---数组;数组得前n个结点存储叶子结点,然后就是分支结点,最后就是根结点;首先初始化叶子结点元素—循环实现;以循环结构,实现分支结点得合成,合成规则按照huffman树构成规则进行。
北邮数据结构实验报告-排序
北邮数据结构实验报告-排序北邮数据结构实验报告-排序一、实验目的本实验旨在掌握常见的排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序等,并通过实际编程实现对数字序列的排序。
二、实验内容1.冒泡排序冒泡排序是一种简单的排序算法,其基本思想是依次比较相邻的两个元素,并按照从小到大或从大到小的顺序交换。
具体步骤如下:- 从待排序序列的第一个元素开始,依次比较相邻的两个元素;- 如果前面的元素大于后面的元素,则交换这两个元素的位置;- 重复上述步骤,直到整个序列有序。
2.插入排序插入排序是一种简单且直观的排序算法,其基本思想是将待排序序列分为已排序和未排序两部分,每次从未排序部分中选择一个元素插入到已排序部分的合适位置。
具体步骤如下:- 从待排序序列中选择一个元素作为已排序部分的第一个元素;- 依次将未排序部分的元素插入到已排序部分的合适位置,使得已排序部分保持有序;- 重复上述步骤,直到整个序列有序。
3.选择排序选择排序是一种简单且直观的排序算法,其基本思想是每次选择未排序部分中的最小(或最大)元素,并将其放在已排序部分的末尾。
具体步骤如下:- 在未排序部分中选择最小(或最大)的元素;- 将选择的最小(或最大)元素与未排序部分的第一个元素交换位置;- 重复上述步骤,直到整个序列有序。
4.快速排序快速排序是一种高效的排序算法,其基本思想是通过一趟排序将待排序序列分割成两部分,其中一部分的元素都比另一部分的元素小。
具体步骤如下:- 选择一个枢轴元素(一般选择第一个元素);- 将待排序序列中小于枢轴元素的元素放在枢轴元素的左侧,大于枢轴元素的元素放在枢轴元素的右侧;- 对枢轴元素左右两侧的子序列分别进行递归快速排序;- 重复上述步骤,直到整个序列有序。
5.归并排序归并排序是一种高效的排序算法,其基本思想是将待排序序列划分成足够小的子序列,然后对这些子序列进行两两合并,最终形成有序的序列。
具体步骤如下:- 将待排序序列递归地划分成足够小的子序列;- 对每个子序列进行归并排序;- 合并相邻的子序列,直到整个序列有序。
北邮数据结构实验报告
北邮数据结构实验报告北邮数据结构实验报告一、引言数据结构是计算机科学中的重要基础知识,对于计算机程序的设计和性能优化起着至关重要的作用。
本报告旨在总结北邮数据结构实验的相关内容,包括实验目的、实验设计、实验过程和实验结果等。
二、实验目的本次实验旨在通过实践操作,加深对数据结构的理解和应用能力。
具体目的如下:1. 掌握线性表、栈和队列等基本数据结构的实现方法;2. 熟悉二叉树、图等非线性数据结构的构建和遍历算法;3. 学会使用递归和非递归算法解决实际问题;4. 培养编程实践能力和团队合作意识。
三、实验设计本次实验包括以下几个部分:1. 线性表实验:设计一个线性表类,实现线性表的基本操作,如插入、删除和查找等。
通过实验,了解线性表的顺序存储和链式存储结构的特点和应用场景。
2. 栈和队列实验:设计栈和队列类,实现栈和队列的基本操作,如入栈、出栈、入队和出队等。
通过实验,掌握栈和队列的应用,如括号匹配、迷宫求解等。
3. 二叉树实验:设计二叉树类,实现二叉树的创建、遍历和查找等操作。
通过实验,熟悉二叉树的前序、中序和后序遍历算法,并了解二叉树的应用,如表达式求值等。
4. 图实验:设计图类,实现图的创建、遍历和最短路径等操作。
通过实验,掌握图的邻接矩阵和邻接表表示方法,并了解图的深度优先搜索和广度优先搜索算法。
四、实验过程1. 线性表实验:根据实验要求,首先选择线性表的存储结构,然后设计线性表类,实现插入、删除和查找等基本操作。
在实验过程中,遇到了一些问题,如边界条件的处理和内存管理等,通过团队合作,最终解决了这些问题。
2. 栈和队列实验:根据实验要求,设计栈和队列类,实现入栈、出栈、入队和出队等基本操作。
在实验过程中,我们发现了栈和队列在实际应用中的重要性,如括号匹配和迷宫求解等,通过实验加深了对栈和队列的理解。
3. 二叉树实验:根据实验要求,设计二叉树类,实现二叉树的创建、遍历和查找等操作。
在实验过程中,我们发现了二叉树在表达式求值和排序等方面的应用,通过实验加深了对二叉树的理解。
数据结构实验报告排序
数据结构实验报告排序数据结构实验报告:排序引言:排序是计算机科学中常见的算法问题之一,它的目标是将一组无序的数据按照特定的规则进行排列,以便于后续的查找、统计和分析。
在本次实验中,我们将学习和实现几种常见的排序算法,并对它们的性能进行比较和分析。
一、冒泡排序冒泡排序是最简单的排序算法之一,它通过不断交换相邻的元素,将较大(或较小)的元素逐渐“冒泡”到数组的一端。
具体实现时,我们可以使用两层循环来比较和交换元素,直到整个数组有序。
二、插入排序插入排序的思想是将数组分为两个部分:已排序部分和未排序部分。
每次从未排序部分中取出一个元素,插入到已排序部分的适当位置,以保持已排序部分的有序性。
插入排序的实现可以使用一层循环和适当的元素交换。
三、选择排序选择排序每次从未排序部分中选择最小(或最大)的元素,与未排序部分的第一个元素进行交换。
通过不断选择最小(或最大)的元素,将其放置到已排序部分的末尾,从而逐渐形成有序序列。
四、快速排序快速排序是一种分治的排序算法,它通过选择一个基准元素,将数组划分为两个子数组,其中一个子数组的所有元素都小于等于基准元素,另一个子数组的所有元素都大于基准元素。
然后对两个子数组分别递归地进行快速排序,最终将整个数组排序。
五、归并排序归并排序也是一种分治的排序算法,它将数组划分为多个子数组,对每个子数组进行排序,然后再将排好序的子数组合并成一个有序的数组。
归并排序的实现可以使用递归或迭代的方式。
六、性能比较与分析在本次实验中,我们对以上几种排序算法进行了实现,并通过对不同规模的随机数组进行排序,比较了它们的性能。
我们使用了计算排序时间的方式,并记录了每种算法在不同规模下的运行时间。
通过对比实验结果,我们可以得出以下结论:1. 冒泡排序和插入排序在处理小规模数据时表现较好,但在处理大规模数据时性能较差,因为它们的时间复杂度为O(n^2)。
2. 选择排序的时间复杂度也为O(n^2),与冒泡排序和插入排序相似,但相对而言,选择排序的性能稍好一些。
北邮数据结构实验报告3二叉树含源码
数据结构实验报告实验名称:实验三——树题目一学生姓名:申宇飞班级:2012211103班内序号:03学号:2012210064日期:2013年12月3日1.实验要求掌握二叉树基本操作的实现方法根据二叉树的抽象数据类型的定义,使用二叉链表实现一个二叉树。
二叉树的基本功能:1、二叉树的建立2、前序遍历二叉树3、中序遍历二叉树4、后序遍历二叉树5、按层序遍历二叉树6、求二叉树的深度7、求指定结点到根的路径8、二叉树的销毁9、其他:自定义操作编写测试main()函数测试线性表的正确性2. 程序分析2.1 存储结构采用二叉树的存储结构,其中每个二叉树的结点定义了一个结构体,该结构体包含三个元素,分别是一个T类型的数据域data,一个指向T类型的指针左孩子,一个指向T 类型的指针右孩子,示意图如图所示。
采用了队列的存储结构。
示意图如图所示:对于二叉树中每个结点的data 域的赋值,我们事先把这些data 储存在一个数组中,通过对数组元素的调用事先对二叉树中每个结点的data 域的赋值。
2.2 关键算法分析 一:二叉树的建立: A . 自然语言描述:1首先判断调用的数组是否为空,如果为空,直接将一个已经初始化好的根节点置为空。
2 如果该数组不为空,则把调用的数组的第一个元素的赋给根节点的data 域。
3 采用递归的思想,分别将根节点的左右孩子作为根节点,递归调用该函数。
完成对左右子树的赋值。
B . 代码详细分析:template <class T> void BiTree<T>::Create(Node<T> *&R,T* buf,int i) {if (buf[i-1]==0) R = NULL; else {R=new Node<T>; R->data = buf[i-1];Create(R->lch, buf, 2*i); Create(R->rch, buf, 2*i+1); }lchdata rchlchdata rchlchdata rchlchdata rchlchdata rchlchdata rchlch data rchA[2*i]A[2*i+1] lch data rch lch data rch二:前序遍历二叉树:A. 自然语言描述:1:建立栈2:判断该栈是否为空并且根节点是否为空指针3:如果根节点不是空指针,则输出它的data域,并且是它入栈4:入栈后将根节点指针指向它的左孩子5:如果根节点是空指针6:则根节点出栈,并且指向根节点的指针指向它的右孩子B.代码详细分析:template <class T> void BiTree<T>::Preorder(Node<T> *R){Stack<Node<T>*> S;while(!S.IsEmpty() || (R!=NULL)){if (R!=NULL){Print(R->data);S.Push(R);R=R->lch;}else{R=S.Pop();R=R->rch;}}}三:中序遍历二叉树:A.自然语言描述:1:建立栈2:判断该栈是否为空并且根节点是否为空指针3:如果根节点不是空指针,将它入栈4:入栈后将根节点指针指向它的左孩子5:如果根节点是空指针6:则根节点出栈,输出该节点的data域,并且指向根节点的指针指向它的右孩子B. 代码详细分析:template <class T> void BiTree<T>::Inorder(Node<T> *R){Stack<Node<T>*> S;while(!S.IsEmpty() || (R!=NULL)){if (R!=NULL){S.Push(R);R=R->lch;}else{R=S.Pop();Print(R->data);R=R->rch;}}}四:后序遍历二叉树:A.自然语言描述:1:判断根节点是否为空2:如果根节点不为空3:递归调用后续遍历函数,函数的参数改为根节点的左孩子 4:递归调用后续遍历函数,函数的参数改为根节点的右孩子 5:输出根节点的data域B.代码详细分析:template <class T> void BiTree<T>::Postorder(Node<T> *R) {if (R!=NULL) {Postorder(R->lch);Postorder(R->rch);Print(R->data);}}}}123五:层序遍历二叉树:A.自然语言描述:1:判断根节点是否为空2:如果根节点不为空3:输出根节点的data域4:递归调用层续遍历函数,函数的参数改为根节点的左孩子5:递归调用层序遍历函数,函数的参数改为跟结点的右孩子B.代码详细分析:template <class T> void BiTree<T>::Levelorder(Node<T> *R){Queue<Node<T>*> Q;while(!Q.IsEmpty() || (R!=NULL)){if (R!=NULL){Print(R->data);Q.EnQueue(R->lch);Q.EnQueue(R->rch);}R=Q.DelQueue();}}}123六:求二叉树的深度:A.自然语言描述:1:判断根节点是否为空,如果根节点为空,返回02:如果根节点不为空但是根节点的左右孩子同时为空,返回13:如果以上两个条件都不成立4:递归调用求二叉树的深度,函数的参数改为根节点的左孩子,并且深度初始化为1 5:递归调用求二叉树的深度,函数的参数改为跟结点的右孩子,并且深度初始化为0 6:返回4与5步中得出深度较大的那个数作为二叉树的深度数B.代码详细分析:template <class T> int BiTree<T>::GetDepth(Node<T> *R,int d){if (R==NULL) return d;if ((R->lch==NULL) && (R->rch==NULL))return d+1;else{int m = GetDepth(R->lch,d+1); int n = GetDepth(R->rch,d+1); return n>m? n:m; } }七:二叉树的销毁 A.自然语言描述:1:判断根节点是否为空 2:如果根节点不为空3:递归调用二叉树的销毁函数,参数改为根节点的左孩子 4:递归调用二叉树的销毁函数,参数改为根节点的右孩子 5:释放根节点指向的内存 B.代码详细分析:template <class T> void BiTree<T>::Destroy(Node<T> *R) {if (R!=NULL) { Destroy(R->lch); Destroy(R->rch); delete R; } }}:123八:求二叉树的叶子结点数: A.自然语言描述:1:判断根节点是否为空,如果为空,返回02:如果根节点不为空,切根节点的左右孩子同时为空,返回13:递归调用求二叉树的叶子节点数函数,参数改为根节点的左孩子 4:递归调用求二叉树的叶子结点数函数,参数改为根节点的右孩子 5:返回根节点的左右子树的叶子结点数之和 B.代码详细分析:template <class T> int BiTree<T>::LeafNodeCount(Node<T> *R) {if (R==NULL) return 0;if ((R->lch==NULL) && (R->rch==NULL)) return 1; else {int n=LeafNodeCount(R->lch); int m=LeafNodeCount(R->rch); return m+n; } }九:求二叉树的结点数: A.自然语言描述:} }123}1231:判断根节点是否为空,如果为空,返回02:如果根节点不为空3:递归调用求二叉树的结点数的函数,参数改为根节点的左孩子4:递归调用求二叉树的结点数的函数,参数改为根节点的右孩子5:返回根节点的左右字数的结点数之和B.代码详细分析:template <class T> int BiTree<T>::NodeCount(Node<T> *R){if (R==NULL) return 0;else{int m=NodeCount(R->lch);}}int n=NodeCount(R->rch); return m+n+1;}1232.3 其他对二叉树的操作上,前序遍历与中序遍历采用了非递归算法,后续遍历,层序遍历,求二叉树深度,求二叉树叶子结点数,求二叉树结点数等函数采用了递归算法。
国家开放大学《数据结构》课程实验报告(实验3 ——栈、队列、递归设计)参考答案
/*判队空*/
int QueueEmpty(SeqQueue *sq)
{
if(sq->rear==sq->front)
return 1;
else
return 0;
}
/*循环队列入队*/
void InQueue(SeqQueue *sq,ElemType x)
{
if ((sq->rear+1)%MaxSize==sq->front) /*队满*/
InitStack(s);
printf("(2)栈为%s\n",(StackEmpty(s)?"空":"非空"));
printf("(3)输入要进栈的数据个数:");
scanf("%d",&n);
printf("依次输入进栈的%d个整数:",n);
/*数据依次进栈*/
for(i=0; i<n; i++)
{
printf("循环队列已空,不能进行出队操作!\n");
exit(1);
}
else{
x=sq->data[sq->front];
sq->front=(sq->front+1)%MaxSize;
return x;
}
}
/*取队头元素*/
ElemType GetQueue(SeqQueue *sq)
{
void InitQueue(SeqQueue *sq); /*初始化队列*/
int QueueEmpty(SeqQueue *sq); /*判队空*/
北邮数据结构实验三题目2哈夫曼树
北京邮电大学信息与通信工程学院
{ while (*s!='\0') { int parent=m-1;//根结点 while(HTree[parent].LChild!=-1)//从根节点开始直到终端结点结束循环 { if (*s=='0') parent=HTree[parent].LChild; else parent=HTree[parent].RChild;
第 4页
北京邮电大学信息与通信工程学院
k++; l=1; HTree[k].c=ctemp; HTree[k].weight=l; } } delete []temp;//释放动态内存空间 Creat();//创建哈夫曼数的数组 CodeTable();//创建哈夫曼编码表 } 时间复杂度:n
2 创建哈夫曼树动态数组 算法步骤: ①根据权重数组初始化哈夫曼树; ②选取权值最小的两个结点作为左右子树,构成一个新的二叉树,其根结点的权值取 左右子树权值之和; ③删除这两棵子树,将新构成的树加入到 T 中,重复②③直至 T 中只含有一棵树。
*d+=HcodeTable[i].code[j]; sum+=1; }
s++; n++; break; } } } cout<<"压缩比为:"<<sum/(8*n)<<endl; } 时间复杂度:n
7 解码函数 算法步骤: ①从根节点开始进行解码,如果字符为 0,则为左孩子,1,则为右孩子; ②编码匹配编码表中对应的数据,进行解码。 源代码: void Huffman::Decode(char *s,char *d)
第 1页
北邮信通院数据结构实验报告三哈夫曼编码器
数据结构实验报告实验名称:实验三树——哈夫曼编/解码器学生:班级:班序号:学号:日期:2014年12月11日1.实验要求利用二叉树结构实现赫夫曼编/解码器。
基本要求:1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树2、建立编码表(CreateTable):利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):以直观的方式打印赫夫曼树(选作)6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
测试数据:I love data Structure, I love Computer。
I will try my best to study data Structure.提示:1、用户界面可以设计为“菜单”方式:能够进行交互。
2、根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2. 程序分析2.1 存储结构Huffman树给定一组具有确定权值的叶子结点,可以构造出不同的二叉树,其中带权路径长度最小的二叉树称为Huffman树,也叫做最优二叉树。
weight lchild rchild parent 2-1-1-15-1-1-16-1-1-17-1-1-19-1-1-1weight lchild rchild parent2-1-155-1-156-1-167-1-169-1-17701713238165482967-12.2 关键算法分析(1)计算出现字符的权值利用ASCII码统计出现字符的次数,再将未出现的字符进行筛选,将出现的字符及頻数存储在数组a[]中。
void Huffman::Init(){int nNum[256]= {0}; //记录每一个字符出现的次数int ch = cin.get();int i=0;while((ch!='\r') && (ch!='\n')){nNum[ch]++; //统计字符出现的次数str[i++] = ch; //记录原始字符串ch = cin.get(); //读取下一个字符}str[i]='\0';n = 0;for ( i=0;i<256;i++){if (nNum[i]>0) //若nNum[i]==0,字符未出现{l[n] = (char)i;a[n] = nNum[i];n++;}}}时间复杂度为O(1);(2)创建哈夫曼树:算法过程:Huffman树采用顺序存储---数组;数组的前n个结点存储叶子结点,然后是分支结点,最后是根结点;首先初始化叶子结点元素—循环实现;以循环结构,实现分支结点的合成,合成规则按照huffman树构成规则进行。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构实验报告1.实验要求(1)实验目的通过选择下面两个题目之一,学习、实现、对比各种排序算法,掌握各种排序算法的优劣,以及各种算法使用的情况。
(2)实验内容使用简单数组实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、希尔排序3、冒泡排序4、快速排序5、简单选择排序6、堆排序(选作)7、归并排序(选作)8、基数排序(选作)9、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试排序算法的正确性。
2. 程序分析2.1 存储结构顺序表:示意图:2.2 关键算法分析(1)测试数据的产生:正序、逆序、随机数据用两个数组实现乱序、顺序以及逆序数据的排序。
基本思想为:随机序列产生一个指定长度的乱序序列,然后通过memcpy()函数拷贝到第二个数组里,第二个数组作为乱序序列的保存数组,每次对第一个数组进行排序,之后拷贝第二个数组中的乱序序列到第一个数组,实现各次乱序排列。
只要算法正确(第一步可以检验),之后顺序排列只需反复对第一个数组进行操作即可,再后用第二个数组保存逆序数组,然后同样在每次排序之后复制第二数组存储的乱序序列到第一组,对第一组反复排序即可。
<1> pRandom1=new long int[Max+1];pRandom2=new long int[Max+1];<2> srand((unsigned)time(NULL)); for(int i = 1; i <= Max;i++ ) pRandom2[i]=rand();<3> memcpy(obj.pRandom1,obj.pRandom2,(Max+1)*sizeof(long int));(2)排序算法:<1>插入排序:依次将待排序的序列中的每一个记录插入到先前排序好的序列中,直到全部记录排序完毕。
/1/int j=0;/2/ for(int i =2; i <= Max;i++) parray[0]=parray[i];comparetimes[0]++;/4/parray[j+1]=parray[0];movetimes[0]+=2;示意图:r1,r2,r3,…,ri-1,ri,ri+1,…,rn有序区待插入无序区<2>希尔排序:先将整个序列分割成若干个子列,分别在各个子列中运用直接插入排序,待整个序列基本有序时,再对全体记录进行一次直接插入排序。
int Sort::ShellSort(long int parray[]){int j=0;for(int d=Max/2;d>=1;d/=2){for(int i=d+1;i<=Max;i++){ parray[0]=parray[i];comparetimes[1]++;for(j=i-d;j>0 && parray[0]<parray[j];j-=d){ parray[j+d]=parray[j];movetimes[1]++;}parray[j+d]=parray[0];movetimes[1]+=2;}}return 0;}<3>冒泡排序:两两比较相邻记录的关键码,如果反序则交换,直到没有反序记录为止。
int Sort::BubbleSort(long int parray[]){ int exchange=Max;int bound,j;while(exchange){ bound=exchange;exchange=0;for(j=1;j<bound;j++){ comparetimes[2]++;if(parray[j]>parray[j+1]){ parray[0]=parray[j];parray[j]=parray[j+1];parray[j+1]=parray[0];exchange=j;movetimes[2]+=3;}}}return 0;}示意图:r1,r2,r3,…,ri-1,ri,ri+1,…,rn反序则交换有序区<4>快速排序:首先选择一个基准,将记录分割为两部分,左支小于或等于基准,右支则大于基准,然后对两部分重复上述过程,直至整个序列排序完成。
int Sort::QuickSort(long int parray[]){QuickSortRecursion(parray,1, Max);return 0;}int Sort::QuickSortRecursion(long int parray[], int first=1, int end=Max) {if (first<end){ int pivot=QuickSortPatition(parray, first, end);QuickSortRecursion(parray, first, pivot-1);//左侧子序列排序QuickSortRecursion(parray, pivot+1, end); //右侧子序列排序} return 0;}int Sort::QuickSortPatition(long int r[], int first, int end ){int i=first;int j=end; int temp;while (i<j){ while (i<j && r[i]<= r[j]){j--;comparetimes[3]++; } //右侧扫描 if (i<j){ temp=r[i]; //将较小记录交换到前面r[i]=r[j];r[j]=temp;i++;movetimes[3]+=3; }while (i<j && r[i]<= r[j]){i++;comparetimes[3]++;} //左侧扫描if (i<j){temp=r[j];r[j]=r[i];r[i]=temp; //将较大记录交换到后面j--;movetimes[3]+=3; } }return i; //i为轴值记录的最终位置示意图:r1,r2,r3,…,ri-1,ri,ri+1,…,rnr<ri 轴值 r>ri<5>选择排序:从待排序的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第一个记录交换位置;然后从不包括第一个位置上的记录序列中选择关键码最小(或最大)的记录并将它与序列中的第二个记录交换位置;如此重复,直到序列中只剩下一个记录为止。
int Sort::SelectSort(long int parray[]){int i,j,index,temp;for (i=1; i<Max; i++) //对n个记录进行n-1趟简单选择排序 {index=i;for (j=i+1; j<=Max; j++){comparetimes[4]++; //在无序区中选取最小记录if (parray[j]<parray[index])index=j;}if (index!=i){temp=parray[i];parray[i]=parray[index];parray[index]=temp;movetimes[4]+=3;}}示意图:r1,r2,r3,…,ri-1,ri,ri+1,…,rn反序则交换有序区<6>堆排序:通过建立大根堆或者小根堆,取出根节点,反复调整堆使之保持大根堆或者小根堆,直至最后序列有序。
int Sort::HeapSort(long int parray[]){int i;for (i=Max/2; i>=1; i--)HeapSortSift(parray, i, Max) ;for (i=1; i<Max; i++){parray[0]=parray[Max-i+1];parray[Max-i+1]=parray[1];parray[1]=parray[0];movetimes[5]+=3;HeapSortSift(parray, 1, Max-i);}return 0;}void Sort::HeapSortSift(long int parray[], int k, int m){int i,j;i=k;j=2*i; //置i为要筛的结点,j为i的左孩子while (j<=m) //筛选还没有进行到叶子{if (j<m && parray[j]<parray[j+1]){j++;comparetimes[5]++;} //比较i的左右孩子,j为较大者 if (parray[i]>parray[j]){comparetimes[5]++;break;} //根结点已经大于左右孩子中的较大者else{parray[0]=parray[i];parray[i]=parray[j];parray[j]=parray[0];movetimes[5]+=3;i=j;j=2*i; //被筛结点位于原来结点j的位置<7>归并排序:将若干个有序序列两两归并,直至所有待排序的记录都在一个有序序列为止。
int Sort::MergeSort(long int parray[]){long int r1[Max+1];int h(1);while (h<Max){MergePass(parray, r1, h); //归并h=2*h;MergePass(r1, parray, h);h=2*h;}return 0;}void Sort::Merge(long int parray[], long int r1[], int s, int m, int t) //一次归并{int i=s;int j=m+1;int k=s;while (i<=m && j<=t){comparetimes[6]++;movetimes[6]++;if (parray[i]<=parray[j]){r1[k++]=parray[i++];}elser1[k++]=parray[j++];}if (i<=m)while (i<=m){r1[k++]=parray[i++];movetimes[6]++;}elsewhile (j<=t){r1[k++]=parray[j++];movetimes[6]++;}}void Sort::MergePass(long int parray[], long int r1[], int h) //一趟归并{int i(1),k;while (i<=Max-2*h+1){Merge(parray, r1, i, i+h-1, i+2*h-1);i+=2*h;}if (i<Max-h+1)Merge(parray, r1, i, i+h-1, Max);else for (k=i; k<=Max; k++){r1[k]=parray[k];movetimes[6]++;(3)比较上述排序算法中关键字的比较次数和移动次数:使用函数指针数组,分别指向各排序函数的入口地址,然后在Statistics()函数中加以调用,使得排序函数运行在统计时间函数之间,这样使用一个for语句即可实现算法的一次性调用、时间统计、移动次数和比较次数统计。