热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【原创】经典排序回顾

部分摘抄自:https:www.cnblogs.comskywang12345p3603935.html一、冒泡排序思路:冒泡,顾名思义,每一次都将最重的元素往数列的末端沉。外层:一

部分摘抄自:https://www.cnblogs.com/skywang12345/p/3603935.html

一、冒泡排序

思路:冒泡,顾名思义,每一次都将最重的元素往数列的末端沉。外层:一层循环,每一次都将一个最大的元素移动到末端,故边界i: 0..n; 里层:负责一次具体的移动,通过不断的比较相邻的两个元素,移动最大的元素。

平均时间复杂度:O(n^2)

空间复杂度:(定义:一个算法在运行过程中临时占用存储空间的大小。)由于此算法的临时存储空间不随输入数据大小n变化,只存在在相邻两个元素交换过程中的一次临时存储(tmp),故为O(1)。

稳定性:(定义:如在数列中有a[j]=a[j](i

1 void bubble_sort(int a[], int n){
2     for(int i=0;i){
3         for(int j=0;j1;j++){
4             if(a[j]>a[j+1]){
5                 swap(a[j],a[j+1]);
6             }
7         }
8     }
9 }
主要代码

 

二、快速排序

快速排序是基于分治策略。首先,选中一个基准值,根据比较,将数列分为两个独立的部分,其中一部分的数都要比另一部分的小。之后再独立地将两个子序列分别照此方法排序。

思路:

1.前提:由于是根据分治法,递归地对序列进行排序,所以,首先,方法的参数应该有3部分:quick_sort(数列对象,排序起始地址,排序结束地址),也就是确定边界值。

2.对排序的数列确定一个基准值(pivot), (习惯性地从起始下标处取值)。

3.从对立方向(也就是终点下标处)j 向左扫描,直到碰见小于pivot的值,将此值移动到左边。

4.再跳转到对立方向,从更新后位置的右边 i 开始向右继续扫描,直到碰见大于pivot的值,将此值移动到右边。

5.重复3,4。直到i=j,将此处a[i]赋值为pivot值。第一次排序完成。即pivot左边的值都不大于它,右边的都不小于它。

6.递归地对pivot左边与右边的子序列进行排序。

 

平均时间复杂度:O( nlog(n) )。因为快排采用的分治法,所以可以想象成一个二叉树。

  补充: (时间复杂度证明)——其中 T(n)=aT(n/b)+f(n) 是用得很多的递归方程。

1 T(n)=2T(n/2)+f(n)----f(n)-O(n),因为实际上我们对每一层递归树进行划分的时候,都是将整个数组都遍历了一遍 
2 T(n)=4T(n/4)+2f(n)
3 ...log2N=k,共进行k次 
4 T(n)=nT(1)+kf(n)=O(n)+kO(n)==kO(n)=O(n*logn) 

空间复杂度:O(logn)。主要空间消耗是在递归调用上,每次递归调用都会需要临时存储一些数据。

稳定性:不稳定

 1 void quick_sort(int a[], int left, int right){
 2     if(left<right){
 3         int i=left;
 4         int j=right;
 5         int tmp=a[i];
 6         while(i<j){
 7             while(itmp) j--;
 8             
 9             if(a[j]<tmp){
10                 a[i]=a[j];
11                 i++;
12 
13             }
14             while(i;
15             if(a[i]>tmp){
16                 a[j]=a[i];
17                 j--;
18             }
19         }
20         a[i]=tmp;
21         quick_sort(a, left, j--);
22         quick_sort(a,i++,right);
23     }
24 }
主要代码

 

三、直接插入排序

 思路:将一个待排序列表看作两部分,一部分是已经排序好的,另一部分有待排序的。从有待排序的部分依次选取值拿到已经排序的部分进行比较,插入到合适的位置,直到所有的都已经排序完成。

平均时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定

 1 void insert_sort(int a[], int n){
 2     int tmp;
 3     int i,j,k;
 4     for(i=1; i){
 5         for(j=i-1;j>=0;j--){
 6             if(a[j]<=a[i]){//在已经排序的数列里往回找,直到找到一个不大于(所以插入排序是稳定的)当前数的下标位置
 7                 break 8             }
 9             if(j!=i-1){//后移元素
10                 tmp=a[i];
11                 for(k=i;k>j+1;k--){
12                     a[k]=a[k-1];
13 
14                 }
15                 a[j+1]=tmp;
16             }
17         }
18     }
19 }
主要代码

 

四、选择排序

思路:将一个待排序序列看作两部分,一部分是已经排序好的,另一部分有待排序的。从待排序的部分依次选择最小值,依次排在已排序的序列的末尾(即与末尾元素交换)。

   (与插入排序的区别:1.插入排序是按下标依次从待排序序列取值,而选择排序是从待排序序列中依次选择最小值 2.插入排序将拿出的数拿到已排序序列中对比插入到合适位置,而选择排序直接放到已排序序列的末尾。)

平均时间复杂度:O(n*2)

空间复杂度:O(1)

稳定性:不稳定

 1 void select_sort(int a[], int n){
 2     int i,j,min;
 3     fot(i=0; i){
 4         min=i;
 5         for(j=i+1; j//从未排序的i+1及后面的数中选取最小值
 6             if(a[j]j;
 7         }
 8         if(min !=i){
 9             swap(a[i],a[min]);//将最小值交换到i位
10         }
11     }
12 }
主要代码

 

五、堆排序

思路:(一种特殊的选择排序)将未排序序列转换为大/小顶堆,取最大/小值,放入未排序序列末尾。对剩余未排序序列重复操作,直到排序完成。

平均时间复杂度:O(n*logn)

空间复杂度:O(1)

稳定性:不稳定

 1 void maxheap_down(int a[], int start, int end)
 2 {
 3     int c = start;            // 当前(current)节点的位置
 4     int l = 2*c + 1;        // 左(left)孩子的位置
 5     int tmp = a[c];            // 当前(current)节点的大小
 6     for (; l <= end; c=l,l=2*l+1)
 7     {
 8         // "l"是左孩子,"l+1"是右孩子
 9         if ( l 1])
10             l++;        // 左右两孩子中选择较大者,即m_heap[l+1]
11         if (tmp >= a[l])
12             break;        // 调整结束
13         else            // 交换值
14         {
15             a[c] = a[l];
16             a[l]= tmp;
17         }
18     }
19 }
20 
21 void heap_sort_asc(int a[], int n)
22 {
23     int i;
24 
25     // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
26     for (i = n / 2 - 1; i >= 0; i--)
27         maxheap_down(a, i, n-1);
28 
29     // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
30     for (i = n - 1; i > 0; i--)
31     {
32         // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
33         swap(a[0], a[i]);
34         // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
35         // 即,保证a[i-1]是a[0...i-1]中的最大值。
36         maxheap_down(a, 0, i-1);
37     }
38 }
主要代码

补充:在C++中,可以使用priority_queue优先队列,是基于堆排序实现的。

 

六、归并排序

思路:它与"从下往上"在排序上是反方向的。它基本包括3步:
① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2; 
② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。

平均时间复杂度:O(n*logn)

空间复杂度:O(n)

稳定性:稳定

 1 /*
 2  * 将一个数组中的两个相邻有序区间合并成一个
 3  *
 4  * 参数说明:
 5  *     a -- 包含两个有序区间的数组
 6  *     start -- 第1个有序区间的起始地址。
 7  *     mid   -- 第1个有序区间的结束地址。也是第2个有序区间的起始地址。
 8  *     end   -- 第2个有序区间的结束地址。
 9  */
10 void merge(int a[], int start, int mid, int end)
11 {
12     int *tmp = (int *)malloc((end-start+1)*sizeof(int));    // tmp是汇总2个有序区的临时区域
13     int i = start;            // 第1个有序区的索引
14     int j = mid + 1;        // 第2个有序区的索引
15     int k = 0;                // 临时区域的索引
16 
17     while(i <= mid && j <= end)
18     {
19         if (a[i] <= a[j])
20             tmp[k++] = a[i++];
21         else
22             tmp[k++] = a[j++];
23     }
24 
25     while(i <= mid)
26         tmp[k++] = a[i++];
27 
28     while(j <= end)
29         tmp[k++] = a[j++];
30 
31     // 将排序后的元素,全部都整合到数组a中。
32     for (i = 0; i )
33         a[start + i] = tmp[i];
34 
35     free(tmp);
36 }
37 
38 /*
39  * 归并排序(从上往下)
40  *
41  * 参数说明:
42  *     a -- 待排序的数组
43  *     start -- 数组的起始地址
44  *     endi -- 数组的结束地址
45  */
46 void merge_sort_up2down(int a[], int start, int end)
47 {
48     if(a==NULL || start >= end)
49         return ;
50 
51     int mid = (end + start)/2;
52     merge_sort_up2down(a, start, mid); // 递归排序a[start...mid]
53     merge_sort_up2down(a, mid+1, end); // 递归排序a[mid+1...end]
54 
55     // a[start...mid] 和 a[mid...end]是两个有序空间,
56     // 将它们排序成一个有序空间a[start...end]
57     merge(a, start, mid, end);
58 }
主要代码

 


推荐阅读
  • 深入理解排序算法:集合 1(编程语言中的高效排序工具) ... [详细]
  • 寒假作业解析:第三周 2月12日 第7题
    尽快完成之前的练习任务!每日一练2.1 Problem A Laurenty and Shop 的题目要求是选择两条不同的路线以最小化总的等待时间。简要分析:通过对比不同路线的等待时间,可以找到最优解。此问题可以通过动态规划或贪心算法来解决,具体取决于路线的复杂性和约束条件。 ... [详细]
  • 在解决区间相关问题时,我发现自己经常缺乏有效的思维方式,即使是简单的题目也常常需要很长时间才能找到解题思路,而一旦得到提示便能迅速理解。题目要求对一个包含n个元素的数组进行操作,并给出一个参数k,具体任务是…… ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 本文将继续探讨 JavaScript 函数式编程的高级技巧及其实际应用。通过一个具体的寻路算法示例,我们将深入分析如何利用函数式编程的思想解决复杂问题。示例中,节点之间的连线代表路径,连线上的数字表示两点间的距离。我们将详细讲解如何通过递归和高阶函数等技术实现高效的寻路算法。 ... [详细]
  • MyISAM和InnoDB是MySQL中最为广泛使用的两种存储引擎,每种引擎都有其独特的优势和适用场景。MyISAM引擎以其简单的结构和高效的读取速度著称,适用于以读操作为主、对事务支持要求不高的应用。而InnoDB引擎则以其强大的事务处理能力和行级锁定机制,在需要高并发写操作和数据完整性的场景下表现出色。选择合适的存储引擎应综合考虑业务需求、性能要求和数据一致性等因素。 ... [详细]
  • 本文全面解析了JavaScript中的DOM操作,并提供了详细的实践指南。DOM节点(Node)通常代表一个标签、文本或HTML属性,每个节点都具有一个nodeType属性,用于标识其类型。文章深入探讨了DOM节点的创建、查询、修改和删除等操作,结合实际案例,帮助读者更好地理解和掌握DOM编程技术。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 题目链接:POJ 2777。问题描述:给定一个区域,需要进行多次涂色操作,并在每次操作后查询某个区间内的不同颜色数量。解决方案:由于题目中颜色种类不超过30种,可以利用线段树的懒惰更新策略来高效处理这些操作。通过懒惰标记,避免了不必要的节点更新,从而显著提高了算法的效率。此外,该方法还能有效应对大规模数据输入,确保在合理的时间内完成所有操作。 ... [详细]
  • Java集合框架特性详解与开发实践笔记
    Java集合框架特性详解与开发实践笔记 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
author-avatar
手机用户2502893613
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有