【笔试题心得】排序算法总结整理

 排序算法汇总

常用十大排序算法_calm_G的博客-CSDN博客

在这里插入图片描述 

 以下动图参考 十大经典排序算法 Python 版实现(附动图演示) - 知乎

冒泡排序

排序过程如下图所示:

image.png

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
// arr: 需要排序的数组; length: 数组长度 
//注: int cnt = sizeof(a) / sizeof(a[0]);获取数组长度
void BubbleSort(int arr[], int length) 
{for (int i = 0; i < length; i++){for (int j = 0; j < length -  i - 1; j++){if (arr[j] > arr[j + 1])swap(arr[j],arr[j+1]);}}
}

选择排序

排序过程如下图所示:

动图

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
  3. 以此类推,直到所有元素均排序完毕
  4. 时间负复杂度:O(n^2),空间O(1),非稳定排序,原地排序
void selectSort(vector<int>& nums) {int len = nums.size();int minIndex = 0;for (int i = 0; i < len; ++i) {minIndex = i;for (int j = i + 1; j < len; ++j) {if (nums[j] < nums[minIndex]) minIndex = j;}swap(nums[i], nums[minIndex]);}
}

插入排序

排序过程如下图所示:

动图

  1. 从第一个元素开始,该元素可以认为已经被排序

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

  5. 将新元素插入到该位置后

  6. 重复步骤2~5

void insertionSort(vector<int>& a, int n) {//{ 9,1,5,6,2,3 }for (int i = 1; i < n; ++i) {if (a[i] < a[i - 1]) {   //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入int j = i - 1;int x = a[i];     //复制为哨兵,即存储待排序元素//a[i] = a[i - 1];           //先后移一个元素,可以不要这一句,跟循环里面的功能重复了while (j >= 0 && x < a[j]) {   //查找在有序表的插入位置,还必须要保证j是>=0的 因为a[j]要合法a[j + 1] = a[j];j--;     //元素后移}a[j + 1] = x;     //插入到正确位置}}
}

快速排序

 排序过程如下图所示:

动图

动图看起来有点复杂,下面放一个分解图https://blog.csdn.net/qq_38082146/article/details/115453732

我们以[ 8,2,5,0,7,4,6,1 ]这组数字为例来进行演示

首先,我们随机选择一个基准值(虽然图中选择了随机元素,但是一般上会以第一个元素为基准值):

快速排序1

与其他元素依次比较,大的放右边,小的放左边:

快速排序2

然后我们以同样的方式排左边的数据:

快速排序3

继续排 0 和 1 :

快速排序4

由于只剩下一个数,所以就不用排了,现在的数组序列是下图这个样子:

快速排序5

 右边以同样的操作进行,即可排序完成。

1、选取第一个数为基准

2、将比基准小的数交换到前面,比基准大的数交换到后面

3、对左右区间重复第二步,直到各区间只有一个数

void quickSort(vector<int>&numbers, int low, int high) {//  numbers = {10,8,4,6,9,10,123,6,2,14,3,8,5};if (low >= high) return;int first = low, last = high, key = numbers[low];cout << low << " " << high << " "<<key << endl;for (int i = 0; i < numbers.size(); ++i) {cout << numbers[i] << " ";}cout << endl;while (first < last) {//从后往前找比他小的放前面,从前往后找比他大的放在后面,//以第一个数为基准,必须先从后往前走,再从前往后走while (first < last && numbers[last] >= key)last--;if (first < last) numbers[first++] = numbers[last];while (first < last && numbers[first] <= key)first++;if (first < last) numbers[last--] = numbers[first];}numbers[first] = key;cout << "the index " << first << "  value " << key << endl;quickSort(numbers, low, first - 1);quickSort(numbers, first + 1, high);
}

 希尔排序

 排序过程如下图所示:

 

希尔排序是插入排序的一种变种。无论是插入排序还是冒泡排序,如果数组的最大值刚好是在第一位,要将它挪到正确的位置就需要 n - 1 次移动。

也就是说,原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置,这样是相对比较花时间了。

希尔排序就是为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。

希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。


void shellSortCore(vector<int>& nums, int gap, int i) {int inserted = nums[i];int j;//  插入的时候按组进行插入for (j = i - gap; j >= 0 && inserted < nums[j]; j -= gap) {nums[j + gap] = nums[j];}nums[j + gap] = inserted;
}void shellSort(vector<int>& nums) {int len = nums.size();//进行分组,最开始的时候,gap为数组长度一半for (int gap = len / 2; gap > 0; gap /= 2) {//对各个分组进行插入分组for (int i = gap; i < len; ++i) {//将nums[i]插入到所在分组正确的位置上shellSortCore(nums,gap,i);}}}

归并排序

 排序过程如下图所示:

1、把长度为n的输入序列分成两个长度为n/2的子序列;

2、对这两个子序列分别采用归并排序;

3、 将两个排序好的子序列合并成一个最终的排序序列。

 

void mergeSortCore(vector<int>& data, vector<int>& dataTemp, int low, int high) {if (low >= high) return;int len = high - low, mid = low + len / 2;int start1 = low, end1 = mid, start2 = mid + 1, end2 = high;mergeSortCore(data, dataTemp, start1, end1);mergeSortCore(data, dataTemp, start2, end2);int index = low;while (start1 <= end1 && start2 <= end2) {dataTemp[index++] = data[start1] < data[start2] ? data[start1++] : data[start2++];}while (start1 <= end1) {dataTemp[index++] = data[start1++];}while (start2 <= end2) {dataTemp[index++] = data[start2++];}for (index = low; index <= high; ++index) {data[index] = dataTemp[index];}
}void mergeSort(vector<int>& data) {int len = data.size();vector<int> dataTemp(len, 0);mergeSortCore(data, dataTemp, 0, len - 1);
}

堆排序

分为创建堆和堆排序两个部分

创建堆(这里假定是大根堆)时,要保证每个父节点的值比左右子节点的值大

当每次堆排序完成后,最顶端的即是当前堆的最大值,随后可以将堆的最大值与堆的倒数第一个元素互换,因为此时当前最大值已经完成排序,将其赶出堆内,堆的size减1,剩下的元素进行堆重构。

堆重构的过程就是维持堆每个父节点的值大于左右子节点值的过程

#include <iostream>
#include <algorithm>
#include <vector>using namespace std;//i位置的数,向上调整大根堆
void heapInsert(vector<int>& arr, int i)
{while (arr[i] > arr[(i - 1) / 2])    //子节点比父节点大{swap(arr[i], arr[(i - 1) / 2]);i = (i - 1) / 2;}}//i位置的数发生了变化,又想维持住大根堆的结构
void heapify(vector<int>& arr, int i, int size)
{int l = 2 * i + 1;  //左孩子while (l < size){int best = l + 1 < size && arr[l + 1] > arr[l] ?  l + 1 : l;best = arr[best] > arr[i] ? best : i;if (best == i)break;swap(arr[i], arr[best]);i = best;l = 2 * i + 1;}}//从顶到底建立大根堆
//依次弹出堆内的最大值,并重新排好序
void heapSort(vector<int>& arr)
{int size = arr.size();for (int i = 0; i < size; i++)    //建立大根堆{heapInsert(arr, i);}while (size > 1){swap(arr[0], arr[size - 1]);  size--;cout<< arr[size] <<endl;heapify(arr, 0, size);}
}int main()
{vector<int> arr = { 3,2,1,5,6,4 };heapSort(arr);
}

计数排序

计数排序用于元素大小范围有限的数值排序。

  • 如果 k(待排数组的最大值) 过大则会引起较大的空间复杂度,一般是用来排序 0 到 100 之间的数字的最好的算法,但是它不适合按字母顺序排序人名。

统计小于等于该元素值的元素的个数i,于是该元素就放在目标数组的索引i位(i≥0)

计数排序

 

  1. 找出待排序的数组中最大和最小的元素;
  2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
  3. 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
  4. 向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;// 计数排序
void CountSort(vector<int>& vecRaw, vector<int>& vecObj)
{// 确保待排序容器非空if (vecRaw.size() == 0)return;// 使用 vecRaw 的最大值 + 1 作为计数容器 countVec 的大小int vecCountLength = (*max_element(begin(vecRaw), end(vecRaw))) + 1;vector<int> vecCount(vecCountLength, 0);// 统计每个键值出现的次数for (int i = 0; i < vecRaw.size(); i++)vecCount[vecRaw[i]]++;// 后面的键值出现的位置为前面所有键值出现的次数之和for (int i = 1; i < vecCountLength; i++)vecCount[i] += vecCount[i - 1];// 将键值放到目标位置for (int i = vecRaw.size(); i > 0; i--)	// 此处逆序是为了保持相同键值的稳定性vecObj[--vecCount[vecRaw[i - 1]]] = vecRaw[i - 1];
}int main()
{vector<int> vecRaw = { 0,5,7,9,6,3,4,5,2,8,6,9,2,1 };vector<int> vecObj(vecRaw.size(), 0);CountSort(vecRaw, vecObj);for (int i = 0; i < vecObj.size(); ++i)cout << vecObj[i] << "  ";cout << endl;return 0;
}

桶排序

 排序过程如下图所示:

桶排序

  1. 设置一个定量的数组当作空桶子。
  2. 寻访序列,并且把项目一个一个放到对应的桶子去。
  3. 对每个不是空的桶子进行排序。
  4. 从不是空的桶子里把项目再放回原来的序列中。
#include<stdio.h>
int main() {int book[1001],i,j,t;//初始化桶数组for(i=0;i<=1000;i++) {book[i] = 0;}//输入一个数n,表示接下来有n个数scanf("%d",&n);for(i = 1;i<=n;i++) {//把每一个数读到变量中去scanf("%d",&t);//计数  book[t]++;}//从大到小输出for(i = 1000;i>=0;i--) {for(j=1;j<=book[i];j++) {printf("%d",i);}}getchar();getchar();//getchar()用来暂停程序,以便查看程序输出的内容//也可以用system("pause");来代替return 0;
}

基数排序

基数排序

  1. 取得数组中的最大数,并取得位数;
  2. arr为原始数组,从最低位开始取每个位组成radix数组;
  3. 对radix进行计数排序(利用计数排序适用于小范围数的特点)
int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{int maxData = data[0];		///< 最大数/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。for (int i = 1; i < n; ++i){if (maxData < data[i])maxData = data[i];}int d = 1;int p = 10;while (maxData >= p){//p *= 10; // Maybe overflowmaxData /= 10;++d;}return d;
}
void radixsort(int data[], int n) //基数排序
{int d = maxbit(data, n);int *tmp = new int[n];int *count = new int[10]; //计数器int i, j, k;int radix = 1;for(i = 1; i <= d; i++) //进行d次排序{for(j = 0; j < 10; j++)count[j] = 0; //每次分配前清空计数器for(j = 0; j < n; j++){k = (data[j] / radix) % 10; //统计每个桶中的记录数count[k]++;}for(j = 1; j < 10; j++)count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中{k = (data[j] / radix) % 10;tmp[count[k] - 1] = data[j];count[k]--;}for(j = 0; j < n; j++) //将临时数组的内容复制到data中data[j] = tmp[j];radix = radix * 10;}delete []tmp;delete []count;
}

 

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

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

相关文章

【LeetCode-简单】剑指 Offer 29. 顺时针打印矩阵(详解)

题目 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;matrix [[1,2,3,4],[5,6,7,8],[9,10,1…

互联网发展历程:速度与效率,交换机的登场

互联网的演进就像一场追求速度与效率的竞赛&#xff0c;每一次的技术升级都为我们带来更快、更高效的网络体验。然而&#xff0c;在网络的初期阶段&#xff0c;人们面临着数据传输速度不够快的问题。一项关键的技术应运而生&#xff0c;那就是“交换机”。 速度不足的困境&…

规划性和可扩展性,助力企业全面预算管理的推进

对于当今社会经济市场的不稳定状况和不断变化的消费者行为&#xff0c;企业业务也从未像今天这样不可预测过。面对变化和变革&#xff0c;企业需要具备规划性的预测能力&#xff0c;才能使得自身在竞争中保持领先地位。那些具备前瞻性的企业都尝试在现阶段通过更好的规划不断提…

基于Mysqlrouter+MHA+keepalived实现高可用半同步 MySQL Cluster项目

目录 项目名称&#xff1a; 基于Mysqlrouter MHA keepalived实现半同步主从复制MySQL Cluster MySQL Cluster&#xff1a; 项目架构图&#xff1a; 项目环境&#xff1a; 项目环境安装包&#xff1a; 项目描述&#xff1a; 项目IP地址规划&#xff1a; 项目步骤: 一…

windows11下配置vscode中c/c++环境

本文默认已经下载且安装好vscode&#xff0c;主要是解决环境变量配置以及编译task、launch文件的问题。 自己尝试过许多博客&#xff0c;最后还是通过这种方法配置成功了。 Linux(ubuntu 20.04)配置vscode可以直接跳转到配置task、launch文件&#xff0c;不需要下载mingw与配…

localhost:8080 is already in use

报错原因&#xff1a;本机的8080端口号已经被占用。因为机器的空闲端口号是随机分配的&#xff0c;而idea默认启动的端口号是8080,所以是存在这种情况。 对于这个问题&#xff0c;我们只需要重启idea或者修改项目的启动端口号即可。 更推荐第二种。对于修改项目启动端口号&…

ZDH-wemock模块

本次介绍基于版本v5.1.1 目录 项目源码 预览地址 安装包下载地址 wemock模块 wemock模块前端 配置首页 配置mock wemock服务 下载地址 打包 运行 效果展示 项目源码 zdh_web: https://github.com/zhaoyachao/zdh_web zdh_mock: https://github.com/zhaoyachao/z…

TCGA数据下载推荐:R语言easyTCGA包

#使用easyTCGA获取数据 #清空 rm(listls()) gc() # 安装bioconductor上面的R包 options(BioC_mirror"https://mirrors.tuna.tsinghua.edu.cn/bioconductor") if(!require("BiocManager")) install.packages("BiocManager") if(!require("TC…

怎样让音频速度变慢?请跟随以下方法进行操作

怎样让音频速度变慢&#xff1f;在会议录音过程中&#xff0c;经常会遇到主讲人语速过快&#xff0c;导致我们无法清晰听到对方说的内容。如果我们能够减慢音频速度&#xff0c;就能更好地记录对方的讲话内容。此外&#xff0c;在听到快速播放的外语或方言时&#xff0c;我们也…

机器人的运动范围

声明 该系列文章仅仅展示个人的解题思路和分析过程&#xff0c;并非一定是优质题解&#xff0c;重要的是通过分析和解决问题能让我们逐渐熟练和成长&#xff0c;从新手到大佬离不开一个磨练的过程&#xff0c;加油&#xff01; 原题链接 机器人的运动范围https://leetcode.c…

高等数学教材重难点题型总结(二)导数与微分

本章重点题目较少&#xff0c;除了*标题页没什么特别难的&#xff0c;本帖出于总结性的角度考虑并未囊概全部的*标&#xff0c;最后会出一期*标题的全部内容整理&#xff0c;在攻克重难点的基础上更上一层楼。 1.根据定义求某点处的导数值 2.通过定义证明导数 3.左右导数的相关…

【数据库】P4 过滤数据 WHERE

过滤数据 WHERE 简介WHERE 子句操作符检测单个值案例范围值检查 BETWEEN AND空值检查 NULL 简介 数据库表一般包含大量的数据&#xff0c;很少需要检索表中的所有行。我们只检索所需数据需要指定搜索条件(search criteria)&#xff0c;搜索条件也称为过滤条件(filter conditio…

完全备份、增量备份、差异备份、binlog日志

Top NSD DBA DAY06 案例1&#xff1a;完全备份与恢复案例2&#xff1a;增量备份与恢复案例3&#xff1a;差异备份与恢复案例4&#xff1a;binlog日志 1 案例1&#xff1a;完全备份与恢复 1.1 问题 练习物理备份与恢复练习mysqldump备份与恢复 1.2 方案 在数据库服务器192…

问AI一个严肃的问题

chatgpt的问世再一次掀起了AI的浪潮&#xff0c;其实我一直在想&#xff0c;AI和人类的关系未来会怎样发展&#xff0c;我们未来会怎样和AI相处&#xff0c;AI真的会完全取代人类吗&#xff0c;带着这个问题&#xff0c;我问了下chatgpt&#xff0c;看一看它是怎么看待这个问题…

Modbus工业RFID设备在自动化生产线中的应用

传统半自动化生产线在运作的过程&#xff0c;因为技工的熟练程度&#xff0c;专业素养的不同&#xff0c;在制造过程中过多的人为干预&#xff0c;工厂将很难对每条生产线的产能进行标准化管理和优化。如果半自动化生产线系统是通过前道工序的作业结果和检测结果来决定产品在下…

react实现模拟弹框遮罩的自定义hook

需求描述 点击按钮用于检测鼠标是否命中按钮 代码实现 import React from react; import {useState, useEffect, useRef} from react;// 封装一个hook用来检测当前点击事件是否在某个元素之外 function useClickOutSide(ref,cb) {useEffect(()>{const handleClickOutside…

JMeter接口自动化测试实例—JMeter引用javaScript

Jmeter提供了JSR223 PreProcessor前置处理器&#xff0c;通过该工具融合了Java 8 Nashorn 脚本引擎&#xff0c;可以执行js脚本以便对脚本进行前置处理。其中比较典型的应用就是通过执行js脚本对前端数据进行rsa加密&#xff0c;如登录密码加密。但在这里我就简单的应用javaScr…

PyTorch翻译官网教程-NLP FROM SCRATCH: GENERATING NAMES WITH A CHARACTER-LEVEL RNN

官网链接 NLP From Scratch: Generating Names with a Character-Level RNN — PyTorch Tutorials 2.0.1cu117 documentation 使用字符级RNN生成名字 这是我们关于“NLP From Scratch”的三篇教程中的第二篇。在第一个教程中</intermediate/char_rnn_classification_tutor…

ChatGPT爆火,会给教育带来什么样的影响或者冲击?

近来&#xff0c;人工智能聊天机器人ChatGPT连上热搜&#xff0c;火爆全网。ChatGPT拥有强大的信息整合能力、自然语言处理能力&#xff0c;可谓是“上知天文&#xff0c;下知地理”&#xff0c;而且还能根据要求进行聊天、撰写文章等。 ChatGPT一经推出&#xff0c;便迅速在社…

stop job is running for Advanced key-value store

今天虚拟机磁盘撑满了&#xff0c;本来还能凑合运行&#xff0c;结果重启了下&#xff0c;就报了这个 stop job is running for Advanced key-value store (1min 59s / no limit) 解决方式很简单&#xff0c; 1、虚拟机关电源&#xff0c;任务管理器&#xff0c;关闭VM&#x…