十大排序(C++版)
创始人
2024-05-28 19:41:57
0

测试排序的题目:

912. 排序数组 - 力扣(LeetCode)

在这里插入图片描述

堕落的做法:

class Solution 
{
public:vector sortArray(vector& nums) {sort(nums.begin(),nums.end());return nums;}
};

视频推荐:

91.基数排序的原理_哔哩哔哩_bilibili

代码推荐:

【C++】十大排序(冒泡、选择、插入、希尔、归并、快排、堆、计数、桶、基数) - 排序数组 - 力扣(LeetCode)

在这里插入图片描述

文章目录

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 希尔排序、
  • 归并排序
  • 快速排序
  • 堆排序
  • 计数排序
  • 桶排序
  • 基数排序

冒泡排序

在这里插入图片描述

  • 算法步骤:
    1. 比较相邻的元素:若第一个比第二个大,则交换;
    2. 遍历开始第一对到结尾最后一对,执行步骤1
    3. 重复步骤1~2,直到排序完成。
class Solution 
{
public:vector sortArray(vector& nums) {int n = nums.size();for (int i = 0; i < n - 1; ++i) {bool flag = false;for (int j = 0; j < n - 1 - i; ++j) {if (nums[j] > nums[j + 1]) {swap(nums[j], nums[j + 1]);flag = true;}                 }if (flag == false) break; //优化点:无交换代表已经排好序了}return nums;}
};

注意:本题中使用冒泡排序过不去,会超时

选择排序

在这里插入图片描述

  • 算法步骤:
    1. 初始状态:无序序列为R[0,n−1],长度n,有序区为空;
    2. 第i=1,…,n−1趟排序从当前无序区R[i−1,n−1]中选出最小的元素R[k],并将它与无序区的第1个记录R[i−1]交换,则R[0,i−1]变为元素个数增加1的新有序区,R[i,n−1]变为元素个数减少1的新无序区;
    3. n−1趟选择交换后结束。
class Solution 
{
public:vector sortArray(vector& nums){int n = nums.size();for (int i = 0; i < n; i++){int minindex = i;for (int j = i + 1; j < n; j++){if (nums[j] < nums[minindex]) minindex = j;}swap(nums[i], nums[minindex]);}return nums;}
};

注意:本题中使用选择排序过不去,会超时

插入排序

  • 算法步骤:
    1. 从第一个元素开始,该元素认为已经被排序;
    2. 取下一个元素,在已经排序的元素序列中从后向前扫描;
    3. 如果已排序元素大于新元素,将已排序元素移到下一位置;
    4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    5. 将新元素插入到该位置后;
    6. 重复步骤2~5

普通版本:

class Solution 
{
public:vector sortArray(vector& nums){int n = nums.size();for (int i = 1; i < n; i++){int currnumber = nums[i];int j = i - 1;for (; j >= 0; j--){if (nums[j] >= currnumber) swap(nums[j], nums[j + 1]);else break;}swap(currnumber, nums[j + 1]);}return nums;}
};

时间复杂度:最好O(n)最坏O(n^2)

空间复杂度:O(1)

希尔排序、

  • 算法步骤:
    1. 选择一个增量序列T1,T2,… ,Tk,其中Ti>Tj,Tk=1,i>j;
    2. 每趟排序,根据对应的增量Ti,将待排序列分割成若干子序列,分别对各子序列进行直接插入排序;
    3. 按增量序列个数k,对序列进行k趟排序。
class Solution 
{
public:vector sortArray(vector& nums) {int n = nums.size();// 分组,最开始时,间隔T为数组的一半for (int T = n / 2; T >= 1; T /= 2) {//对分组进行插入排序for (int i = T; i < n; ++i){shellSort(nums, T, i);}}return nums;}void shellSort(vector& nums, int T, int i) {int j, tmp = nums[i];for (j = i - T; j >= 0 && tmp < nums[j]; j -= T) {nums[j + T] = nums[j];}nums[j + T] = tmp;}
};

第一个通过的排序

归并排序

  • 算法步骤:
    1. 把长度为n的输入序列分成两个长度为n/2的子序列;
    2. 对这两个子序列分别采用归并排序;
    3. 将两个排序好的子序列合并成一个最终的排序序列。
class Solution 
{
public:vector sortArray(vector& nums){res.resize(nums.size(), 0);MerageSort(nums, 0, nums.size() - 1);return nums;}void MerageSort(vector& nums, int left, int right){if (left >= right) return;int mid = (left + right) / 2;MerageSort(nums, left, mid);MerageSort(nums, mid + 1, right);int l = left, r = mid + 1, i = left;while (l <= mid && r <= right){if (nums[l] < nums[r]) res[i++] = nums[l++];else res[i++] = nums[r++];}while (l <= mid) res[i++] = nums[l++];while (r <= right) res[i++] = nums[r++];for (int i = left; i <= right; i++) nums[i] = res[i];}
private:vector res;
};

归并排序最好情况下:O(nlogn),最坏情况下 O(nlogn),归并排序的空间复杂度 O(n),如果使用的是堆上空间,则操作时间可能更长(分配销毁时间太长)

快速排序

算法步骤:

  1. 从数列中挑出一个元素,称为基准pivot
  2. 分区partition操作:比基准值小的元素放在左边,比基准值大的元素放在右边;
  3. 递归recursive:把小于基准值元素的子数列和大于基准值元素的子数列分别递归排序。

普通的快排(选取中间点):

class Solution 
{
public:vector sortArray(vector& nums){QuickSort(nums, 0, nums.size() - 1);return nums;}void QuickSort(vector& nums, int left, int right){if (left >= right) return;int x = nums[(left + right) / 2], l = left - 1, r = right + 1;while (l < r){do l++; while (nums[l] < x);do r--; while (nums[r] > x);if (l < r)	swap(nums[l], nums[r]);}QuickSort(nums, left, r);QuickSort(nums, r + 1, right);}
};

优化后的快排(随机选点)

class Solution 
{
public:vector sortArray(vector& nums){QuickSort(nums, 0, nums.size() - 1);return nums;}void QuickSort(vector& nums, int left, int right){if (left >= right) return;int x = nums[rand() % (right - left + 1) + left], l = left - 1, r = right + 1;while (l < r){do l++; while (nums[l] < x);do r--; while (nums[r] > x);if (l < r)	swap(nums[l], nums[r]);}QuickSort(nums, left, r);QuickSort(nums, r + 1, right);}
};

**rand() % (b-a+1)+ a ** 就表示 a~b 之间的一个随机整数。

快速排序最好情况下:O(nlogn) ,最坏情况下退化为选择排序(例如每次选择的都是最大值,之后遍历左边的 n -1 序列):O(n^2)

堆排序

  • 算法步骤:
    1. 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
    2. 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n]
    3. 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

class Solution 
{
public:vector sortArray(vector& nums) {// heapSort 堆排序int n = nums.size();// 将数组整理成大根堆buildMaxHeap(nums);for (int i = n - 1; i >= 1; --i) {// 依次弹出最大元素,放到数组最后,当前排序对应数组大小 - 1swap(nums[0], nums[i]);--n;maxHeapify(nums, 0, n);}return nums;}void buildMaxHeap(vector& nums){int n = nums.size();for (int i = (n - 1) / 2; i >= 0; --i)//从最后一个节点的父节点开始进行堆排序{maxHeapify(nums, i, n);}}void maxHeapify(vector& nums, int i, int n){while (i * 2 + 1 < n){// 代表当前 i 节点的左右儿子;// 超出数组大小则代表当前 i 节点为叶子节点,不需要移位int lSon = 2 * i + 1;int rSon = 2 * i + 2;int large = i;if (lSon < n && nums[lSon] > nums[i]) large = lSon;if (rSon < n && nums[rSon] > nums[large]) large = rSon;if (large != i){swap(nums[i], nums[large]);// 迭代判断对应子节点及其儿子节点的大小关系i = large;}else//若当前根节点和左右节点和维持大根堆则跳出循环{break;}}}
};

什么是堆?堆有哪些特征?

堆是一个可以被看做一棵树的数组对象, 它的形状特征是完全二叉树且是顺序存储,数值特征:根总是优于左孩子和右孩子。

堆中的父结点和孩子结点的下标有什么特征

若 pos 是父亲节点下标(其节点编号是 pos + 1),左孩子的下标就是 2pos +1,右孩子是 2pos + 2

简述堆排序的流程:

1、对N个无序元素的数组简历大根堆

​ 从最后一个父亲节点开始,向前循环

​ 先比较左右孩子,再用胜者与父亲比较,如果孩子胜利,交换父亲和孩子,再重复检查交换后原父亲的稳定性。

2、交换堆顶和末尾元素,缩小堆的规模,从新的根节点出发,重建堆。

3、循环步骤2,堆的规模从n到2.

计数排序

  • 算法步骤:
    1. 找出待排序的数组中最大和最小的元素;
    2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
    3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
    4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
class Solution {
public:vector sortArray(vector& nums) {// CountSort 计数排序int n = nums.size();int minNum = INT_MAX, maxNum = INT_MIN;// 找到数组中的最小和最大元素 这里面最小的那个可能为负数for (int i = 0; i < n; ++i) {if (nums[i] < minNum) minNum = nums[i];if (nums[i] > maxNum) maxNum = nums[i];}// 构造计数数组vector counts(maxNum - minNum + 1, 0);for (int i = 0; i < n; ++i) {++counts[nums[i] - minNum];}// 计数排序int index = 0;for (int i = 0; i < counts.size(); ++i) {while (counts[i] != 0) {nums[index++] = i + minNum;counts[i]--;}}return nums;}
};

桶排序

  • 算法步骤:
    1. 设置一个定量的数组当作空桶;
    2. 遍历输入数据,并且把数据一个一个放到对应的桶里去;
    3. 对每个不是空的桶进行排序;
    4. 从不是空的桶里把排好序的数据拼接起来。
class Solution 
{
public:vector sortArray(vector& nums) {// BucketSort 桶排序int n = nums.size();// 获取数组的最小值和最大值int maxNum = nums[0], minNum = nums[0];for (int i = 1; i < n; ++i) {if (nums[i] > maxNum) maxNum = nums[i];if (nums[i] < minNum) minNum = nums[i];}// 初始化桶int bucketNum = 5, bucketSize = (maxNum - minNum) / bucketNum + 1;vector> buckets(bucketNum, vector(0));// 小至大分桶for (int num : nums) {int bucketIndex = (num - minNum) / bucketSize;buckets[bucketIndex].emplace_back(num);}// 桶内排序for (int i = 0; i < buckets.size(); ++i) {sort(buckets[i].begin(), buckets[i].end());}// 从桶中依次取数int index = 0;for (auto& bucket : buckets) {for (int num : bucket) {nums[index++] = num;}}return nums;}
};

基数排序

  • 算法步骤:
    1. 取得数组中的最大数,并取得位数;
    2. arr为原始数组,从最低位开始取每个位组成radix数组;
    3. radix进行计数排序(利用计数排序适用于小范围数的特点);
class Solution {public:vector sortArray(vector& nums) {// RadixSort 基数排序int n = nums.size();// 预处理,让所有的数都大于等于0for (int i = 0; i < n; ++i) {nums[i] += 50000; // 50000为最小可能的数组大小}// 找出最大的数字,并获得其最大位数int maxNum = nums[0];for (int i = 0; i < n; ++i) {if (nums[i] > maxNum) maxNum = nums[i];}int num = maxNum, maxLen = 0;while (num) {++maxLen;num /= 10;}// 基数排序,低位优先int divisor = 1;for (int i = 0; i < maxLen; ++i) {radixSort(nums, divisor);divisor *= 10;}// 减去预处理量for (int i = 0; i < n; ++i) {nums[i] -= 50000;}return nums;}void radixSort(vector& nums, int divisor)//divisor 目标位{vector> bucket(10, vector(0, 0));//桶for (int i = 0; i < nums.size(); i++)//根据目标位的大小依次放入桶中{int number = nums[i] / divisor % 10;bucket[number].push_back(nums[i]);}for (int i = 0, index = 0; i < bucket.size(); i++)//从桶中取出并放入到nums中{for (int j = 0; j < bucket[i].size(); j++){nums[index++] = bucket[i][j];}}}
};

相关内容

热门资讯

清朝有很多有名的历史人物,能给... 清朝有很多有名的历史人物,能给大家说几个清朝的历史人物吗?林则徐,张之洞,乾隆皇帝,曾国藩,朱耷,这...
曹丕的“太子四友”指的是谁 曹丕的“太子四友”指的是谁首先说,曹丕这四个,陈群司马懿是顶级的谋士和政治家,吴质有些小聪明,朱铄不...
在这次遇难者中存在了几名幸存者... 在这次遇难者中存在了几名幸存者。这句话是不是逻辑错误这句话的逻辑没有问题,有问题的是用词不当。遇难者...
在中国古代,有许多充满哲学智慧... 在中国古代,有许多充满哲学智慧的成语典故、寓言故事,如...在中国古代,有许多充满哲学智慧的成语典故...
清澈的意思是什么,… 清澈的意思是什么,…清净而明澈清而透明【造句】看着他清澈而又天真的眼眸,我的心久久不能平静……
蚂蚁森林合种爱情树一方退出怎么... 蚂蚁森林合种爱情树一方退出怎么找回来两个人合种的爱情树,我退出来,我怎么再次加入进去继续合作那个树?...
有好看的古代修炼小说推荐吗? 有好看的古代修炼小说推荐吗?古代重生穿越修炼......让我帮你找一下这些古代修炼的小说,找到这些类...
女主重生爱上前世辜负的人 女主重生爱上前世辜负的人重生我是你正妻渣女重生之竹马重生之弃渣重生之夫君可欺重生之换我疼你重生妇归来...
华胥引有广播剧吗 华胥引有广播剧吗现有的华胥引的广播剧是忆语广播剧社出品的,只有十三月和杯(这个是错字,请无视,居然输...
选文韩麦尔先生在说了,我的朋友... 选文韩麦尔先生在说了,我的朋友们我就要离开你们呢了,再见了银头鲑鱼tj75rt6yturdrruv ...
中通快递从北京保定市到广东揭阳... 中通快递从北京保定市到广东揭阳普宁要多久?中通快递从北京保定市到广东揭阳普宁要多久?从北京到广东需要...
关于离婚悲伤的歌曲 关于离婚悲伤的歌曲关于离婚悲伤的歌曲林俊杰《可惜没如果》 张靓颖《我走以后》 金志文《流着泪说分手》...
形容文笔差怎么说啊? 形容文笔差怎么说啊?哥哥姐姐,麻烦问下,我是做文员的,形容文案方面的工作很差应该怎么说啊?粗鄙怎么样...
我想做未婚妈妈,可行吗? 我想做未婚妈妈,可行吗?没关系吧?我同学好多他们妈妈都是30岁以后省得他们,都没事啊,但是如果你自己...
如何在两个excel表格里筛选... 如何在两个excel表格里筛选出重复的名字如何在两个excel表格里筛选出重复的名字1、电脑打开EX...
且试天下 哪些小说是用白绫做武... 且试天下 哪些小说是用白绫做武器的?神雕侠侣吖- -..小龙女一开始就是用白绫的聊斋 辛十四娘嘿嘿~...
自带高冷体质,笑起来温暖又治愈... 自带高冷体质,笑起来温暖又治愈的星座,你了解吗?虽然天生高冷体质,但是笑起来特别的温暖治愈的新作用天...
火星未解之谜 火星未解之谜多列举一些,每个事例最好长一点,谢啦~“火星人脸”, “地表被水冲击河道”,“原始大气和...
我是1991年10月4号生的,... 我是1991年10月4号生的,是什么星座啊有的说是处女座有的说是天平座,糊涂了,到底是什么啊很负责任...
梦见白狐狸,然后当时我骑着自行... 梦见白狐狸,然后当时我骑着自行车,我想躲开它,他很凶的的追赶我,最后它向我扑了过来,然后我就醒乐.你...