数据结构实验报告实验名称:实验四——链表的排序学生姓名:班级:班内序号:学号:日期:1.实验要求[内容要求]使用链表实现下面各种排序算法,并进行比较。
排序算法:1、插入排序2、冒泡排序3、快速排序4、简单选择排序5、其他要求:1、测试数据分成三类:正序、逆序、随机数据2、对于这三类数据,比较上述排序算法中关键字的比较次数和移动次数(其中关键字交换计为3次移动)。
3、对于这三类数据,比较上述排序算法中不同算法的执行时间,精确到微秒(选作)4、对2和3的结果进行分析,验证上述各种算法的时间复杂度编写测试main()函数测试线性表的正确性代码要求1、必须要有异常处理,比如删除空链表时需要抛出异常;2、保持良好的编程的风格:代码段与段之间要有空行和缩近标识符名称应该与其代表的意义一致函数名之前应该添加注释说明该函数的功能关键代码应说明其功能3、递归程序注意调用的过程,防止栈溢出2. 程序分析2.1 存储结构[内容要求]存储结构:双链表2.2 关键算法分析[内容要求]定义类:template <class T>class LinkList{public:LinkList(){front = new Node <T>;front->next=rear;front->prior=NULL;rear=newNode<T>;rear->next=NULL;rear->prior=front;}LinkList(T a[],int n);void BackLinkList(T a[]);//恢复原链表~LinkList();//析构函数void PrintList();//打印数列void InsertSort();//插入排序void BubbleSort();//冒泡排序Node <T>* Partion(Node <T> *i,Node <T> *j);//快速排序中寻找轴值的函数void Qsort(Node <T> *i,Node <T> *j);//快速排序void SelectSort();//选择排序Node<T>*front;Node<T>*rear;};成员函数包括:构造函数:单链表,打印单链表,插入排序,快速排序,冒泡排序,选择排序,析构函数公有成员:头指针和尾指针1、构造函数:LinkList<T>::LinkList(T a[],int n){front=new Node<T>;rear=new Node<T>;front->prior=NULL;front->next=rear;rear->next=NULL;rear->prior=front;Node <T>*s;for (int i=n-1;i>=0;i--){s=new Node<T>;s->data=a[i];s->next=front->next;front->next=s;s->prior=front;s->next->prior=s;}}2、析构函数LinkList<T>::~LinkList(){Node<T> *p=front->next;while(p->next!=NULL){delete p->prior;p=p->next;}delete rear;front=NULL;rear=NULL;}3、打印函数template <class T>void LinkList<T>::PrintList(){Node<T> *p=front->next;while(p->next!=NULL){cout<<p->data<<" ";p=p->next;}cout<<endl;}4、插入排序template <class T>void LinkList<T>::InsertSort(){Node<T> *p=front->next->next;Node<T> *q;T temp;while (p->next!=NULL){if (p->data<p->prior->data){temp=p->data;MoveCount++;q=p->prior;while (temp<q->data){q->next->data=q->data;q=q->prior;CompareCount++;MoveCount++;}q->next->data=temp;MoveCount++;}CompareCount++;p=p->next;}}将链表分为有序区和无序区,分别将无序区的每一个元素插入到有序区的相应位置。
每次首先都找到无序区中最小的链节,然后摘除链表,在新的位置插入。
时间复杂度:O(n^n)空间复杂度:O(n)5、冒泡排序template <class T>void LinkList<T>::BubbleSort(){Node<T> *p;Node<T> *q;T temp;Node<T> *pos=rear->prior;Node<T> *bound;while (pos!=NULL){bound=pos;pos=NULL;q=front->next;p=front->next->next;while (q!=bound&&p->next!=NULL)//后半个条件很重要,处理一个元素和2个元素的情况{if (q->data>p->data){temp=p->data;p->data=temp;p->data=q->data;q->data=temp;pos=p->prior;MoveCount+=3;}CompareCount++;q=q->next;p=p->next;}}}和数组实现是一样的,一共进行n-1趟,每一趟当前面的数据大于后面的数据,进行交换,一趟可以实现将这一趟的最大的放在最后面。
时间复杂度:O(N^2)空间复杂度O(n)6、快速排序template <class T>Node <T>* LinkList<T>::Partion(Node <T> *i,Node <T> *j){T pivot=i->data;while (i!=j){while((i!=j)&&(j->data>=pivot)){j=j->prior;CompareCount++;}i->data=j->data;MoveCount++;while((i!=j)&&(i->data<=pivot)){i=i->next;CompareCount++;}j->data=i->data;MoveCount++;}i->data=pivot;MoveCount++;return i;}template <class T>void LinkList<T>::Qsort(Node <T> *i,Node <T> *j){if (i!=j&&i->prior!=j){Node <T>* pivotloc=Partion(i,j);Qsort(i,pivotloc->prior);Qsort(pivotloc->next,j);}}每一次将第一个作为轴值,交换一次使比他大的放在左面,比他小的放在右面。
接着递归将轴值左面的链表和右面的链表分别做相同操作直至只有一个链结为止。
时间复杂度:O(nlog n)空间复杂度:调用栈空间O(log2n)7、选择排序template <class T>void LinkList<T>::SelectSort(){Node<T> *p=front->next;Node<T> *q=p->next;Node<T> * index;T temp;while (p->next->next!=NULL){index=p;while (q->next!=NULL){if (q->data<index->data){index=q;}q=q->next;CompareCount++;}if (index!=p){temp=p->data;p->data=index->data;index->data=temp;MoveCount+=3;}p=p->next;q=p->next;}}每一轮都是找到最小的数第i趟交换,交换n趟。
是冒泡排序的优化了交换次数。
时间复杂度:O(n^2)空间复杂度:O(n)2.3 其他使用微秒级的计数器:调用<windows.h>库函数//计时的函数void Timer(mem_fun ptr,LinkList<int> &A,int a[],mem_fun2 ptr2=NULL,mem_fun3 ptr3=NULL){QueryPerformanceFrequency(&litmp);dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率(QuadPart是8字节整形数)QueryPerformanceCounter(&litmp);QPart1 = litmp.QuadPart;// 获得初始值if (ptr2!=NULL)for (double i=0;i<10000;i++)(A.*ptr2)(a);else if(ptr3!=NULL)for (double i=0;i<10000;i++){A.BackLinkList(a);(A.*ptr3)(A.front->next,A.rear->prior);}elsefor (double i=0;i<10000;i++)//由于使用QueryPerformanceFrequency(&litmp)和QueryPerformanceCounter(&litmp)计时只精确到毫秒,因此循环10000次,忽略判断时间{A.BackLinkList(a);(A.*ptr)();}QueryPerformanceCounter(&litmp);QPart2 = litmp.QuadPart;//获得终止值dfMinus = (double)(QPart2-QPart1);dfTim=100*dfMinus/dfFreq-dfBackTim;// 获得对应的时间值,单位为微秒if(ptr2!=NULL)return;cout<<setiosflags(ios::left);cout<<"Compare:"<<setw(8)<<CompareCount/10000<<setw(8)<<"Move:"<<setw(8)<<MoveCount/10000<<setw(8)<<"Time:"<<setw(8)<<dfTim<<"us"<<endl;cout<<"排序结果:";A.PrintList();cout<<endl;MoveCount=0;CompareCount=0;}3. 程序运行结果4. 总结1.通过与数组排序比较,链表的排序所花时间更长,因为增加了指针的移动;2.递归调用的排序算法时间较长,因为涉及到函数调用进栈出栈的问题。