【数据结构】交换排序—冒泡排序、快速排序

简介: 【数据结构】交换排序—冒泡排序、快速排序

一、什么是交换排序?


1.交换排序的基本思想是两两比较待排序记录的关键字,若两个记录的次序相反则交换这两个记录,直到没有反序的记录为止。


2.交换排序主要方法:


冒泡排序

快速排序


二、冒泡排序


1、什么是冒泡排序?


冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。


它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。


将待排序的数组看成从上到下的存放,把关键字较小的记录看成较轻的,关键字较大的记录看成较重的。


较小关键字值的记录,好像水中的气泡一样,向上浮;


较大关键字值的记录,好像水中的石块一样,向下沉;


当所有的气泡都浮到了相应的位置,并且所有的石块都沉到了水中,排序就结束了。


2、冒泡排序算法的原理分析?


比较相邻的元素。如果第一个比第二个大,就交换他们两个。


对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。


针对所有的元素重复以上的步骤,除了最后一个。


持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。


6c161dda0f6547acb5d644ed8f5b4544.gif


3)算法分析


从第一个记录开始,依次对无序区中相邻记录进行关键字比较,如果大在上,小在下,则交换,第一趟扫描下来表中最大的沉在最下面


然后再对前n-1个记录进行冒泡排序,直到排序成功为止。


一般,第i趟扫描时,r[0]到r[n-i]和r[i+1]到r[n-1]分别为当前的无序区和有序区。


待排序的序列:{52, 39, 67, 95, 70, 8, 25, 52}

b96a5ddd9f9543beba0f873a7aa58d16.png

4、算法代码实现


代码

//【算法】 冒泡排序算法
public void bubbleSort() {
//    System.out.println("冒泡排序");
    RecordNode temp;                                    // 辅助结点
    boolean flag = true;                                // 是否交换的标记
    for (int i = 1; i < this.curlen && flag; i++) {   // 有交换时再进行下一趟,最多n-1趟
        flag = false;                                   // 假定元素未交换
        for (int j = 0; j < this.curlen - i; j++) {     // 一次比较、交换
            if (r[j].key.compareTo(r[j + 1].key) > 0) { // 逆序时,交换
                temp = r[j];
                r[j] = r[j + 1];
                r[j + 1] = temp;
                flag = true;              //如果发生交换,记录
            }
        }
        System.out.print("第" + i + "趟: ");
        display();
    }
}
  • 测试
public class TestSeqList4_bubble {
    public static void main(String[] args) throws Exception {
        int[] arr = {52,39,67,95,70,8,25,52};
        SeqList seqList = new SeqList(arr.length);
        for (int i = 0; i < arr.length; i++) {
            seqList.insert(i, new RecordNode(arr[i]));
        }
        // 希尔排序算法
        seqList.bubbleSort();
    }
}
//冒泡排序
//第1趟:  39 52 67 70  8 25 52 95
//第2趟:  39 52 67  8 25 52 70 95
//第3趟:  39 52  8 25 52 67 70 95
//第4趟:  39  8 25 52 52 67 70 95
//第5趟:   8 25 39 52 52 67 70 95
//第6趟:   8 25 39 52 52 67 70 95

5)性能分析


时间复杂度:O(n2)


空间复杂度:仅需要一个记录的辅助空间,O(1)


是稳定的排序方法


6)练习


练习1:使用冒泡排序,写出每一趟的排序结果。


序列:16, 15, 50, 53, 64, 7


00711996975e4ca089a024620c5f3354.png


练习2:使用冒泡排序,写出每一趟的排序结果


序列:9 , 20 , 13 , 20 , 12


c0a93626ba2a4b6f835eaf40875512bd.png


三、快速排序


1、什么是快速排序?


快速排序(Quicksort)是对冒泡排序算法的一种改进。


快速排序:Quick Sort


通过一趟排序将要排序的记录分割成独立的两个部分,其中一部分的所有记录的关键字值都比另外一部分的所有记录关键字值小。


然后再按此方法对这两部分记录分别进行快速排序。


整个排序过程可以递归进行,以此达到整个记录序列变成有序。


d6c78afac614497499c5d7881102c2d6.gif


2、快速排序步骤分析


快速排序算法通过多次比较和交换来实现排序,其排序流程如下:


(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。


(2)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。


(3)重复上述过程,可以看出,这是一个递归操作。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。


8b4d872607fc403fa0c5e64eb4d5c331.png


3、算法分析


假设待排序的记录序列为:{ r[row] , r[row+1] , ... , r[high] }


首先在该序列中任意选取一条记录(该记录称为支点,通常选r[row]作为支点)


然后将所有关键字值比支点小的记录都放到它的前面


所有关键字值比支点大的记录都放到它的后面


由此可以将该支点记录最后所落的位置i作为分界线,


将记录序列{ r[row] , r[row+1] , ... , r[i-1], r[i], r[i+1], r[i+2], ... , r[high] }分隔成两个子序列 { r[row] , r[row+1] , ... , r[i-1]} 和 {r[i+1], r[i+2], ... , r[high] }。


这个过程称为一趟快速排序。通过一趟排序,支点记录就落在了最终排序结果的位置上。


cc18c5d6829c42e0bb9cb39928df108a.png


核心思路:


变量i 作用:从左往右寻找比支点大的值,从而交换到后面的位置(上一次j的位置)。


变量 j 作用:从右往左寻找比支点小的值,从而交换到前面的位置(上一次i的位置)。

3b3037b0c2ed4e2ba2742febd9a01a3f.png



4)算法代码实现:一趟快速排序


代码

//【算法】一趟快速排序
//交换排序表r[i..j]的记录,使支点记录到位,并返回其所在位置
//此时,在支点之前(后)的记录关键字均不大于(小于)它
public int Partition(int i, int j) {
    RecordNode pivot = r[i];          //第一个记录作为支点记录
    System.out.println(i + ".." + j + ",  pivot=" + pivot.key + "  ");
    System.out.print("初始值:     ");
    int c = 0;
    display();
    while (i < j) {    //从表的两端交替地向中间扫描
        // 1.1 从后到前,寻找第一个比支点小的元素
        while (i < j && pivot.key.compareTo(r[j].key) <= 0) {
            j--;
        }
        // 1.2 将后面较小的元素,移到前面去
        if (i < j) {
            r[i] = r[j];   //将比支点记录关键字小的记录向前移动
            System.out.print("第"+(++c)+"次交换后:");
            display();
            i++;
        }
        // 2.1 从前到后,选择第一恶比支点大的元素
        while (i < j && pivot.key.compareTo(r[i].key) > 0) {
            i++;
        }
        // 2.2 将前面较大的元素,移到后面去
        if (i < j) {
            r[j] = r[i];   //将比支点记录关键字大的记录向后移动
            System.out.print("第"+(++c)+"次交换后:");
            display();
            j--;
        }
    }
    r[i] = pivot;         //支点记录到位
    System.out.print("一趟完成:   ");
    display();
    return i;             //返回支点位置
}


  • 测试
public class TestSeqList5_part {
    public static void main(String[] args) throws Exception {
        int[] arr = {52,39,67,95,70,8,25,52};
        SeqList seqList = new SeqList(arr.length);
        for (int i = 0; i < arr.length; i++) {
            seqList.insert(i, new RecordNode(arr[i]));
        }
        // 一趟快速排序
        seqList.Partition(0, arr.length - 1);
    }
}
//一趟快速排序
//0..7,  pivot=52
//初始值:      52 39 67 95 70  8 25 52
//第1次交换后: 25 39 67 95 70  8 25 52
//第2次交换后: 25 39 67 95 70  8 67 52
//第3次交换后: 25 39  8 95 70  8 67 52
//第4次交换后: 25 39  8 95 70 95 67 52
//一趟完成:    25 39  8 52 70 95 67 52

5)算法代码实现:完整快排


代码

//【算法】 递归形式的快速排序算法
//对子表r[low..high]快速排序
public void qSort(int low, int high) {
    if (low < high) {
        int pivotloc = Partition(low, high);   // 一趟排序,将排序表分为两部分
        qSort(low, pivotloc - 1);       // 低子表递归排序
        qSort(pivotloc + 1, high);      // 高子表递归排序
    }
}
//【算法完整快排】顺序表快速排序算法
public void quickSort() {
    qSort(0, this.curlen - 1);
}
  • 测试
public class TestSeqList6_quick {
    public static void main(String[] args) throws Exception {
        int[] arr = {52,39,67,95,70,8,25,52};
        SeqList seqList = new SeqList(arr.length);
        System.out.print("数组序列号:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print("  " + i);
            seqList.insert(i, new RecordNode(arr[i]));
        }
        System.out.println();
        // 快速排序
        seqList.quickSort();
    }
}
//快速排序
//数组序列号:  0  1  2  3  4  5  6  7
//0..7,  pivot=52
//初始值:      52 39 67 95 70  8 25 52
//第1次交换后: 25 39 67 95 70  8 25 52
//第2次交换后: 25 39 67 95 70  8 67 52
//第3次交换后: 25 39  8 95 70  8 67 52
//第4次交换后: 25 39  8 95 70 95 67 52
//一趟完成:    25 39  8 52 70 95 67 52
//0..2,  pivot=25
//初始值:      25 39  8 52 70 95 67 52
//第1次交换后:  8 39  8 52 70 95 67 52
//第2次交换后:  8 39 39 52 70 95 67 52
//一趟完成:     8 25 39 52 70 95 67 52
//4..7,  pivot=70
//初始值:       8 25 39 52 70 95 67 52
//第1次交换后:  8 25 39 52 52 95 67 52
//第2次交换后:  8 25 39 52 52 95 67 95
//第3次交换后:  8 25 39 52 52 67 67 95
//一趟完成:     8 25 39 52 52 67 70 95
//4..5,  pivot=52
//初始值:       8 25 39 52 52 67 70 95
//一趟完成:     8 25 39 52 52 67 70 95

6)性能分析


时间复杂度:O( nlog2n )


空间复杂度:O( log2n )


是不稳定排序


7)快速排序练习


练习1:使用快速排序,写出每一趟的排序结果。


序列:16, 15, 50, 53, 64, 7


59e4e697570a48449a3601c665277b1c.png



练习2:使用快速排序,写出每一趟的排序结果


序列:9 , 20 , 13 , 20 , 12

46a36d07f249428ab440f5384d8415ff.png


相关文章
|
1月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
23 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
1月前
|
算法 搜索推荐
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
本文介绍了冒泡排序、选择排序和插入排序三种基础排序算法的原理、实现代码和测试结果。
17 0
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
|
1月前
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
22 1
|
1月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
|
1月前
|
人工智能 搜索推荐 算法
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(三)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
14 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
40 4

热门文章

最新文章