排序算法:直接插入排序,希尔排序,选择排序,快速排序,堆排序,归并排序

1.直接插入排序

基本思想:把待排序的数按照大小逐个插入到前面已经排序好的有序序列中,直到所有的都插入完为止,得到一个新的有序序列。

如图所示,当插入第i个(i>=1)元素的时候,前面的arr[0],arr[1]......arr[i-1]已经排好序,此时用arr[i]的元素与前面的元素进行比较,找到插入的位置将arr[i]插入,将原来的元素顺序后移。

void InsertSort(int* a, int n)//插入排序
{// [0,end]区间有序,将a[end+1]的值插入for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end])//a[end+1]<a[end]数据往后移 end向前走{a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

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

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

2.最坏时间复杂度:O(N^2)

   最好时间复杂度:O(N)

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

4.稳定性:稳定

2.希尔排序

希尔排序又称缩小增量法。希尔排序的基本思想:采用分组插入的方法,先将待排序记录序列分割成几组(希尔对记录的分组不是简单的“逐段分割”,而是将相隔gap增量的记录分成一组),这样当经过几次分组排序后,整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

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

1.希尔排序是对直接插入排序的优化。

2.当gap>1时都是预排序,目的是让数组更接近有序,当gap==1时,数组已经接近有序的了。

3.时间复杂度:O(N^1.3)。

4.稳定性:不稳定

3.选择排序

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

遍历一般数组,找出最大的数和最小的数,将最大的数和end交换,将最小的数和begin交换,当最小的数和begin重合时需要将最小的数换的前面。

void SelectSort(int* a, int n)//选择排序
{int begin = 0, end = n-1;while (begin < end){int min = begin;int max = begin;for (int i = begin + 1; i <= end; i++){if (a[i] > a[max]){max = i;}if (a[i] < a[min]){min = i;}}Swap(&a[begin], &a[min]);if (a[begin] == a[max]){max = min;}Swap(&a[end], &a[max]);begin++;end--;}
}

1.时间复杂度:O(N^2)

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

3.稳定性:不稳定

4.堆排序

void AdjustUp(int* a, int child)//向上调整算法
{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, int parent)//向下调整算法:n为数组的大小
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child] < a[child + 1]){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)//堆排序
{
//	for (int i = 1; i < n; i++)
//	{
//		AdjustUp(a, i);//向上调整建堆建为大堆,时间复杂度为O(N*logN)
//	}for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);//先下调整算法,从叶子节点开始调整,时间复杂度为O(N)}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);//交换堆顶和最后一个数AdjustDown(a, end, 0);//end是元素个数end--;}
}

选用向下调整算法建堆是因为时间复杂度比向上调整算法建堆小。

1.时间复杂度:O(N*logN)

2.稳定性:不稳定。

5.快速排序

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

1.hoare版本

将左面的值设为key,R先走,R找小,L找大,交换L与R,当L与R相遇时再与key交换。

int GetMid(int* a, int left, int right)//三数取中
{int mid = (left + right) / 2;if (a[left] < a[right]){if (a[right] < a[mid]){return right;}else if (a[mid] < a[left]){return left;}else{return mid;}}else//a[left] > a[right]{if (a[right] < a[mid]){return right;}else if (a[left] < a[mid]){return left;}else{return mid;}}
}void QuickSort(int* a, int left, int right)//快速排序
{if (left >= right){return;}if ((right - left + 1) < 10)//小区间优化{InsertSort(a + left, right - left + 1);}int mid = GetMid(a, left, right);//三数取中Swap(&a[left], &a[mid]);int key = left;int begin = left, end = right;while (begin < end){while (begin < end && a[end] >= a[key])//右先走{end--;}while (begin < end && a[begin] <= a[key]){begin++;}Swap(&a[begin], &a[end]);}Swap(&a[key], &a[begin]);//key还在左,要将key放到相遇的位置key = begin;//[left key-1] key [key+1,right]QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);}

为什么要右先走:左边做key,右边先走可以保证相遇位置比key小

L遇R:R先走,停下来,R停下来的条件是遇到比key小的值,R停的位置一定比key小,L没有找到大,遇见R就停下来了。

R遇L:R先走,找下,没有找到比key小的,直接根L相遇。L停留的位置是上一轮交换的位置,上一轮交换,把比key小的值换到了L的位置。

相反:如果让右边做key,左边先走,可以保证相遇的位置比key大。

2.前后指针法

void QuickSort1(int* a, int left, int right)//快速排序快慢指针
{if (left >= right){return;}if ((right - left + 1) < 10)//小区间优化{InsertSort(a + left, right - left + 1);}int mid = GetMid(a, left, right);//三数取中Swap(&a[left], &a[mid]);int key = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[key] && prev++ != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[key], &a[prev]);key = prev;//[left, key - 1] key [key+1,right]QuickSort1(a, left, key - 1);QuickSort1(a, key + 1, right);
}

3.非递归方法

创建一个栈,将右左区间范围放入栈中(出栈的时候就是先出左,再出右)。

void QuickSortNonR(int* a, int left, int right)//快速排序非递归
{stack st;STInit(&st);STPush(&st, right);STPush(&st, left);while (!STEmpty(&st)){int begin = STTop(&st);STPop(&st);int end = STTop(&st);STPop(&st);int key = Quick(a, begin, end);if (key + 1 < end){STPush(&st, end);STPush(&st, key + 1);}if (begin < key - 1){STPush(&st, key - 1);STPush(&st, begin);}}}

4.三路划分

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本二分居中,那么快排的递归树就是颗均匀的二叉树,性能最佳。但是实践中虽然不可能每次都是二分居中,但性能也还是可控的。但是如果出现每次选到最小值/最大值,划分为0个和N-1个子问题时,时间复杂度为O(N^2),数组有序时就会出现这个问题,我们前面已经用三数取中或者随机选key解决了这个问题,但是还有一些场景没有解决(数组中大量重复数据时)。

三路划分算法解析:

当面对有大量跟key相同的值时,核心思想是把数据分为三段【比key小的值】【根key想等的值】【比key大的值】。

  1. key默认取left位置的值。
  2. left指向区间的最左边,right指向区间的最右边,cur指向left+1的位置。
  3. cur遇到比key小的值后跟left交换,换到左边,left++,cur++。
  4. cur遇到比key大的值后跟right交换,换到右边,right--。
  5. cur遇到跟key相等的值后,cur++。
  6. 直到cur>right时结束。
void QuickSortBy3Way(int* a, int left, int right)//三路划分
{if (left >= right){return;}int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int begin = left;int key = a[left];int end = right;int cur = left + 1;while (cur <= end){if (a[cur] < key){Swap(&a[cur], &a[left]);cur++;left++;}else if (a[cur] > key){Swap(&a[cur], &a[right]);right--;}else{cur++;}}//[begin ,left - 1] [left right] [right + 1, end];QuickSortBy3Way(a, begin, left - 1);QuickSortBy3Way(a, right + 1, end);
}

1.时间复杂度:O(N*logN)

2.空间复杂度:O(logN)

3.稳定性:不稳定

6.归并排序

基本思想:归并排序是建立在归并操作上的一种有效排序算法,该算法是采用分治法的一个典型应用,将已有序的子序列合并,得到完全有序的序列,即使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,称为二路归并。

void _MergeSort(int* a, int* tmp, int left, int right)
{if (left >= right){return;}int mid = (left + right) / 2;_MergeSort(a, tmp, left, mid);_MergeSort(a, tmp, mid + 1, right);//分割//归并int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int i = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + left, tmp + left, (right - left + 1) * sizeof(int));//将tmp一段一段拷贝回a中
}void MergeSort(int* a, int n)//归并排序
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);tmp = NULL;
}

先不断分割,分割成长度为1数组进行比较,一段一段比较进行归并,开辟一个tmp空间,用来存放归并完的结果再将tmp一段一段的拷贝回a中。

非递归

通过循环来进行非递归,每组数据设为gap个,将gap个数据归并。

void MergeSortNonR(int* a, int n)//归并排序非递归
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}//每组数据有gap个int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap)//每次归并两组,i代表每组归并的起始位置{int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;int j = i;if (begin2 >= n)//当第二组都越界不存在时,这一组就不需要归并{break;}if (end2 >= n)//当第二组begin2没有越界,end2越界时,修正end2{end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));//一组一组往回拷贝}gap *= 2;}free(tmp);tmp = NULL;
}

7.非比较排序

思想:计数排序有称为鸽巢原理,是对哈希直接定址的变形应用

  1. 统计相同元素出现的次数
  2. 根据统计的结果将序列回收到原来的序列中

void CountSort(int* a, int n)//计数排序
{int min = a[0], max = a[0];for (int i = 1; i < n; i++){if (a[i] > max){max = a[i];}if (a[i] < min){min = a[i];}}int range = max - min + 1;int* count = (int*)calloc(range,sizeof(int));if (count == NULL){perror("calloc fail");return;}//统计次数for (int i = 0; i < n; i++){count[a[i] - min]++;}int j = 0;//排序for (int i = 0; i < range; i++){while (count[i]--){a[j++] = i + min;}}free(count);
}

计数排序的特性总结

  1. 计数排序在数据范围集中时,效率很高,但适用的范围及场景有限。
  2. 时间复杂度:O(N+range)
  3. 空间复杂度:O(range)

8.排序算法及稳定性分析

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

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

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

相关文章

Qt:信号槽

一. 信号槽概念 信号槽 是 Qt 框架中一种用于对象间通信的机制 。它通过让一个对象发出信号&#xff0c;另一个对象连接到这个信号的槽上来实现通信。信号槽机制是 Qt 的核心特性之一&#xff0c;提供了一种灵活且类型安全的方式来处理事件和数据传递。 1. 信号的本质 QT中&a…

aws凭证(一)凭证存储

AWS 凭证用于验证身份&#xff0c;并授权对 DynamoDB 等等 AWS 服务的访问。配置了aws凭证后&#xff0c;才可以通过编程方式或从AWS CLI连接访问AWS资源。凭证存储在哪里呢&#xff1f;有以下几个方法&#xff1a; 一、使用文件存储 1、介绍 文件存储适用于长期和多账户配置…

Win11下载和配置VSCode(详细讲解)

配置VSCode需要的工具&#xff1a; 一、MinGW-w64 二、Visual Studio Code 一、MinGW-w64下载 1、下载 MinGW官网地址&#xff1a; Downloads - MinGW-w64 直链下载&#xff1a; 下载 mingw-w64-install.exe &#xff08;MinGW-w64 - 适用于 32 位和 64 位 Windows&#…

Python简介以及解释器安装(保姆级教学)

目录 一、Python介绍 1、简介 2、特点 3、来源 4、发展 二、Python解释器的安装 1、安装包下载 2、下载完成后&#xff0c;点击安装包进入安装流程 一、Python介绍 1、简介 Python 是一门解释型、面向对象以及动态数据类型的高级程序设计语言&#xff0c;语法简洁&…

【论文速读】| RobustKV:通过键值对驱逐防御大语言模型免受越狱攻击

基本信息 原文标题&#xff1a;ROBUSTKV: DEFENDING LARGE LANGUAGE MODELS AGAINST JAILBREAK ATTACKS VIA KV EVICTION 原文作者&#xff1a;Tanqiu Jiang, Zian Wang, Jiacheng Liang, Changjiang Li, Yuhui Wang, Ting Wang 作者单位&#xff1a;Stony Brook University…

美畅物联丨智能分析,安全管控:视频汇聚平台助力智慧工地建设

随着科技的持续发展&#xff0c;建筑行业正朝着智能化的方向迅猛迈进。智慧工地作为建筑行业智能化的关键体现形式&#xff0c;借助各类先进技术来提升工地的管理效率、安全性以及生产效益。在这个过程中&#xff0c;视频汇聚平台发挥着极为重要的作用。以畅联AIoT开放云平台为…

AI赋能:PPT制作的创意革命

在现代信息社会&#xff0c;PPT已成为沟通和展示的利器。然而&#xff0c;如何快速制作出高质量的PPT&#xff0c;却是一门学问。幸运的是&#xff0c;智能生成PPT技术的出现&#xff0c;让这一切变得轻松自如。 ai生成PPT技术&#xff0c;犹如一位无形的助手&#xff0c;帮助用…

实战 | C#中使用YoloV8和OpenCvSharp实现目标检测 (步骤 + 源码)

导 读 本文主要介绍在C#中使用YoloV8实现目标检测,并给详细步骤和代码。 详细步骤 【1】环境和依赖项。 需先安装VS2022最新版,.NetFramework8.0,然后新建项目,nuget安装 YoloSharp,YoloSharp介绍: https://github.com/dme-compunet/YoloSharp 最新版6.0.1,本文…

蓝桥杯每日真题 - 第20天

题目&#xff1a;&#xff08;机房&#xff09; 题目描述&#xff08;13届 C&CG题&#xff09; 解题思路&#xff1a; 这道题目可以看作在一个无向图中查找两点之间的最短路径。题目中的 n 台电脑和 n−1 根网线形成了一棵树&#xff0c;树是一个特殊的无向图&#xff0c…

iOS应用网络安全之HTTPS

移动互联网开发中iOS应用的网络安全问题往往被大部分开发者忽略, iOS9和OS X 10.11开始Apple也默认提高了安全配置和要求. 本文以iOS平台App开发中对后台数据接口的安全通信进行解析和加固方法的分析. 1. HTTPS/SSL的基本原理 安全套接字层 (Secure Socket Layer, SSL) 是用来…

项目虚拟机配置测试环境

在企业中&#xff0c;有专门的服务器部署开发环境&#xff0c;测试环境等等 直接在虚拟机中打开虚拟机就可以 dps查看容器

初始ArkUI

一. 什么是ArkUI ArkUI基于方舟UI框架为应用的UI开发提供了完整的基础设施&#xff0c;UI语法更加简洁&#xff0c;丰富的UI功能&#xff08;组件、布局、动画以及交互事件&#xff09;&#xff0c;以及实现界面预览工具等&#xff0c;可以支持开发者进行可视化界面开发。 &a…

【PCIE常见面试问题-1】

PCIE常见面试问题-1 1 PCIE概述1.1 PCI为何发展开PCIE&#xff1f;1.2 什么是Root Complex(RC)1.3 什么是EP&#xff1f;1.4 什么是Swith1.5 PCIE协议如何组织通信的&#xff1f;1.6 简要介绍一下PCIE的分层结构&#xff0c;为什么需要分层&#xff1f;1.7 PCIE的事务类型有哪些…

用pyspark把kafka主题数据经过etl导入另一个主题中的有关报错

首先看一下我们的示例代码 import os from pyspark.sql import SparkSession import pyspark.sql.functions as F """ ------------------------------------------Description : TODO&#xff1a;SourceFile : etl_stream_kafkaAuthor : zxxDate : 2024/11/…

什么是反向 DNS 查找以及它的作用是什么?

反向DNS查询&#xff08;rDNS&#xff09;是一种技术&#xff0c;用于确定与某个IP地址对应的域名。当我们对一个IP地址进行反向DNS查询时&#xff0c;实际上是向域名系统&#xff08;DNS&#xff09;的特殊部分请求信息&#xff0c;这部分被称为PTR记录。PTR记录会返回与这个I…

HarmonyOS鸿蒙系统上File文件常用操作

HarmonyOS鸿蒙系统上&#xff0c;file文件常用操作记录 1.创建文件 createFile(fileName: string, content: string): string {// 获取应用文件路径let context getContext(this) as common.UIAbilityContext;let filesDirPath context.filesDir / fileName;// 新建并打开…

音视频pts/dts

现在的视频流有两个非常重要的时间戳&#xff0c;pts和dts&#xff0c;其中pts是显示的时候用&#xff0c;dts在解码的时候用。 pts很好理解&#xff0c;按照pts的顺序以及duration不间断的display就可以了。 dts在解码的时候用&#xff0c;那么这句话怎么理解&#xff0c;解…

输出比较简介

输出比较简介 主要是用来输出PWM波形&#xff0c;这个波形是驱动电机的&#xff08;智能车和机器人等&#xff09;必要条件 OC&#xff08;Output Compare&#xff09;输出比较&#xff0c;还有IC&#xff0c;全称是Input Capture&#xff0c;意为输入捕获&#xff0c;还有CC…

揭秘AIGC下的数字时代:交互设计的隐秘力量与未来革命

在当今数字化时代&#xff0c;交互设计已经成为我们日常生活中不可或缺的一部分。它不仅仅是关于产品或服务的界面设计&#xff0c;更是关于如何通过这些界面与人进行有效的沟通和互动。本文将探讨交互设计的深层含义、面临的挑战以及其对未来科技发展的影响。 文章来源&#x…

使用node-addon-api实现从c到nodejs模块全流程

目录 1 前言 2 安装nodejs 3 安装开发工具链 3.1 安装node-gyp 3.2 安装编译工具链&#xff08;C/C 编译器&#xff09; 4 初始化 Node.js 项目 4.1 创建项目目录 4.2 初始化 package.json 4.3 安装必要的库 5 编写代码 5.1 创建项目结构 5.2 编写动态库代码 5.3 编…