关于我的数据结构与算法——初阶第二篇(排序)

(叠甲:如有侵权请联系,内容都是自己学习的总结,一定不全面,仅当互相交流(轻点骂)我也只是站在巨人肩膀上的一个小卡拉米,已老实,求放过)。

排序的概念及其运用

排序的概率

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

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

内部排序:数据元素全部放在内存中的排序。

外部排序:数据太多不能同时放在内存中,根据排序过程的要求,不能再内外存之间移动的排序。

常见的排序算法

常见排序算法的实现

插入排序

直接插入排序是一种简单的插入排序法,其基本思想是把待排序的记录按其关键码值的大小逐个插入到一个已经安排好的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列——一张一张摸牌的扑克牌。

 

当插入第i(i>=1)个元素时,前面的array[0],……,array[i-1]已经排好序了,此时用array[i]的排序码与array[i-1],array[i-2],……的排序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"int main()
{int a[5] = { 44,8,63,45,10};//插入排序InsertSort(a, 5);PrintArray(a, 5);return 0;
}

 Sort.c

void InsertSort(int* a, int n)
{assert(a);for (int i = 0; i < n-1; i++){int end = i;int tem = a[end + 1];while (end>=0){if (tem < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tem;}
}//打印
void PrintArray(int* a, int n)
{assert(a);for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}

Sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//插入排序
void InsertSort(int* a, int n);//希尔排序(缩小增量排序)
void ShellSort(int* a, int n);//选择排序
void SelectSort(int* a, int n);//堆排序
void AdjustDown(int* a, int n, int root);
void HeapSort(int* a, int n);//冒泡排序
void BubbleSort(int* a, int n);int GetMidIndex(int* a, int left, int right);//快速排序Hoare版本
int ParSort1(int* a, int left, int right);//快速排序挖坑法版本
int ParSort2(int* a, int left, int right);//快速排序前后指针版本
int ParSort3(int* a, int left, int right);void QuickSort(int* a, int left, int right);//快速排序非递归实现
void QuickSort(int* a, int left, int right);//归并排序递归实现
void MergeSort(int* a, int n);//归并排序非递归实现
void MergeSort(int* a, int n);//计数排序
void CountSort(int* a, int n);//打印
void PrintArray(int* a, int n);//交换
void Swap(int* pa, int* pb);

直接插入排序的特性总结:

1)元素集合越接近有序,直接插入排序算法的时间效率越高;

2)时间复杂度:O(N^{2});

3)空间复杂度:O(1),它是一种稳定的排序算法;

4)稳定性:稳定

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

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"int main()
{int a[5] = { 44,8,63,45,10};//希尔排序ShellSort(a, 5);PrintArray(a, 5);return 0;
}

  Sort.c

//希尔排序(缩小增量排序)
void ShellSort(int* a, int n)
{assert(a);int gap = 0;gap = n;while (gap >1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i += gap){int end = i;int tem = a[end + gap];while (end >= 0){if (tem < a[end]){a[end + gap] = a[end];end--;}else{break;}}a[end + gap] = tem;}}
}

希尔排序又称缩小增量法,希尔排序的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为记录在同一组内,并对一组内的记录进行排序,然后,取,重复上述分组和排序的工作,当达到= 1时,所有记录在统一组内排好序。

希尔排序的特性总结:

1)希尔排序是对直接插入排序的优化;

2)当 gap>1时都是预排序,目的是让数组更接近有序,当gap ==1 时,数组已经接近有序了,这样就很快,这样整体而言,可以达到优化的效果;

3)希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在哼多树中给出的希尔排序的时间复杂度都不固定;根据大量实验可以暂时的按照O(N^{1.25})到

O(1.6*N^{1.25})来算。

4)稳定性:不稳定

选择排序

基本思想

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

直接选择排序:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"int main()
{int a[5] = { 44,8,63,45,10};//选择排序SelectSort(a, 5);PrintArray(a, 5);return 0;
}

Sort.c

//交换
void Swap(int* pa, int* pb)
{assert(pa);assert(pb);int tem = *pa;*pa = *pb;*pb = tem;
}//选择排序
void SelectSort(int* a, int n)
{assert(a);int left = 0;int right = n - 1;while (left <= right){int mini = left;int maxs = right;for (int i = 0; i <= right; i++){if (a[i] < a[mini]){mini = i;}if (a[i] > a[right]){maxs = i;}}Swap(&a[left], &a[mini]);if (maxs == left){maxs = mini;}Swap(&a[right], &a[maxs]);left++;right--;}
}

 

1)在元素集合array[i]--array[n-1])中选择关键码最大(小)的数据元素;

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

3)在剩余的arrqy[i]--array[n-2] (arry[i+1]--array[n-1])集合中,重复上述步骤,直集合剩余1个元素;

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

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

2)时间复杂度:O(N^{2});

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

4)稳定性:不稳定

堆排序

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"int main()
{int a[5] = { 44,8,63,45,10};//堆排序HeapSort(a,5);PrintArray(a, 5);return 0;
}

Sort.c

//交换
void Swap(int* pa, int* pb)
{assert(pa);assert(pb);int tem = *pa;*pa = *pb;*pb = tem;
}//堆排序(建大堆)
void AdjustUp(int* a, int n)
{int child = n - 1;int parent = (child-1) / 2;while (child>0){if (a[child] > a[parent]){Swap(&a[child] ,&a[parent]);child = parent;parent = (child-1) / 2;}else{break;}}
}//堆排序(向下调整)
void AdjustDown(int* a, int n)
{assert(a);int parent = 0;int child = 2 * parent + 1;while (child<n){if (child+1<n && a[child + 1] >a[child]){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}//堆排序(排序)
void HeapSort(int* a, int n)
{for (int i = 0; i <= n; i++){AdjustUp(a, i);}assert(a);while (n>0){Swap(&a[0], &a[n-1]);n--;AdjustDown(a, n);}}

堆排序(heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种,它是通过堆来进行选择数据,需要注意的是排升序要建大堆,排降序建小堆。

 堆排序的特性总结:

1)堆排序使用堆来选数,效率就高很多

2)时间复杂度:O(N*log^{N}

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

4)稳定性:不稳定

交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"int main()
{int a[5] = { 44,8,63,45,10};//冒泡排序BubbleSort(a, 5);PrintArray(a, 5);return 0;
}

Sort.c

//冒泡排序
void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){for (int j = 1; j < n - i; j++){if (a[j - 1] > a[j]){int tem = a[j];a[j] = a[j - 1];a[j - 1] = tem;}}}
}

冒泡排序的特性总结

1)冒泡排序是一种非常容易理解的排序

2)时间复杂度:O(N^{2}

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

4)稳定性:稳定

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素为基准值,按照该排序码将排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快速排序递归实现的主框架,发现与二叉树前序遍历很相似,同学们在写递归框架时,可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:

1)hoare版本

Sort.h

//交换
void Swap(int* pa, int* pb)
{assert(pa);assert(pb);int tem = *pa;*pa = *pb;*pb = tem;
}//快速排序Hoare版本
int ParSort1(int* a, int left, int right)
{int keyi = left;while (left < right){while ((left < right) && (a[right] >=a[keyi])){right--;}while ((left < right) && (a[left] <=a[keyi])){left++;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[right]);return left;
}

2)挖坑法

Sort.c

//快速排序挖坑法版本
int ParSort2(int* a, int left, int right)
{int keyi = a[left];int pia = left;while (left < right){while ((left < right) && (a[right] >= keyi)){right--;}a[pia] = a[right];pia = right;while ((left < right) && (a[left] <= keyi)){left++;}a[pia] = a[left];pia = left;}a[pia] = keyi;return pia;
}

3)前后指针法

Sort.c

//交换
void Swap(int* pa, int* pb)
{assert(pa);assert(pb);int tem = *pa;*pa = *pb;*pb = tem;
}//快速排序前后指针版本
int ParSort3(int* a, int left, int right)
{int prev = left;int cur = prev+1;int keyi = left;while (cur <= right){if (a[cur] <= a[keyi] && a[++prev] != a[cur]){Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}
快速排序优化

1)三数取中法选key

Sort.c

//快速排序找中间值
int GetMidIndex(int* a, int left, int right)
{int mid = left + (right / 2);if (mid >= right){mid = left;}if (a[mid] > a[left]){if (a[mid] < a[right]){return mid;}else if (a[right] > a[left]){return right;}else{return left;}}else{if (a[mid] < a[right]){return mid;}else{return right;}}
}void QuickSort1(int* a, int left, int right)
{if (left >= right){return;}int mid = GetMidIndex(a, left, right);ParSort1(a, left, right);QuickSort1(a, left, mid);QuickSort1(a, mid+1, right);}

2)递归到小的子区间时,可以考虑使用插入排序

快速排序非递归

快速排序的特性总结:

1)快速排序整体的综合性能和使用场景都是比较好的;

2)时间复杂度:O(N*log^{N})

3)空间复杂度:O(log^{N}

4)稳定性:不稳定

归并排序

基本思想:

归并排序(merge-sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序列表合成一个有序表,称为二路归并,归并排序核心步骤是分解和合并;

归并排序的特性总结:

1)归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题;

2)时间复杂度:O(N*log^{N})

3)空间复杂度:O(N)

4)稳定性:稳定

非比较排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用,操作步骤

1)统一相同元素出现次数;

2)根据统计的结果将序列回收到原来的序列中

计数排序的特性总结:

1)计数排序在数据范围集中时,效率很高,但是适用范围及场景有限

2)时间复杂度:O(MAX(N,范围))

3)空间复杂度:O(范围)

4)稳定性:稳定

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

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

相关文章

AI驱动的低代码未来:加速应用开发的智能解决方案

引言 随着数字化转型的浪潮席卷全球&#xff0c;企业对快速构建应用程序的需求愈发强烈。然而&#xff0c;传统的软件开发周期冗长、成本高昂&#xff0c;往往无法满足快速变化的市场需求。在此背景下&#xff0c;低代码平台逐渐成为开发者和企业的优选方案&#xff0c;以其“低…

三周精通FastAPI:21 子依赖项和路径操作装饰器依赖项

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/dependencies/sub-dependencies/#_6 子依赖项 FastAPI 支持创建含子依赖项的依赖项。 并且&#xff0c;可以按需声明任意深度的子依赖项嵌套层级。 FastAPI 负责处理解析不同深度的子依赖项。 第一层依赖项 …

模具生产管理系统软件:提升制造业效率的新利器

引言 我们都知道&#xff0c;企业面临着提高生产效率、降低成本和提升产品质量的压力。模具生产作为制造过程中至关重要的一环&#xff0c;如何有效管理和优化模具生产过程&#xff0c;成为企业关注的重点。模具生产管理系统应运而生&#xff0c;能够为企业提供实时监控、流程…

MySQL中,如何定位慢查询?定位到的慢SQL如何分析?

目录 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 2.1 详细解释 3. 定位到的慢SQL如何分析&#xff1f; 3.1 详细说明 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 介绍一下当时产生问题…

「C/C++」C++ 设计模式 之 单例模式(Singleton)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

华为云开源项目Sermant正式成为CNCF官方项目

近日&#xff0c;云原生计算基金会&#xff08;CNCF&#xff09;正式接纳由华为云发起的云原生无代理服务网格项目Sermant。Sermant的加入&#xff0c;极大地丰富了云原生微服务治理技术的探索、创新和发展&#xff0c;为CNCF社区注入了新的活力。 Sermant是华为云在微服务治理…

用sdcc给51单片机编译C程序

学习单片机大部分人用的是Keil uVision&#xff0c;虽然好用&#xff0c;可大部分人用的是盗版&#xff0c;其实单片机程序小的话&#xff0c;完全可以用文本编辑器&#xff08;推荐notepad)编写&#xff0c;然后用免费的sdcc来编译&#xff0c;下面介绍一下大致的过程。 sdcc…

Ajax:表单 模板引擎

Ajax&#xff1a;表单 & 模板引擎 form 表单form 属性 Ajax操控表单事件监听阻止默认行为收集表单数据 模板引擎art-template{{}}语法原文输出条件输出循环输出过滤器 原理 form 表单 在HTML中&#xff0c;可以通过<form>创建一个表单&#xff0c;收集用户信息。而采…

【水下生物数据集】 水下生物识别 深度学习 目标检测 机器视觉 yolo(含数据集)

一、背景意义 随着全球海洋生态环境的日益变化&#xff0c;水下生物的监测和保护变得愈发重要。水下生物种类繁多&#xff0c;包括螃蟹、鱼类、水母、虾、小鱼和海星等&#xff0c;它们在海洋生态系统中扮演着关键角色。传统的水下生物监测方法通常依赖于人工观察&#xff0c;效…

[vulnhub]Kioptrix: Level 1.2 (#3)

https://www.vulnhub.com/entry/kioptrix-level-12-3,24/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是169 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-29 13:16 CST …

TVM前端研究--Relay

文章目录 深度学习IR梳理1. IR属性2. DL前端发展3. DL编译器4. DL编程语言Relay的主要内容一、Expression in Relay1. Dataflow and Control Fragments2. 变量3. 函数3.1 闭包3.2 多态和类型关系3.3. Call4. 算子5. ADT Constructors6. Moudle和Global Function7. 常量和元组8.…

Ubuntu UFW防火墙规则与命令示例大全

在服务器安全领域&#xff0c;防火墙是守护网络安全的坚实盾牌。UFW&#xff08;Uncomplicated Firewall&#xff09;&#xff0c;即“不复杂的防火墙”&#xff0c;是一个运行在iptables之上的防火墙配置工具&#xff0c;它为Ubuntu系统默认提供了一个简洁的命令行界面&#x…

Linux高阶——1026—验证内存映射mmap函数使用

1、验证共享映射后修改文件内容&#xff0c;是否能够同步 先创建一个映射文件&#xff0c;写入数据 分为四个步骤 1、打开映射文件 设文件描述符&#xff0c;使用open函数 int fd; if((fdopen("mapfile",O_RDWR))-1) { perror("open failed");exit…

从零开始的 vue项目部署到服务器详细步骤(vue项目build打包+nginx部署+配置ssl证书)

从零开始的 vue项目部署到服务器详细步骤&#xff08;vue项目build打包nginx部署配置ssl证书&#xff09; 文章目录 从零开始的 vue项目部署到服务器详细步骤&#xff08;vue项目build打包nginx部署配置ssl证书&#xff09;一、前言二、vue项目部署前配置1、vite.config.js 增加…

快速遍历包含合并单元格的Word表格

Word中的合并表格如下&#xff0c;现在需要根据子类&#xff08;例如&#xff1a;果汁&#xff09;查找对应的品类&#xff0c;如果这是Excel表格&#xff0c;那么即使包含合并单元格&#xff0c;也很容易处理&#xff0c;但是使用Word VBA进行查找&#xff0c;就需要一些技巧。…

智慧园区 | 数智引领,让智慧触手可及

随着科技的飞速发展&#xff0c;智慧园区正成为现代城市发展的重要方向之一。在智慧园区中&#xff0c;各种高科技手段被应用于园区的管理和服务&#xff0c;为园区的运营和居民的生活带来无限可能。 智慧园区管理平台是智慧园区建设的核心。它集聚了大数据、物联网、云计算等技…

基于uniapp微信小程序的旅游系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

【分布式知识】分布式对象存储组件-Minio

文章目录 什么是minio核心特点&#xff1a;使用场景&#xff1a;开发者工具&#xff1a;社区和支持&#xff1a; 核心概念什么是对象存储&#xff1f;MinIO 如何确定对对象的访问权限&#xff1f;我可以在存储桶内按文件夹结构组织对象吗&#xff1f;如何备份和恢复 MinIO 上的…

iQOO手机怎样将屏幕投射到MacBook?可以同步音频吗?

众所周知&#xff0c;苹果品牌的设备自己有AirPlay的投屏功能&#xff0c;iPhone要投屏到MacBook只要连接同一网络&#xff0c;然后开启AirPlay就可以投屏。但其他品牌的手机没有AirPlay&#xff0c;怎么将手机屏幕投射到MacBook呢&#xff1f; 安卓系统的手机可以使用无线投屏…

2. 从服务器的主接口入手

Webserver 的主函数 main.cpp&#xff0c;完成了哪些功能&#xff1f; #include "config.h"int main(int argc, char *argv[]) {string user "";string passwd "";string databasename "";Config config;config.parse_arg(argc, a…