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


    基本思想
    每趟不断记录两两比较,如果发生逆序则按"前小后大"规则交换,直到所有记录都排好序为止;
    
    • 1
    常见的交换排序方法
    • 冒泡排序(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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    冒泡排序优劣
    优点: 每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;
    如何提高效率?
    	一旦某一趟比较时不出现记录交换,说明已经排好序了,就可以结束本算法.
    
    • 1
    • 2
    • 3
    冒泡排序算法分析
    时间复杂度:
    	最好情况:(正序)
    		比较次数: n-1次
    		移动次数:0次
    	最坏情况:(逆序)
    		比较次数: (n-1)+(n-2)+..+0 = (-n)/2
    		移动次数: 两个值交换,存在一个中间变量,则有3(-n)/2
    
    冒泡排序最好时间复杂度为O(n);
    冒泡排序最坏时间复杂度为O();
    冒泡排序平均时间复杂度为O();
    冒泡排序算法中增加一个辅助空间temp,复制空间为S(n)=O(1);
    冒泡排序是稳定的;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    快速排序 ------ 改进的交换排序(不稳定)
    基本思想: 
    	任取一个元素(: 第一个)为中心: pivot: 枢轴,中心点;
    	所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
    	对各子表重新选择中心元素并依此规则调整; ------ 递归思想-
    	直到每个子表的元素只剩一个;
    
    通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序.
    
    具体实现: 选定一个中间数作为参考,所有元素与之比较,小的调到其左边,大的调到其右边.
    	(枢轴)中间数: 可以是第一个数,最后一个数,最中间的数,任选一个数等.	
    	初始:下标为0的哨兵位置不放数据,low指向第一个元素,high指向最后一个元素;
    	将序列的第一个元素选为界点放入哨兵位置,此时low指向为空,high与界点比较
    		若high大,则high向左移动;
    		若high小,则将high指向的元素换到low指向的位置,然后low向右移动;
    	直到low与high在序列的某个位置相遇,则找到了界点放置的位置,此时将哨兵位置的元素放置到这里.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    快速排序算法
    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;     //返回枢轴所在的下标
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    快速排序算法分析(不稳定)
    时间复杂度:
    	可以证明,平均计算时间是O(nlogn);
    	就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个;
    	
    空间复杂度:
    	快速排序不是原地排序;
    	由于程序中使用了递归,需要递归调用栈,而栈的长度取决于递归调用的深度.
    	在平均情况下: 需要O(logn)的栈空间;
    	最坏情况下: 栈空间可达O(n);
    稳定性:
    	快速排序是一种不稳定的排序方法;
    
    对于原本有序的或基本有序的记录进行排序,由于每次枢轴记录的关键字都是大于其它所有记录的关键字,致使依次划分之后得到的子序列(1)的长度为0,这时已经退化成为没有改进措施的冒泡排序;
    故快速排序不适用于对原本有序或基本有序的记录序列进行排序;
    
    划分元素的选取是影响时间性能的关键;
    
    输入数据次序越乱,所选划分元素值的随机性越好,排序速度越快,快速排序不是自然排序方法;
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    Python异步编程——asyncio、协程
    【vue3】07. 跟着官网学习vue3
    Log4j2的JNDI注入漏洞(CVE-2021-44228)原理分析与思考
    电脑重装系统后内存占用高怎么解决?
    域名及地址正确外,若依后台无法正常加载页面和退出报404问题
    PMSM中常用的两种坐标变换——两种参数的由来
    网络流探索:解决网络最大流问题的算法集锦
    client-go实战之九:手写一个kubernetes的controller
    基于JSP的某餐厅点餐系统
    ajax请求实现学生信息的增改查
  • 原文地址:https://blog.csdn.net/sjn2212297386/article/details/127770097