排序算法分析

我们都知道,在应届面试的时候,问到最多的就是快速排序,快速排序是最经典、最常用的排序算法,因为它的平均效率最优,也最稳定。

快速排序使用了分治的算法思想,分治算法本身理解起来很符合人类的思路(递归是很容易被理解的),其最关键的一步,就是划分了,算法导论上介绍了一种划分方法,和我在数据结构课上学的略有不同,昨晚,我把它们全都用java实现了。

这个是算法导论的版本:

 1 /**
 2      * 快速排序的划分方法一:算法导论版本 sit的下一个比最后一个大
 3      * 
 4      * @param begin
 5      * @param end
 6      * @return
 7      */
 8     private int QuickMergeA(int begin, int end) {
 9         System.out.print("初始:" + arr);
10         System.out.print(begin + " " + end);
11         int anchor = (int) arr.get(end);
12         int sit = begin - 1;
13         int temp;
14         for (int i = begin; i < end; i++) {
15             if ((int) arr.get(i) <= anchor) {
16                 sit++;
17                 temp = (int) arr.get(i);
18                 arr.set(i, (int) arr.get(sit));
19                 arr.set(sit, temp);
20             }
21         }
22         temp = (int) arr.get(sit + 1);
23         arr.set(sit + 1, anchor);
24         arr.set(end, temp);
25         System.out.print(arr);
26         System.out.println(sit + 1);
27         return sit + 1;
28     }

不难看出,这个版本的划分思路是,从数组一端往另一端推进,对于比指定元素小的值,均放在左侧,比指定元素大的值,放在右侧。

然后我们再看我数据结构课本上学过的版本:

/*** 快速排序的划分方法二:数据结构课本版本* * @param begin* @param end* @return*/private int QuickMergeB(int begin, int end) {System.out.print("初始:" + arr);System.out.print(begin + " " + end);int anchor = arr.get(end);int send = end - 1;int temp;int tbegin = begin;while (begin <= send) {while (arr.get(begin) <= anchor && begin <= end - 1) {begin++;}while (send >= tbegin && arr.get(send) > anchor) {send--;}if (begin < send) {temp = arr.get(begin);arr.set(begin, arr.get(send));arr.set(send, temp);}if (begin >= send) {temp = arr.get(begin);arr.set(begin, arr.get(end));arr.set(end, temp);}}System.out.print(arr);System.out.println(send + 1);return send + 1;}

可以看出,这种思路是两端推进的手段,两端同时推进,同样是左边存放比指定元素小的值,右边存放比指定元素大的值。

 

这两种方法遍历数组的推进方法虽然不同,但是其效率是一致的,划分一次都相当于是要遍历一次子数组。其划分的包装入口也是相似的:

/*** 快速排序入口* * @param begin* @param end*/private void QuickSequence(int begin, int end) {// if (begin < end) {// int q = QuickMergeA(begin, end);// this.QuickSequence(begin, q - 1);// this.QuickSequence(q + 1, end);// }if (begin < end) {int q = QuickMergeB(begin, end);this.QuickSequence(begin, q - 1);this.QuickSequence(q + 1, end);}}

需要指明的是,测试上面方法的过程中,应该把arr数组设置为静态的,在java中,实现类似于C++的地址访问的情况,我经常使用静态变量。

 

最古老的排序算法是插入排序,之前学习数据结构的时候,对于各种排序算法总是混淆,比如对于快速排序和插入排序的算法思路都明白,但是总是把快速排序当成插入排序,插入排序当成快速排序。也许是当时两节课学了冒泡排序、快速排序、插入排序、堆排序等太多的排序算法吧,加上编程经验少造成的。

 

其实插入排序很容易和快速排序区分开。因为插入排序是真的“插入”。插入排序的基础是左侧数据已经排序准确,你要做的是把下一个数插入到有序数组的指定位置,你可以脑补扑克牌。

/*** 插入排序*/private void InsertSequence() {for (int i = 0; i < arr.size(); i++) {int p = 0;while (p < i && arr.get(p) < arr.get(i)) {p++;}if (p < i) {int temp = arr.get(i);for (int k = i; k > p; k--) {arr.set(k, arr.get(k - 1));}arr.set(p, temp);}}}

插入排序的代码很短,但是我们的经验是,短的代码代表思路简单,不代表效率高。其实是这样的,插入排序的效率是N^2,而快速排序的效率则是NlogN。插入排序是最朴素的排序算法,也是相对较慢的排序算法。

 

学数据结构的课程时,我非常喜欢堆排序。或许是因为它是最形象的排序算法。其实,堆排序利用特有的数据结构,高效的做了这么一件事,每次找出最小的数,然后从数组中删掉这个数,继续查找。

 1 private void MaintainHeap(int length) {
 2         int temp;
 3         for (int i = length / 2 - 1; i >= 0; i--) {
 4             if ((arr.get(i) < arr.get((i + 1) * 2 - 1))
 5                     || ((i + 1) * 2 < length && arr.get(i) < arr
 6                             .get((i + 1) * 2))) {
 7                 if ((i + 1) * 2 < length) {
 8                     if (arr.get((i + 1) * 2) < arr.get((i + 1) * 2 - 1)) {
 9                         temp = arr.get(i);
10                         arr.set(i, arr.get((i + 1) * 2 - 1));
11                         arr.set((i + 1) * 2 - 1, temp);
12                     } else {
13                         temp = arr.get(i);
14                         arr.set(i, arr.get((i + 1) * 2));
15                         arr.set((i + 1) * 2, temp);
16                     }
17 
18                 } else {
19                     temp = arr.get(i);
20                     arr.set(i, arr.get((i + 1) * 2 - 1));
21                     arr.set((i + 1) * 2 - 1, temp);
22                 }
23 
24             }
25         }
26 
27         System.out.println(arr);
28     }
29 
30     private void Heapsequence(int length) {
31         for (int i = length; i > 0; i--) {
32             this.MaintainHeap(i);
33             int temp = arr.get(0);
34             arr.set(0,arr.get(i - 1));
35             arr.set(i - 1, temp);
36         }
37     }

堆排序的重要步骤是堆的维护。对于最大堆而言,要确保特定个元素构建的堆中,每个节点的值都大于其孩子节点,这通常的方法是,从最后一个有子节点的节点开始,遍历到根节点,对于这其间的每一个结点,如果其子节点存在大于当前节点的节点,则把最大的子节点同当前节点交换,并同时维护交换后的子节点的状态(这个子节点的子节点可能在交换后比子节点的值大)。

 

除了上面的排序算法,我们常见的还有冒泡排序之类。同时,我在算法导论上,还接触过计数排序、基数排序、桶排序等线性排序算法。因为其思路比较简单,所以这里只给出一般思路:

计数排序顾名思义,使用了一个计数数组,对于每一个值,初始计数为0,每新增一个,该计数就增加一,最后形成一个排序序列。计数排序的效率一般,但是由于其稳定,常常作为其它排序算法基本过程的算法使用。

基数排序是另外一种比较有意思的排序算法,它先从最低位对数据进行排序,然后再依次上升到最高位,而对每个数位的排序,他通常使用计数排序。

桶排序是效率最高的排序算法,但是它对数据要求较高,同时是一种以空间换时间的排序算法。它的方案是先new一个足够大的数组,然后对待排序数组遍历,将新数组键等于待排序数组中值的位置置为一(多个则继续增一),然后遍历新数组,即可得到待排序数组的顺序。这种算法不适合离散性数据,也不适合不知道范围的数据排序。

 

对于常规排序算法,冒泡排序适合只有少数数据不在正确位置上的数据,快速排序的平均效率最高,堆排序性能极不稳定,插入排序的效率较差。

 

以上。

 

转载于:https://www.cnblogs.com/toocooltohavefriends/p/5349375.html

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

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

相关文章

sklearn 线性回归_使用sklearn库做线性回归拟合

背景资料随着海拔高度的上升&#xff0c;温度越来越低&#xff0c;经过气象专家的研究&#xff0c;在一定的海拔高度范围内&#xff0c;高度和温度呈线性关系。现有一组实测资料&#xff0c;我们需要对这些数据进行处理拟合&#xff0c;获得此线性关系。解决思路采用sklearn库中…

VS2022之DebuggerVisualizer

在Debug程序时&#xff0c;面对一些大集合&#xff0c;之前是这样查看的&#xff0c;如下图&#xff0c;这样看起来不直观&#xff0c;集合中的数据只能一个一个实体查看&#xff1a;VS2022预览版带来一个新功能&#xff0c;集合表格可视化&#xff0c;比如下面这样一段代码&am…

Web程序员的Mysql进阶序一之sql使用分类及基础

一般sql语句分为3类&#xff1a;DDL、DML和DCL。 DDL&#xff1a;数据库定义类操作&#xff0c;例如&#xff1a;create、drop、alter DML&#xff1a;数据库数据操作&#xff0c;例如&#xff1a;insert、delete、update、select DCL&#xff1a;数据库权限安全操作&#xff0…

NPOI 将DataGridView导出到Excel

导出为xls格式用HSSF&#xff0c;xlsx用XSSF。 1、类 using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Text; using NPOI.HSSF.UserModel; using System.IO; using NPOI.SS.UserModel;public class ExportExce…

android log.d 格式化,android – 在我的代码中使用Log.d()或Log.e()

考虑使用它&#xff1a;isLoggable()Checks to see whether or not a log for the specified tag is loggable at the specified level. The default level of any tag is set to INFO. This means that any level above and including INFO will be logged. Before you make a…

Java 调用存储过程 返回结果集

Java 调用存储过程 返回结果集 初学Java调用存储过程返回一行或多行结果集的实例转载于:https://www.cnblogs.com/kingxiaozi/p/4634803.html

loadrunner发送json_Loadrunner模拟JSON接口请求进行测试

Loadrunner模拟JSON接口请求进行测试一、loadrunner脚本创建1.Insert - New step -选择Custom Request -web_custom_request2.填入相应参数3.生成脚本&#xff0c;并修改如下(参数中的引号"前需要加斜杠\转译)Action(){web_custom_request("web_custom_request"…

SmartIDE支持开源国产IDE - 阿里蚂蚁的OpenSumi丨IDCF

作者&#xff1a;徐磊文章首发地址&#xff1a;https://smartide.cn/zh/blog/2022-0419-sprint16/SmartIDE v0.1.16 (Build 3137)已经在2022年4月19日发布到稳定版通道&#xff0c;我们在这个版本中增加了阿里和蚂蚁发布的国产IDE OpenSumi的支持&#xff0c;以及其他一些改进。…

windows之如何知道C盘目录下的大文件路径

1 准备linux的命令环境 window环境安装git,因为我们需要git bash,使用命令操作 2 用find和xargs命令 比如我们要知道C盘大于300M的文件路径和具体大小 我们打开git bash,然后cd到C盘&#xff0c;命令如下 find . -type f -size 300M | xargs du -h | sort -nr

js操作文件

在HTML表单中&#xff0c;可以上传文件的唯一控件就是<input type"file">。 注意&#xff1a;当一个表单中包含<input type"file">时&#xff0c;表单的enctype必须指定 为multipart/form-data,method必须指定为post&#xff0c;浏览器才能正确…

JavaScript读取本地图片到浏览器

代码: <html> <head> <script type="text/javascript"> function getFileUrl(sourceId) { var url; if (navigator.userAgent.indexOf("MSIE")>=1) { // IE url = document.getElementById(sourceId).value; } else if(navigator.…

Web程序员的Mysql进阶序二之sql多条数据插入、多条数据更新、多表同时查询

数据库在web开发的时候&#xff0c;减少连接次数可以降低数据库负载&#xff0c;所以一次连接&#xff0c;多数据操作可以有效的优化数据库。 假设表结构如下&#xff1a; create table test(name varchar(10),sex varchar(10) ); create table test1(name varchar(10),sex v…

Android封装快捷键,android打包一个没有快捷键的apk,并且通过另一个应用启动

1.有时候我们的需求是每个功能可能类似一个插件那样分开&#xff0c;需要我们分别不同开发2.首先我们如何安装apk之后不显示Lanch呢&#xff1f;android:name".app.Books.Books"android:configChanges"orientation|keyboardHidden|screenSize|smallestScreenSiz…

MVC 扩展方法特点

.NET MVC 3中扩展方法特点&#xff1a; &#xff08;1&#xff09;扩展类的名称以Extensions结尾&#xff1b; &#xff08;2&#xff09;扩展类的类型是static&#xff1b; &#xff08;3&#xff09;扩展方法至少有一个参数&#xff0c;第一个参数必须指定该方法作用于哪个类…

C# Linq源码解析之Aggregate

前言在Dotnet开发过程中&#xff0c;Aggregate作为IEnumerable的扩展方法&#xff0c;十分常用。本文对Aggregate方法的关键源码进行简要分析&#xff0c;以方便大家日后更好的使用该方法。使用Aggregate是对序列应用累加器的函数。看下面一段代码:List<string> lst new…

c语言输出中文为乱码_C语言编程出现汉字输出乱码现象

匿名用户1级2014-06-27 回答//因为不知道你具体的功能流程&#xff0c;所以我只能先就语法来提下代码的问题int cha(){//int a[N],b[N];//int i0,x,v1,v2;char a[N][M] {0};//M为字符串最大长度加1,根据要求设置int b[N] {0};int i0,x,v2;char v1[M] {0};FILE *f;if((ffopen…

剑指offer之二叉树的高度

1 问题 求二叉树的深度&#xff0c;比如下面的二叉树&#xff0c;高度是4 22 13 3 2 53 2 代码实现 int getTreeHeigh(Node *haed) {if (head NULLL){return 0;}int left getTreeHeigh(head->left);int right getTreeHeigh(head->right);retur…

Entity Framework Code First模式基础知识及入门实例01

在深入学习某项技术之前,应该努力形成对此技术的总体印象,并了解其基本原理,本文的目的就在于此。 一、理解EF数据模型 EF本质上是一个ORM框架,它需要把对象映射到底层数据库中的表,为此,它使用了三个模型来描述这种映射关系。 (1)概念模型(Conceptual Model):主要…

mycat 双主 热切换

为什么80%的码农都做不了架构师&#xff1f;>>> Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz schema.xml <?xml version"1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat"ht…

Web程序员的Mysql进阶序三之sql多表数据删除、子查询、联合查询

假设表结构如下&#xff1a; create table test(name varchar(10),sex varchar(10) ); create table test1(name varchar(10),sex varchar(10) );假设多条数据同时插入&#xff1a; insert into test (name,sex) values(xiao,nan),(xiao1,nan1),(xiao2,nan2); insert into te…