数据结构与算法-第八章 交换排序

基本思想
每趟不断记录两两比较,如果发生逆序则按"前小后大"规则交换,直到所有记录都排好序为止;
常见的交换排序方法
  • 冒泡排序(O(n²)) ------ 稳定
  • 快速排序(O(nlog2n))
冒泡排序 ------ 基于简单交换思想(稳定)
初始: 	21, 25, 49, 25*, 18, 08	n=6
第一趟: 位置0,1进行比较-判断-不交换,结果:
	 	21, 25, 49, 25*, 18, 08
		位置1,2进行比较-判断-不交换,结果:
		21, 25, 49, 25*, 18, 08
		位置2,3进行比较-判断-交换,结果:
		21, 25, 25*, 49, 18, 08
		位置2,3进行比较-判断-交换,结果:
	 	21, 25, 25*, 18, 49, 08
	 	位置2,3进行比较-判断-交换,结果:
	 	21, 25, 25*, 18, 08, 49
第一趟结束,序列中的49元素被移动到了序列末位;
继续下一趟,每一趟增加一个有序元素;
第二趟结束:
		21, 25, 18, 08, 25*, 49
...直到第五趟:
		08, 18, 21, 25, 25*, 49
总结: 	n个记录,总共需要 n-1;
		每一趟需要比较n-m次(m为趟数);

/*
 * 冒泡排序算法
 */
void bubble_sort(SqList &L) {
    int m,i,j;
    int flag = 1;   // flag作为是否有交换的标记
    RedType x;  // 交换临时存储
    n = L.length
    for (m = 1; m <= n-1 && flag == 1; m++) {  // 总共需要m趟
        flag = 0;
        for (j = 1; j <= n-m; j++)
            if (L.r[j].key > L.r[j+1].key) {    // 发生逆序,交换
                flag = 1;   // 发生贾环,flag置1,若本趟没有发生交换,flag保持0
                x = L.r[j];
                L.r[j] = L.r[j+1];
                L.r[j+1] = x;
            }// end if
    }// for
}
冒泡排序优劣
优点: 每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;
如何提高效率?
	一旦某一趟比较时不出现记录交换,说明已经排好序了,就可以结束本算法.
冒泡排序算法分析
时间复杂度:
	最好情况:(正序)
		比较次数: n-1次
		移动次数:0次
	最坏情况:(逆序)
		比较次数: (n-1)+(n-2)+..+0 = (-n)/2
		移动次数: 两个值交换,存在一个中间变量,则有3(-n)/2

冒泡排序最好时间复杂度为O(n);
冒泡排序最坏时间复杂度为O();
冒泡排序平均时间复杂度为O();
冒泡排序算法中增加一个辅助空间temp,复制空间为S(n)=O(1);
冒泡排序是稳定的;
快速排序 ------ 改进的交换排序(不稳定)
基本思想: 
	任取一个元素(: 第一个)为中心: pivot: 枢轴,中心点;
	所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
	对各子表重新选择中心元素并依此规则调整; ------ 递归思想-
	直到每个子表的元素只剩一个;

通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序.

具体实现: 选定一个中间数作为参考,所有元素与之比较,小的调到其左边,大的调到其右边.
	(枢轴)中间数: 可以是第一个数,最后一个数,最中间的数,任选一个数等.	
	初始:下标为0的哨兵位置不放数据,low指向第一个元素,high指向最后一个元素;
	将序列的第一个元素选为界点放入哨兵位置,此时low指向为空,high与界点比较
		若high大,则high向左移动;
		若high小,则将high指向的元素换到low指向的位置,然后low向右移动;
	直到low与high在序列的某个位置相遇,则找到了界点放置的位置,此时将哨兵位置的元素放置到这里.

在这里插入图片描述

快速排序算法
  1. 每一趟的子表的形成是采用从两头向中间交替式逼近法;
  2. 由于每趟中对各子表的操作都相似,可采用递归算法;
/*
 * 快速排序算法
 */
void QSort(SqList &L, int low, int high) {  // 对顺序表L快速排序
    if (low < high) {   // 长度大于1
        pivotloc = Partition(L, low, high);
        // 将L.r[low...high]一分为二,pivotloc为枢轴元素排好序的位置
        QSort(L, low, pivotloc-1);  // 对低子表递归排序
        QSort(L, pivotloc+1, high); // 对高子表递归排序
    }// endif
}// QSort

int Partition(SqList &L, int low, int high) {
    L.r[0] = L.r[low];  // 将第一个元素放到0号位置,此时low位置为空
    pivotloc = L.r[low].key;    // 给定枢轴元素
    while (low < high) {    //  low只要比high小则循环
        while (low < high && L.r[high].key >= pivotloc) --high;     // high比枢轴大,就向左移
        L.r[low] = L.r[high];   // 此时high比枢轴小了,则将元素移动到low空着的位置
        while (low < high && L.r[low].key <= pivotloc) ++low;       // low比枢轴小,就向右移
        L.r[high] = L.r[low];   // 此时low比枢轴大了,则将元素移动到high空着的位置
    }// while结束
    L.r[low] = L.r[0];  // 将枢轴放到low与high相遇的位置
    return low;     //返回枢轴所在的下标
}
快速排序算法分析(不稳定)
时间复杂度:
	可以证明,平均计算时间是O(nlogn);
	就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个;
	
空间复杂度:
	快速排序不是原地排序;
	由于程序中使用了递归,需要递归调用栈,而栈的长度取决于递归调用的深度.
	在平均情况下: 需要O(logn)的栈空间;
	最坏情况下: 栈空间可达O(n);
稳定性:
	快速排序是一种不稳定的排序方法;

对于原本有序的或基本有序的记录进行排序,由于每次枢轴记录的关键字都是大于其它所有记录的关键字,致使依次划分之后得到的子序列(1)的长度为0,这时已经退化成为没有改进措施的冒泡排序;
故快速排序不适用于对原本有序或基本有序的记录序列进行排序;

划分元素的选取是影响时间性能的关键;

输入数据次序越乱,所选划分元素值的随机性越好,排序速度越快,快速排序不是自然排序方法;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值