排序排序的概念及其运用和选择排序

排序排序的概念及其运用和选择排序

  • 7. 排序
    • 7.1 排序的概念及其运用
    • 7.2 选择排序算法——直接选择排序
      • 选择排序基本思想:
      • 直接选择排序
        • 选择排序原理
        • 参考程序
      • 如何交换数据
      • 直接选择排序的特性总结:

7. 排序

7.1 排序的概念及其运用

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]==r[j],且r[i]r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

举个例子,同一张试卷,分数相同的情况下,谁先交卷,谁排在前面。

或者总分相同,谁的语文成绩更好,谁排在前。此时对这种特殊需求,排序算法的稳定性就很有必要。

内部排序:数据元素全部放在内存中的排序。(下文也称内排序)

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。(下文也称外排序)

例如 1 G = = 102 4 3 b y t e 1G==1024^3 byte 1G==10243byte,一个int型整数4byte,100亿( 1 0 10 10^{10} 1010)个int型整数占用空间为37G左右,针对这100亿个数据的排序已无法用常见的排序算法去操作。

排序的算法先考虑可行性,再考虑效率。

排序要掌握的点:

  1. 不同排序算法的时间复杂度和空间复杂度
  2. 排序的思想
  3. 排序的实现

排序主要学习这几种:
请添加图片描述

如果是参加算法竞赛,则只需要学习堆排序、快速排序和归并排序即可,遇到竞赛题除非有特殊需求,否则一律用库函数sort解决。

7.2 选择排序算法——直接选择排序

选择排序基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

直接选择排序

选择排序原理
  • 在元素集合array[i]array[n-1]中选择关键码最大(小)的数据元素。

  • 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换。

  • 在剩余的array[i]array[n-2]array[i+1]array[n-1])集合中,重复上述步骤,直到集合剩余1个元素。

排序动图示例:
请添加图片描述

参考程序

以升序排序为例。

根据原理分析,得到的未优化选择排序:

//选择排序,因为传递的是指针,要另外传递元素个数 
void selectSort(int b[],int n) {int i=0,j=0,k=0;for(i=0;i<n;i++) {k=i;//每次更新第一个(或最后一个)元素的位置 for(j=i+1;j<n;j++)if(b[j]<b[k])//遍历寻找最小值k=j;if(k!=i) {//找到的最小值没有放在第一个位置int temp=b[k];b[k]=b[i];b[i]=temp;}}
}

尝试优化:我们在每次遍历数据时,同时找到最大值和最小值的位置并分别放在开头和结尾,还能再减少一半的整体遍历次数。这个思路需要两个起到指针作用的变量分别标记开头和结尾。

这个思路很像双指针,但并不是,只是用到了双指针的思路进行优化。

在实现时我们先看一组样例:

int a[]={7,4,3,6,5,2,3};

我们发现,最大值7和开头位置重合。若按照思路进行枚举,则交换最小值到开头时,数据变化:

(7) 4 3 6 5 (2) 3  ==>  (2) 4 3 6 5 (7) 3

此时2莫名其妙的被判断成最大值和3进行交换。

(2) 4 3 6 5 (7) 3  ==>  3 4 3 6 5 (7) (2)

到这里可以看出,排序没能使数组有序。

解决方法:优化方案会进行两次数据交换,在第一次交换时判断找到的最值是否与开头、结尾重叠,重叠的话进行修正即可。

优化后的选择排序参考程序

//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(int* a, int n) {int begin = 0, end = n - 1;//起标记作用的两个变量while (begin < end) {//能不能加等于,取决于交换数据时用的方法int maxi = begin, mini = begin;for (int i = begin; i <= end; i++) {if (a[i] > a[maxi])maxi = i;if (a[i] < a[mini])mini = i;}Swap(&a[begin], &a[mini]);if (begin == maxi)//处理begin和maxi重叠时的情况maxi = mini;Swap(&a[end], &a[maxi]);++begin;//双指针思路--end;}
}

如何交换数据

在优化的选择排序中,有提到为啥这句能不能用<=取决于交换数据的方法。

程序设计语言进行交换数据的方法有3种:

  1. 设置中间变量

这个是最基础、最常用的方法。但会额外开一个临时变量的空间。若交换的数据比较庞大的话可能需要将庞大的数据分成若干份分别进行交换。

void Swap(Datatype* a,Datatype* b){int tmp = *a;*a = *b;*b = tmp;
}
  1. 三次异或

我们知道,c语言有一个按位异或操作符^,而异或操作的真值表:
请添加图片描述

尽管按位异或是针对二进制位的操作,但如果是两个相同的数据进行异或操作,结果是0。例如:3^3==0的结果为真,因为两个3在内存中存储的二进制形式完全相同,这就使得每一个bit位进行异或时的结果都是0,得到的补码也是0,对应的真实数据自然也是0。

利用这个特点,我们也可以设计出一种不用额外申请空间的数据交换方式:

void Swap(Datatype* a,Datatype* b){Datatype aa=*a,bb=*b;//先进行演示,后进行优化//交换a、b的数据*a=aa^bb^aa;*b=aa^bb^bb;
}

其中aa^bb我们可以用另一个变量进行存储:

void Swap(Datatype* a,Datatype* b){Datatype aa=*a,bb=*b;//先进行演示,后进行优化//交换a、b的数据Datatype tmp=aa^bb;*a=tmp^aa;*b=tmp^bb;
}

我们将tmp更换成aa也没关系,因为此时bb还没发生变化,即使aa的值变成了aa^bb,我们还可以再通过aa^bb^bb的方式重新拿回aa的数据。

void Swap(Datatype* a,Datatype* b){Datatype aa=*a,bb=*b;aa=aa^bb;*b=aa^bb;*a=aa^*b;//运行到这里时,*b已经变成了*a的样子
}

第3行:此时aa存储的数据是*a^*b

第4行:此时bb还未发生改变,aa存储的数据是*a^*b,因此=右边的表达式相当于是*a^*b^*b*b已经变成了*a的样子。

第5行:*b的内容已经变成了*a的数据,而aa存储的数据依旧是*a^*b,所以*a变成了*b

到这里,其实aa和bb作为中间变量的功能已经可以被取代。所以我们得到了一个新的交换数据的方式:

void Swap(Datatype* a,Datatype* b){*a=*a^*b;*b=*a^*b;*a=*a^*b;
}

第2行:*a自己作为中间变量存储*a^*b的数据

第3行:运行完这一句后,*b存储了*a的数据

第4行:*a存储的数据依旧是*a^*b的值,而*b存储的是*a的数据,所以*a存储的数据也变成了*b的值。

例如:

void f00(){int a=3,b=4;printf("%d %d\n",a,b);a=a^b;b=a^b;a=a^b;printf("%d %d\n",a,b);
}

输出:

3 4
4 3

当两个变量的值相等时,第一次异或的值为0,此后再用0参与异或时的值是1,而不是我们想要的值。所以这种数据交换的方式不能用于两个变量一样的情况。而且这种变量也不需要交换数据。
3. 作差

利用数值都有大小的特点,我们可以通过作差得到两数差的部分,再将这部分填到另一部分,这样也能做到交换数据的目的:

void f01(int a,int b){a=a-b;//获得差值b=a+b;//差值填给aa=b-a;//b已变成a,砍掉差值变成b
}

这种交换方式不能用于a和b都代表同一空间的情况,否则数据会丢失(这也是为什么while(begin<end)不加等号的原因,但没多少人会用这种方式进行数据交换,加不加取决于个人爱好)。此外不清楚比较规则的自定义类型也不能用这种数据交换方式。

例如:

void f02(int x){int* a=&x,*b=&x;printf("%d\n",*a,*b);*a=*a-*b;*b=*a+*b;*a=*b-*a;printf("%d\n",*a,*b);
}

输出:

6 6
0 0

直接选择排序的特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

  2. 时间复杂度: O ( N 2 ) O(N^2) O(N2)(两层循环)

  3. 空间复杂度: O ( 1 ) O(1) O(1)

  4. 稳定性:不稳定(要与当前的第一个和最后一个数据进行数据交换,即使是相同的数据也不知道会被移动到哪里)。

例如这个样例:[5,5,3],第一次选择最小数的3时候,不可避免的和第1个5进行交换,这时数组变成了[3,5,5]

我们还可以通过下列代码测试稳定性。只要找到值相同但次序不同的数的位置即可发现算法是否稳定。

#include<stdio.h>typedef struct Num {int x;int i;
}Num;
typedef Num DataType;void Swap(DataType* a, DataType* b) {DataType t = *a;*a = *b;*b = t;
}void selectSort(DataType b[],int n) {int i=0,j=0,k=0;for(i=0;i<n;i++) {k=i;//每次更新第一个(或最后一个)元素的位置 for(j=i+1;j<n;j++)if(b[j].x<b[k].x)//遍历寻找最小值k=j;if(k!=i) {//找到的最小值没有放在第一个位置DataType temp=b[k];b[k]=b[i];b[i]=temp;}}
}//经优化的选择排序,每次同时选出最小的和最大的
void selectSortplus(DataType* a, int n) {int begin = 0, end = n - 1;//起标记作用的两个变量while (begin < end) {//能不能加等于,取决于交换数据时用的方法int maxi = begin, mini = begin;int i=0;for (i = begin; i <= end; i++) {if (a[i].x > a[maxi].x)maxi = i;if (a[i].x < a[mini].x)mini = i;}Swap(&a[begin], &a[mini]);if (begin == maxi)//处理begin和maxi重叠时的情况maxi = mini;Swap(&a[end], &a[maxi]);++begin;//双指针思路--end;}
}void f() {srand((size_t)time(0));//随机数的种子Num a[30] = { {0,0} },b[30] = { {0,0} };int i=0;for (i = 0; i < 30; i++) {a[i].x = rand()%100+1;a[i].i = i + 1;b[i] = a[i];}for (i = 0; i < 30; i++)printf("%d %d,",a[i].x,a[i].i);printf("\n\n");selectSort(a, 30);selectSortplus(b, 30);for (i = 0; i < 30; i++)printf("%d %d,", a[i].x, a[i].i);printf("\n\n");for (i = 0; i < 30; i++)printf("%d %d,", b[i].x, b[i].i);
}int main(){f();return 0;
}

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

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

相关文章

centos和ubuntu有什么区别?

CentOS与Ubuntu的区别在于以下几个方面&#xff1a;1、源码基础不同&#xff1b;2、设计理念不同&#xff1b;3、使用场景不同&#xff1b;4、软件包管理不同&#xff1b;整体上看&#xff0c;CentOS更倾向于企业级服务器应用&#xff0c;稳定性较强&#xff1b;而Ubuntu更注重…

Websocket如何分块处理数据量超大的消息体

若我们服务端一次性最大处理的字节数是1M,而客户端发来了2M的数据&#xff0c;此时服务端的数据就要被切割成两次传输解码。Http协议中有分块传输&#xff0c;而在Websocket也可以分块处理超大的消息体。在jsr356标准中使用javax.websocket.MessageHandler.Partial可以分块处理…

ELMo模型介绍:深度理解语言模型的嵌入艺术

ELMo模型介绍&#xff1a;深度理解语言模型的嵌入艺术 引言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;词嵌入&#xff08;word embedding&#xff09;是将词汇或短语从词汇表映射到向量的数学表示&#xff0c;这些向量能够捕捉词汇之间的语义和语法关系。E…

【澜舟科技-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

C++ 编程基础(8)模版 | 8.3、类模版

文章目录 一、类模版1、定义2、模板参数3、模版的实例化4、模版的特化5、模版成员函数6、总结 前言&#xff1a; 这里是引用 一、类模版 1、定义 类模板的定义以template关键字开始&#xff0c;后面跟着一个模板参数列表&#xff08;用尖括号< >包围&#xff09;。模板参…

HarmonyOS NEXT应用开发实战:十二、远场通信RCP简单好用的模块化封装

在进行HarmonyOS的应用开发中&#xff0c;我们常常需要进行网络通信。然而&#xff0c;原始的远场通信&#xff08;RCP&#xff09;使用方式较为繁琐&#xff0c;让人感到不够便捷。作为一位前期从事小程序开发的开发者&#xff0c;我深受小程序网络访问的简单性和便利性的吸引…

uni-app快速入门(十)--常用内置组件(下)

本文介绍uni-app的textarea多行文本框组件、web-view组件、image图片组件、switch开关组件、audio音频组件、video视频组件。 一、textarea多行文本框组件 textarea组件在HTML 中相信大家非常熟悉&#xff0c;组件的官方介绍见&#xff1a; textarea | uni-app官网uni-app,un…

Tomcat 如何管理 Session

Tomcat 如何管理 Session 我们知道&#xff0c;Tomcat 中每一个 Context 容器对应一个 Web 应用&#xff0c;而 Web 应用之间的 Session 应该是独立的&#xff0c;因此 Session 的管理肯定是 Context 级的&#xff0c;也就是一个 Context 一定关联多个 Session。 Tomcat 中主…

Flink vs Spark

Flink vs Spark Flink和Spark都是大数据处理领域的热门分布式计算框架&#xff0c;它们有各自的特点和优势&#xff0c;适用于不同的场景。本文对两者进行对比。 一、技术理念与架构 Flink&#xff1a; 基于事件驱动&#xff0c;面向流的处理框架。支持真正的流计算&#xff0c…

鸿蒙NEXT开发-用户通知服务的封装和文件下载通知

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…

01 IP路由基础

一、路由器是怎么转发数据包 • 当数据包到达路由器之后&#xff0c;根据数据包的目的 IP 地址&#xff0c;查找 路由表&#xff0c;并根据路由表中相应的路由所指示出接口还有下一跳 指导数据包在网络中的转发。 • 如果路由器路由表没有路由怎么办&#xff1f; -------- 将数…

Android studio 呼叫盒app

一、权限文件 0.gradle切换国内源 #Fri Nov 08 15:46:05 CST 2024 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-8.4-bin.zip zipStoreBaseGRADLE_USER_HOME zipStorePathwrapper/dists1…

[Admin] Dashboard Filter for Mix Report Types

Background RevOps team has built a dashboard for sales team to track team members’ performance, but they’re blocked by how to provide a manager view based on sales’ hierarchy. Therefore, they seek for dev team’s help to clear their blocker. From foll…

网络技术-路由协议

路由协议是网络中确保数据包能够有效地从源节点传递到目的节点的重要机制。以下是常见的几种路由协议&#xff1a; 一、根据算法分类 1.距离向量路由协议&#xff08;Distance Vector Routing Protocol&#xff09; RIP&#xff08;Routing Information Protocol&#xff09;&…

2024年人工智能技术赋能网络安全应用测试:广东盈世在钓鱼邮件识别场景荣获第三名!

近期&#xff0c;2024年国家网络安全宣传周“网络安全技术高峰论坛主论坛暨粤港澳大湾区网络安全大会”在广州成功举办。会上&#xff0c;国家计算机网络应急技术处理协调中心公布了“2024年人工智能技术赋能网络安全应用测试结果”。结果显示&#xff0c;广东盈世计算机科技有…

Java进阶四-异常,File

异常 概念&#xff1a;代表程序出现的问题。 目的&#xff1a;程序出现了异常我们应该如何处理。 最高父类&#xff1a;Exception 异常分为两类 编译时异常&#xff1a;没有继承RuntimeException的异常,直接继承与Exception,编译阶段就会错误提示。运行时异常:RuntimeExc…

Gin 框架中的路由

1、路由概述 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。 RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路 由的时候建议参考 …

ERROR TypeError: AutoImport is not a function

TypeError: AutoImport is not a function 原因&#xff1a;unplugin-auto-import 插件版本问题 Vue3基于Webpack&#xff0c;在vue.config.js中配置 当unplugin-vue-components版本小于0.26.0时&#xff0c;使用以下写法 const { defineConfig } require("vue/cli-se…

Elasticsearch:更好的二进制量化(BBQ)对比乘积量化(PQ)

作者&#xff1a;来自 Elastic Benjamin Trent 为什么我们选择花时间研究更好的二进制量化而不是在 Lucene 和 Elasticsearch 中进行生产量化。 我们一直在逐步使 Elasticsearch 和 Lucene 的向量搜索变得更快、更实惠。我们的主要重点不仅是通过 SIMD 提高搜索速度&#xff0…

检查课程是否有效

文章目录 概要整体架构流程技术细节小结 概要 这是一个微服务内部接口&#xff0c;当用户学习课程时&#xff0c;可能需要播放课程视频。此时提供视频播放功能的媒资系统就需要校验用户是否有播放视频的资格。所以&#xff0c;开发媒资服务&#xff08;tj-media&#xff09;的…