数据结构实验报告实验名称:实验三——排序学生姓名:XXX班级:xxxxxxxxxxx班内序号:学号:日期:2018年6月3日1.实验要求实验目的:通过选择下面两个题目之一,学习、实现、对比各种排序算法,掌握各种排序算法的优劣,以及各种算法使用的情况。
实验内容:使用链表实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、冒泡排序3、快速排序4、简单选择排序5、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试线性表的正确性。
2. 程序分析2.1 存储结构单链表,储存每个元素值同时,储存该元素的直接后继元素位置信息。
即数据域(data),指针域(next)。
struct Node{int data;struct Node *next;};2.2 关键算法分析链表的建立:Linklist::Linklist (int a[],int n){front = new Node;Node *r = front ;for(int i=0;i<n;i++) //尾插法初始化链表{Node *s = new Node;s->data = a[i];r->next = s;r=s;}r->next = NULL;}尾插法创建链表:①堆中建立新节点②将a[i]写入新节点data域③新节点加入链表r->next=s.④修改尾指针r=s.简单选择排序:void Linklist ::Link_Selectsort (int n){Node *p=front->next ;int a=0,b=0; //a记载比较次数,b记载移动次数while(p!=NULL){Node *s =p; //s指向最小节点Node *q=p->next ;while(q!=NULL){if(q->data<s->data) s=q;a++;q=q->next ;}if(p!=s){int t=p->data ;p->data =s->data ;s->data =t; //交换节点值b=b+3;}p=p->next ;}Print(n,a,b);}建立p节点记载有序区最后节点的下一个节点,s节点则记载无序区的最小节点,q节点用于遍历链表无序区,找出最小值。
每次循环中q都找到无序最小节点,用s指向该节点,后p和s节点值交换。
时间复杂度为O(n^2)直接插入排序:void Linklist ::Link_Insertsort(int n){Node *p=front;Node *q=front->next ; //q为无序区第一个节点的前驱int a=0,b=0; //a记录比较次数,b记录移动次数while(p->next !=NULL){p=front; //初始化p为有序区的第一个前驱while(q->next !=NULL&&p!=q){a++;if (p->next ->data>q->next ->data ) //比较,交换节点顺序 { Node *s=q->next ;q->next =s->next ; //摘除s 节点 s->next =p->next ; //插到p 节点之后 p->next =s; b=b+3;break ;}p=p->next ;}if (p==q) q=q->next ; //若本躺排序无节点交换,则q 点下移p=q; //当q 到链尾时,用于结束循环}Print(n,a,b);}① 指针p 指向有序区的待比较节点前驱(初始化时即front 节点)②指针q 指向无序区第一个元素的前驱Ⅰ:若无序区的第一个节点值较大,则p 下移,直到p=q 为止Ⅱ:若无序区第一个元素节点值较小,则用指针s 指向该节点,然后摘除s 节点,插入到p 节点后继位置,即本趟循环结束。
时间复杂程度平均为O(n^2),最好为O(n),最坏为O(n^2).…………③冒泡排序:void Linklist ::Link_Bubblesort (int n)Node *p,*r;p=front->next ;r=NULL; //尾指针int a=0,b=0; //a记载比较次数,b记载交换次数while(p!=NULL&&r!=front->next ){p=front->next ; //初始化p到链表头部while(p->next !=r){if(p->data >p->next ->data ) //相邻反序,则交换data域{int t=p->data ;p->data =p->next ->data ;p->next ->data =t;b=b+3;}p=p->next ;a++;}r=p; //尾指针前移}Print(n,a,b);}①每趟排序,指针p指向无序区的待比较的节点,初始化时p指向无序区的第一个节点。
②指针r指向有序区的第一个节点,初始化,r=NULL。
③从p指针开始,前后两个节点的元素值比较,若反序则交换元素的data,否则p下移,直到计较到r为止,本趟排序完成。
④一趟排序后,将尾指针前移一位,即r=p,直到r为第一个元素排序结束。
时间复杂程度平均为O(n^2),最好为O(n),最坏为O(n^2).快速排序:void Linklist ::swap (Node *a,Node *b){int t=a->data ;a->data =b->data ;b->data =t;}Node *Linklist ::partion (Node *start,Node *end,int b[]) //求中间结点{if(start==end||start->next ==end)return start;Node *p=start,*q=start,*refer=start; //取第一个元素为基准元素,refer为轴值while(q!=end) //从start开始向后进行一次遍历,因为单链表无法从左右向中间遍历{if(q->data <refer->data ){p=p->next ;swap(p,q);b[1]=b[1]+3;}q=q->next ;b[0]++;}swap(p,refer);b[1]=b[1]+3;return p;}void Linklist ::quick_sort (Node* start,Node *end,int b[]) //递归,与课本不同,每次递归的第一节点即为轴值{if(start==end||start->next ==end)return;Node *pivotloc=partion (start,end,b);quick_sort (start,pivotloc ,b); //左分区快速排序quick_sort (pivotloc ->next ,end,b); //右分区快速排序}void Linklist ::Link_Quicksort (int n){if((front->next ==NULL)||(front->next ->next==NULL)){if(front->next ->next ==NULL&&front->next !=NULL) Print(n,0,0);return;}int b[2]={0}; //用于记载数据,b[0]记载比较次数,b[1]记载移动次数Node *start =front->next ;Node *end=NULL;quick_sort (start,end,b);int a=b[0], c=b[1];Print(n,a,c);}传入的开始节点,与结束节点中,默认第一个元素为轴值,再把元素一一与轴值比较,比轴值小的放前面,比轴值大的放后面。
不断递归下去排序,当传进去的头尾节点一样时,表明排序已经完成。
注意,由于局部变量的原因,整形变量不能在递归中,因此我用一个数组b 来记录比较次数、移动次数。
时间复杂程度平均为O(nlog2n),,最好情况也同此,最坏情况为O(n^2)。
2.3 其他一、编写一个公共的打印函数,在比较函数中调用,就不用考虑把记载比较次数、交换次数的整形变量如何传递出函数的问题了(当然用整形变量也能解决这一问题)。
二、要设置一个重置链表顺序的函数,应为一次排序后,链表已经变得有序,影响后面排序的结果。
所以调用一个排序函数时,先用一次重置函数。
void Linklist ::Reset (int a[],int n){Node *p = front ;for(int i=0;i<n;i++) //重新把数组a赋值进入链表{p=p->next ;p->data =a[i];}}三、用switch写主函数,让程序有一点人机交互能力。
3. 程序运行结果流程图:结果:4. 总结这次实验,我选的是第二题,比第一题还是复杂一点点。
不过还好,参考书里给了我三种排序的代码,虽然理解起来比用数组麻烦了一点。
但我还是很乐意一试的。
通过编程,不仅对这四种排序有了更深入的了解,而且还能复习一下单链表的使用,还是一举两得的。
不过,很遗憾我没有做选作部分,也就是计算排序函数所用时间。
虽然上网查了方法,比较简单的是使用clock()函数,但是这是毫秒级别的计算,我的数组手动输入,太小了,无法达到毫秒级别,不能计算出来。
用同学给我了建议,那就是用随机数生成大量数据,挺好的,不过上千个数字打印出来也是问题,我作罢了。
倒数第二次实验了,我还是比较满意。
希望最后一次也能如此!。