当前位置: 首页 > news >正文

C++基础算法④——排序算法(快速、归并附完整代码)

快速排序

快速排序是对冒泡排序的一种改进。
它的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,以达到整个序列有序。
假设我们现在对 6 1 2 7 9 3 4 5 1 0 8 这个10个数进行排序。

int main(){int n;cin>>n;for(int i=1;i<=n;i++) cin>>a[i];qsort(1,n);for(int i=1;i <=n;i++){cout<<a[i]<<" ";}	return 0;
}

首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数)。为了方便,就让第一个数6作为基准数吧。接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似下面这种排列:

int a[10001];
void qsort(int l,int r){int i,j,mid;i=l+1,j=r;mid=a[l];

在这里插入图片描述
可以发现,i从第二个位置开始,j从最后一个位置开始;当 i 指向的值 大于基准值6 而且 当 j 指向的值 小于基准值6,就把这两个值交换,然后接着往下继续比。
在这里插入图片描述

while(i<=j){ //当 i j 没有碰到while(a[i]<mid) i++;while(a[j]>mid) j--;if(i<=j){swap(a[i],a[j]);i++; j--;}}

在这里插入图片描述
当 i j 相遇了,就把 j指向的值 与基准数交换。

swap(a[j],a[l]); //交换基准数

在这里插入图片描述
可以发现,一趟完毕,原基准数6的左边值一定比6小,右边比6大。这样就确定了基准数6的排序。
接下来,对于6左边的序列3 1 2 5 4和右边的序列9 7 10 8分别进行快速排序。
把整体分了左右边,再把左边的序列3 1 2 5 4看成新的重新进行快速排序。不断地分解。
在这里插入图片描述
所以这个排序运用了分治的思想!
完整代码:

#include<bits/stdc++.h>
using namespace std;
int a[10001];
void qsort(int l,int r){int i,j,mid;i=l+1,j=r;mid=a[l];while(i<=j){while(a[i]<mid) i++;while(a[j]>mid) j--;if(i<=j){swap(a[i],a[j]);i++; j--;}}swap(a[j],a[l]); //交换基准数if(l<j) qsort(l,j-1);if(i<r) qsort(i,r); 
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++) cin>>a[i];qsort(1,n);for(int i=1;i <=n;i++){cout<<a[i]<<" ";}	return 0;
}

快速排序是不稳定的排序方法,时间复杂度是O(nlog2n),速度快,平均时间来说,快速排序是最好的一种内部排序方法。但快速排序需要一个栈空间实现递归,每一趟排序都会将记录序列分割成两个子序列,栈最大深度为log(n+1)。


归并排序

归并的思路(分治)是把一个大问题a拆解成两个小问题b和c,解决了两个子问题再整合一下,就解决了原问题。用递归的方法,先分解再合并(分治是一种解决问题的处理思想,递归是一种编程技巧,这两者并不冲突)。

  • 稳定性:稳定;
  • 空间复杂度O(n);
  • 复杂度:时间复杂度O(nlogn);
  • 优缺点:效率高且稳定,但是消耗的辅助空间与原数据空间成正比。
int main(){cin>>n;for(int i=1;i<=n;i++){ //输入 cin>>a[i];}//归并排序mergesort(1,n);for(int i=1;i<=n;i++){ //输出 cout<<a[i]<<" ";}return 0;
}
  1. 递归分解
    在这里插入图片描述
    不断地二分分解,拆左右。
void mergesort(int l,int r){int mid = (l+r)/2;if(l==r) return ;mergesort(l,mid); //左边排序mergesort(mid+1,r);//右边排序//上面已经拆成一个一个 merge(l,mid,mid+1,r); //合并操作 
}

分解到1个值,然后再合并排序。合并的思路看成:左边是有序的a数组;右边是有序的b数组。两数组开始比较,小的值依次存到c数组。

int a[100],c[100],n,cnt;
void merge(int left,int i,int j,int right){int lenc = left;int len1 = left;  //左边开头 看成a[] int len2 = j;  	//右边开头	 看成b[] while(len1<=i && len2<=right){ //并c[] if(a[len1]<a[len2]){ //左边小于右边 c[lenc++] = a[len1++];}else{//右边小于左边c[lenc++] = a[len2++];}} while(len1<=i){c[lenc++] = a[len1++];} while(len2<=right){c[lenc++] = a[len2++];}//把排好序的c数组存回a数组里面for(int k=left;k<=right;k++){a[k]=c[k];} 
} 

完整代码:

#include<bits/stdc++.h>
using namespace std;
int a[100],c[100],n,cnt;
void merge(int left,int i,int j,int right){int lenc = left;int len1 = left;  //左边开头 看成a[] int len2 = j;  	//右边开头	 看成b[] while(len1<=i && len2<=right){ //并c[] if(a[len1]<a[len2]){ //左边小于右边 c[lenc++] = a[len1++];}else{//右边小于左边c[lenc++] = a[len2++];}} while(len1<=i){c[lenc++] = a[len1++];} while(len2<=right){c[lenc++] = a[len2++];}//把排好序的c数组存回a数组里面for(int k=left;k<=right;k++){a[k]=c[k];} 
} void mergesort(int l,int r){int mid = (l+r)/2;if(l==r) return ;mergesort(l,mid); //左边排序mergesort(mid+1,r);//右边排序//上面已经拆成一个一个 merge(l,mid,mid+1,r); //合并操作 
}
int main(){cin>>n;for(int i=1;i<=n;i++){ //输入 cin>>a[i];}//归并排序mergesort(1,n);for(int i=1;i<=n;i++){ //输出 cout<<a[i]<<" ";}return 0;
}

相关文章:

  • 【ARM Trace32(劳特巴赫) 使用介绍 2 -- Trace32 cmm 脚本基本语法及常用命令】
  • 理解springboot那些过滤器与调用链、包装或封装、设计模式相关等命名规范,就可以读懂80%的springboot源代码,和其他Java框架代码
  • 在react中使用redux react-redux的使用demo
  • 【c++|opencv】二、灰度变换和空间滤波---1.灰度变换、对数变换、伽马变换
  • 我的创作纪念日1024
  • 国家数据局正式揭牌,数据专业融合型人才迎来发展良机
  • 【咕咕送书 | 第5期】国家数据局正式揭牌,数据专业融合型人才迎来发展良机
  • 39基于matlab的全局路径规划算法中的快速扩展随机树RRT路径规划算法及其改进方法
  • InfoHound:一款针对域名安全的强大OSINT工具
  • Tiny Plane固定翼小飞机机身硬件整理开源
  • Java Web 项目通用基础响应结果类 BaseRespResult
  • OSEK OS介绍(二)
  • [鹏城杯 2022]简单的php 取反的另一种无数字字母rce 通过请求头执行命令
  • 我与“云栖大会”剪不断的缘分
  • STM32F407的系统定时器
  • [PHP内核探索]PHP中的哈希表
  • css的样式优先级
  • If…else
  • Java程序员幽默爆笑锦集
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Solarized Scheme
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 从重复到重用
  • 深入浏览器事件循环的本质
  • 使用 QuickBI 搭建酷炫可视化分析
  • 浅谈sql中的in与not in,exists与not exists的区别
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ​linux启动进程的方式
  • ​低代码平台的核心价值与优势
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • #stm32驱动外设模块总结w5500模块
  • #在 README.md 中生成项目目录结构
  • $().each和$.each的区别
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (七)理解angular中的module和injector,即依赖注入
  • (三分钟)速览传统边缘检测算子
  • *1 计算机基础和操作系统基础及几大协议
  • ./configure,make,make install的作用(转)
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .jks文件(JAVA KeyStore)
  • .net core 6 集成和使用 mongodb
  • .Net MVC + EF搭建学生管理系统
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .net 程序发生了一个不可捕获的异常
  • .NET/C# 的字符串暂存池
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
  • [20180312]进程管理其中的SQL Server进程占用内存远远大于SQL server内部统计出来的内存...
  • [BZOJ4016][FJOI2014]最短路径树问题
  • [C++]类和对象【下】
  • [ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager失败
  • [E链表] lc83. 删除排序链表中的重复元素(单链表+模拟)
  • [hdu 1247]Hat’s Words [Trie 图]