数据结构与算法·第10章【内部排序】

news2024/11/24 13:44:43

概念

排序问题可以分为内部排序和外部排序。若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序;反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。

在内部排序中,若对于两个相等的元素 K i 和 K j ( i ≠ j ) Ki 和 Kj(i≠j) KiKji=j,在排序前的序列中 R i Ri Ri 领先于 R j Rj Rj(即 i < j i<j i<j),排序后的序列中 Ri 仍领先于 Rj,则称所用的排序方法是稳定的;反之,若可能使排序后的序列中 R j Rj Rj 领先于 R i Ri Ri,则称所用的排序方法是非稳定的。

插入排序

插入排序的基本思想是将待排序的序列分成已排序和未排序的两部分,每次从未排序的部分取出第一个元素,插入到已排序部分合适的位置,直到未排序部分为空为止。具体操作如下:

  1. R [ 1.. i − 1 ] R[1..i-1] R[1..i1] 中查找 R [ i ] R[i] R[i] 的插入位置,使得 R [ 1.. j ] . k e y ≤ R [ i ] . k e y < R [ j + 1.. i − 1 ] . k e y R[1..j].key ≤ R[i].key < R[j+1..i-1].key R[1..j].keyR[i].key<R[j+1..i1].key
  2. R [ j + 1.. i − 1 ] R[j+1..i-1] R[j+1..i1] 中的所有记录均后移一个位置;
  3. R [ i ] R[i] R[i] 插入(复制)到 R [ j + 1 ] R[j+1] R[j+1] 的位置上。

这样就完成了一次插入操作,对于待排序的序列,重复执行以上操作直到全部排序完成。

直接插入排序

直接插入排序的基本思想是将待排序的序列分成已排序和未排序的两部分,每次从未排序的部分取出第一个元素,插入到已排序部分合适的位置,直到未排序部分为空为止。具体操作如下:

  1. 初始时,将 R [ 1 ] R[1] R[1] 看作是有序区, R [ 2.. n ] R[2..n] R[2..n] 构成无序区;
  2. 依次将无序区的元素插入到有序区中,使得有序区始终有序。插入操作包括以下三步:
    1)在 R [ 1.. i − 1 ] R[1..i-1] R[1..i1] 中查找 R [ i ] R[i] R[i] 的插入位置,使得 R [ 1.. j ] . k e y ≤ R [ i ] . k e y < R [ j + 1.. i − 1 ] . k e y R[1..j].key ≤ R[i].key < R[j+1..i-1].key R[1..j].keyR[i].key<R[j+1..i1].key
    2)将 R [ j + 1.. i − 1 ] R[j+1..i-1] R[j+1..i1] 中的所有记录均后移一个位置;
    3)将 R [ i ] R[i] R[i] 插入(复制)到 R [ j + 1 ] R[j+1] R[j+1] 的位置上。
  3. 重复执行 2 直到无序区为空,排序完成。
void InsertionSort(SqList& L) {
    // 对顺序表 L 作直接插入排序
    for (int i = 2; i <= L.length; ++i) {
        if (L.r[i].key < L.r[i - 1].key) {
            // 将当前待排序的记录暂存到监视哨中,等待插入
            L.r[0] = L.r[i];
            int j;
            for (j = i - 1; L.r[0].key < L.r[j].key; --j) {
                // 将记录后移,寻找插入位置
                L.r[j + 1] = L.r[j];
            }
            L.r[j + 1] = L.r[0]; // 插入到正确位置
        }
    }
}

最好的情况:

  • 序列顺序有序,比较的次数:n-1,移动的次数:0

最坏的情况:
在这里插入图片描述
时间复杂度大概 O ( n 2 ) O(n^2) O(n2)

其他插入排序

折半插入

void BiInsertionSort(SqList &L) {
    for (int i = 2; i <= L.length; ++i) {
        L.r[0] = L.r[i];      // 将 L.r[i] 暂存到 L.r[0]
        int low = 1, high = i - 1;
        while (low <= high) { 
            int mid = (low + high) / 2; // 折半
            if (L.r[0].key < L.r[mid].key)
                high = mid - 1;   // 插入点在低半区
            else
                low = mid + 1;    // 插入点在高半区
        }
        for (int j = i - 1; j >= high + 1; --j) {
            L.r[j + 1] = L.r[j];      // 记录后移
        }
        L.r[high + 1] = L.r[0];  // 插入
    } 
}

L.r[high + 1] = L.r[0]; // 插入是在 h i g h + 1 high+1 high+1的位置插入(此时,low>high)

希尔排序

希尔排序(又称缩小增量排序)

基本思想:对待排记录序列先作“宏观”调整,再作“微观”调整。所谓“宏观”调整,指的是“跳跃式”的插入排序。具体做法为:

1.将记录序列分成若干子序列,分别对每个子序列进行插入排序。

2.待整个序列中的纪录‘基本有序’时,再对全体记录进行一次直接插入排序。

例如:将 n n n个记录分成 d d d个子序列:

R [ 1 ] , R [ 1 + d ] , R [ 1 + 2 d ] , … , R [ 1 + k d ] { R[1],R[1+d],R[1+2d],…,R[1+kd] } R[1]R[1+d]R[1+2d]R[1+kd]

R [ 2 ] , R [ 2 + d ] , R [ 2 + 2 d ] , … , R [ 2 + k d ] { R[2],R[2+d],R[2+2d],…,R[2+kd] } R[2]R[2+d]R[2+2d]R[2+kd]

… …

R [ d ] , R [ 2 d ] , R [ 3 d ] , … , R [ k d ] , R [ ( k + 1 ) d ] { R[d],R[2d],R[3d],…,R[kd],R[(k+1)d] } R[d]R[2d]R[3d]R[kd]R[(k+1)d]

其中, d d d 称为增量,它的值在排序过程中从大到小逐渐缩小,直至最后一趟排序减为 1。

在这里插入图片描述

冒泡排序

void BubbleSort(Elem R[], int n) {
    int i = n;
    while (i > 1) { 
        int lastExchangeIndex = 1; 
        for (int j = 1; j < i; j++) {
            if (R[j+1].key < R[j].key) { 
                Swap(R[j], R[j+1]);
                lastExchangeIndex = j;
            } //if
        } //for
        i = lastExchangeIndex; // 本趟进行过交换的最后一个记录的位置          
    } // while
} // BubbleSort
  1. 起泡排序的结束条件为,最后一趟没有进行“交换记录”。

  2. 一般情况下,每经过一趟“起泡”,i减1,但并不是每趟都如此。具体来说,每一趟排序时,我们都记录下最后一次交换操作的位置,如果在一趟排序结束之后,最后一次交换操作的位置和上一趟排序结束时的位置相同,那么说明这次排序并没有进行任何交换操作,也就是说从该位置之后的元素已经有序。此时,我们便可以认为序列已经有序了,因此结束算法的执行。

在这里插入图片描述
时间复杂度大概 O ( n 2 ) O(n^2) O(n2)

快速排序

找一个记录,以它的关键字作为“枢轴”,凡其关键字小于枢轴的记录均移动至该记录之前,反之,凡关键字大于枢轴的记录均移动至该记录之后。

经过一趟排序之后,记录的无序序列 R [ s . . t ] R[s..t] R[s..t]将分割成两部分: R [ s . . i − 1 ] R[s..i-1] R[s..i1] R [ i + 1.. t ] R[i+1..t] R[i+1..t],且 R [ j ] . k e y ≤ R [ i ] . k e y ≤ R [ p ] . k e y ( s ≤ j ≤ i − 1 )  枢轴  ( i + 1 ≤ p ≤ t ) R[j].key\leq R[i].key \leq R[p].key (s\leq j\leq i-1)~ 枢轴 ~(i+1\leq p\leq t) R[j].keyR[i].keyR[p].key(sji1) 枢轴 (i+1pt)其中 i i i表示枢轴记录的位置, j ≤ i − 1 j \leq i-1 ji1的记录的关键字都小于等于枢轴的关键字, p ≥ i + 1 p \geq i+1 pi+1的记录的关键字都大于等于枢轴的关键字。注意,这里假设枢轴所在的位置不是 s s s t t t,否则就没有对应的一侧了。

在这里插入图片描述

int Partition (RedType& R[], int low, int high) {
    pivotkey = R[low].key;  // 用子表的第一个记录作枢轴记录
    while (low < high) {    // 从表的两端交替地向中间扫描
        while (low < high && R[high].key >= pivotkey)    
            --high;  
        R[low] ←→ R[high];   // 将比枢轴记录小的记录交换到低端
        while (low < high && R[low].key <= pivotkey) 
            ++low;   
        R[low] ←→ R[high];  // 将比枢轴记录大的记录交换到高端
    }
    return low;  // 返回枢轴所在位置
} // Partition

void QSort (RedType & R[], int low, int high) {
    // 对记录序列R[low..high]进行快速排序
    if (low < high) {  // 长度大于1
        pivotloc = Partition(R, low, high);
        // 对 R[s..t] 进行一次划分
        QSort(R, low, pivotloc - 1);
        // 对低子序列递归排序,pivotloc是枢轴位置
        QSort(R, pivotloc + 1, high); // 对高子序列递归排序
    }
} // QSort

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

选择排序

在这里插入图片描述
从未排序的序列选择一个最小的元素排到有序序列里

void SelectSort(Elem R[], int n) {
    // 对记录序列 R[1..n] 进行简单选择排序。
    for (int i = 1; i < n; ++i) {
        // 选择第 i 小的记录,并交换到位
        int j = SelectMinKey(R, i);
        // 在 R[i..n] 中选择关键字最小的记录
        if (i != j) {
            // 与第 i 个记录交换
            R[i] ↔ R[j];
        }
    }
} // SelectSort

在这里插入图片描述

堆排序

在这里插入图片描述
在这里插入图片描述
光看定义还有点不太明白,但是根据就应该很明晰了

建大顶堆

在这里插入图片描述
在这里插入图片描述
自下而上

习题

各个排序

在这里插入图片描述
后面2个考试不涉及

  • 直接插入排序:503 087 512 061 908 170 897 275 653 426
    第一趟结果:087 503 512 061 908 170 897 275 653 426
    第二趟结果:087 503 512 061 908 170 897 275 653 426
    第三趟结果:061 087 503 512 908 170 897 275 653 426
    第四趟结果:061 087 503 512 908 170 897 275 653 426
    第五趟结果:061 087 170 503 512 908 897 275 653 426
    第六趟结果:061 087 170 503 512 897 908 275 653 426
    第七趟结果:061 087 170 275 503 512 897 908 653 426
    第八趟结果:061 087 170 275 503 512 653 897 908 426
    第九趟结果:061 087 170 275 426 503 512 653 897 908
    粗体为已经排好序的序列

  • 希尔排序初始关键字: 503 087 512 061 908 170 897 275 653 426
    第一趟结果:d[1]=5 170 087 275 061 426 503 897 512 653 908
    第二趟结果:d[2]=3 061 087 275 170 426 503 897 512 653 908
    第三趟结果:d[3]=1 061 087 170 275 426 503 512 653 897 908
    主要注意一下希尔排序是以下标号的后x个作排序——在d[1]=5,a[0]=503是a[5]=170排序的

  • 快速排序在这里插入图片描述
    注意,快速排序不是把比Key小的数直接随便放到Key前面,是用low和high遍历出来的

  • 堆排序
    在这里插入图片描述
    小顶堆

堆排序

在这里插入图片描述
在这里插入图片描述

监视哨

在这里插入图片描述

void directInsertSort(int L[], int k) {
    int i, j;
    for (i = 2; i <= k; i++) {
        L[k+1] = L[i]; 
        j = i - 1;
        while (L[j] > L[0]) {
            L[j + 1] = L[j];
            j--;
        }
        L[j + 1] = L[0]; 
    }
}

设计算法

在这里插入图片描述

void process(int A[n]) {
    int low = 0;
    int high = n - 1;
    while (low < high) {
        while (low < high && A[low] < 0)
            low++;
        while (low < high && A[high] > 0)
            high++;
        if (low < high) {
            // 交换 A[low] 和 A[high]
            int temp = A[low];
            A[low] = A[high];
            A[high] = temp;
            low++;
            high--;
        }
    }
    return;
}

双指针法
时间复杂度 O ( n 2 ) O(n^2) O(n2)

荷兰国旗问题

在这里插入图片描述

typedef enum {RED, WHITE, BLUE} color; // 定义枚举类型表示三种颜色

void Flag_Arrange(color a[], int n) {
    int i = 0;
    int j = 0;
    int k = n - 1;

    while (j <= k) {
        switch (a[j]) {
            case RED:
                // a[i] 与 a[j] 交换
                // 增加 i 和 j 的值,同时继续处理下一个元素
                swap(a[i], a[j]);
                i++;
                j++;
                break;
            case WHITE:
                // 当遇到白色时,只需要将 j 向右移动一位
                j++;
                break;
            case BLUE:
                // a[j] 与 a[k] 交换
                // 不增加 j 的值,因为可能需要再次检查交换后的 a[j]
                // 减少 k 的值,将蓝色元素移至数组末尾
                swap(a[j], a[k]);
                k--;
                break;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/662096.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

成为一名成功的项目经理,你需要了解这些

作为一名有抱负的项目经理&#xff0c;你需要了解自己的职责和任务。你的职责不仅仅是确保项目的成功&#xff0c;更要负责带领团队制定可靠的执行计划&#xff0c;并确保所有工作按计划有序进行。因此&#xff0c;你必须具备身兼多职的能力&#xff0c;以确保项目能够顺利完成…

机器视觉初步5-2:图像增强专题

图像增强是一种提高图像质量和信息量的技术&#xff0c;常用于图像处理、计算机视觉和机器学习中。常见的图像增强方法包括直方图均衡化、高斯滤波、锐化、对比度拉伸、图像平滑、图像锐化、图像滤波、图像金字塔等。 以下是一些常见的图像增强方法的示例代码&#xff0c;使用H…

数据结构——顺序表(文字+代码+带图详细讲解)

在 C 语言中&#xff0c;顺序表可以使用数组来实现。顺序表是一种线性表&#xff0c;其中的元素在物理上连续存储&#xff0c;可以通过下标访问任意元素。 顺序表的基本操作包括插入、删除、查找、遍历和初始化等。 这段代码定义了一个顺序表的结构体&#xff0c;其中包括三个…

《C++多态》

文章目录 思维导图一、多态的概念二、多态的定义及其实现1.多态的构成条件2.虚函数3.虚函数的重写不构成多态的情况展示4.虚函数重写的两个例外4.1 协变4.2析构函数的重写 5.C11 override和final5.1. final5.2.override 三、抽象类1.概念2、对比纯虚函数与override3.接口继承和…

suse linux安装介质下载

在suse官网注册一个账号&#xff0c;就可以免费在上面下载软件的安装介质。 SUSE HAE介质下载和安装说明&#xff1a;

工作十年还不知道数字化转型工具?别等老板问你时才去查资料!

在职场中&#xff0c;到底有什么比较好上手又能轻易提升数字化的工具&#xff1f; 应粉丝邀请&#xff0c;我来给出一个回答。 对于企业来说&#xff0c;你可以选择大屏。对于个人来说&#xff0c;你可以选择仪表板。 工作汇报已经越来越卷&#xff0c;对于个人来说&#xff0c…

Windows远程桌面(mstsc)不能复制粘贴的解决办法

最近突然发现Windows远程桌面(mstsc)不能在远程端和本地端之间自由的复制和粘贴了&#xff0c;这还是非常影响使用体验的&#xff1b;因此记录一下解决方法&#xff0c;以便后续再遇到此类问题时查看如何解决&#xff1b; 文章目录 一、背景二、解决办法2.1 方法1 重启rdpclip.…

ChatGPT或致全球3亿人失业,人工智能时代下教育会发生什么样的变革?

不久前&#xff0c;谷歌教育发布了一份关于未来教育的研究报告。该报告由谷歌公司和 Canvas8合作&#xff0c;对来自世界24个国家的94位教育专家进行了历时长达2年的调研&#xff0c;探讨了未来教育形态、教育在未来的作用、教育公平、全球人才需求、教学方式、学习生态、工作技…

Attentive Moment Retrieval in Videos论文笔记

Attentive Moment Retrieval in Videos论文笔记 0.论文地址1.摘要2.引言3.模型结构3.1Memory Attention Network3.2Cross-Modal Fusion Network 4.训练4.1对齐损失4.2定位回归损失4.3合并 5.实验5.1数据集5.2效果5.3ACRN的研究 6未来工作 0.论文地址 2018 Attentive Moment Re…

验证码客户端回显测试-业务安全测试实操(15)

验证码客户端回显测试,验证码绕过测试,验证码自动识别测试 往期文章: 验证码暴力破解测试-业务安全测试实操(13)_luozhonghua2000的博客-CSDN博客 验证码客户端回显测试 测试原理和方法 当验证码在客户端生成而非服务器端生成时,就会造成此类问题。当客户端需要和服务器进行…

【正点原子STM32连载】第三十五章 IIC实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html# 第三…

2023年NPDP产品经理认证线上班,到这里

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

docker 镜像制作 与 CI/CD

目录 镜像到底是什么&#xff1f; 使用docker创建镜像 步骤&#xff1a; 1、编辑Dockerfile&#xff08;Dockerfile是docker制作镜像的配方文件&#xff09; 2、编辑requirements.txt文件 3、编辑app.py文件&#xff0c;我们的程序文件 4、生成镜像文件 5、查看生成的镜…

这些软件,你知道几个呢?

软件分享一&#xff1a;情绪指压 情绪指压(MoodPress)是一款记录心情的应用&#xff0c;也是一款非常简单的减压游戏。可以根据自己现在的心情来决定指压的力度和时间&#xff08;压力越大或者时间越长越生气&#xff09;&#xff0c;适时释放&#xff0c;来判断和记录自己的心…

Oracle19c安装和远程访问设置

Oracle Database&#xff0c;又名Oracle RDBMS&#xff0c;或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统&#xff0c;系统可移植性好、使用方便、功能强&#xff0c…

cron表达式 字符含义 详细解释

cron表达式的组成 cron表达式是一个字符串&#xff0c;由6到7个字段组成&#xff0c;用空格分隔。 其中前6个字段是必须的&#xff0c;最后一个年是可选填的。 cron表达式的字段含义 字段允许值通用字符秒0-59* , - /分0-59时0-23日期1-31 ? L C W月1-12 JAN-DEC星期1-7 SU…

单片机原理及接口技术 - 第三版 张毅刚 习题答案

第 1 章 思考题及习题 1 参考答案 一、填空 1.除了单片机这一名称之外&#xff0c;单片机还可称为 微控制器 或 嵌入式控制器 2.单片机与普通微型计算机的不同之处在于其将 CPU、存储器、I/O 口三部分,通过内部总线连接一起&#xff0c;集成芯片上。 3. AT89S52 单片机工作频率…

高并发缓存实战RedisSon、性能优化

高并发缓存实战RedisSon、性能优化 分布式锁性能提升 1.数据冷热分离 对于经常访问的数据保留在redis缓存当中&#xff0c;不用带数据设置超时时间定期删除控制redis的大小 String productStr redisUtil.get(productCacheKey);if (!StringUtils.isEmpty(productStr)) {prod…

docker搭建nginx

一、安装Docker 1、安装&#xff1a; yum install docker 2、启动/停止/重启docker服务 service docker start service docker stop service docker restart 3、查看docker版本信息 docker version 4、查看所有docker镜像 docker images 二、安装Nginx 1、拉取Nginx镜像…

关于POL网络中的ODN部署方案,这些你都了解吗?

近年来&#xff0c;行业的智能化和信息化呈现加速发展趋势&#xff0c;高清视频会议、云服务、移动办公等应用几乎成为企业标配。与此同时&#xff0c;带宽的接入、升级和物联网融合等网络新要求也变得越来越迫切&#xff0c;网络架构升级成为企业解决网络难题的一个新选择。 …