十大排序(C++版)

news2024/11/19 21:24:49

测试排序的题目:

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

在这里插入图片描述

堕落的做法:

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

视频推荐:

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

代码推荐:

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

在这里插入图片描述

文章目录

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

冒泡排序

在这里插入图片描述

  • 算法步骤:
    1. 比较相邻的元素:若第一个比第二个大,则交换;
    2. 遍历开始第一对到结尾最后一对,执行步骤1
    3. 重复步骤1~2,直到排序完成。
class Solution 
{
public:
    vector<int> sortArray(vector<int>& 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<int> sortArray(vector<int>& 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<int> sortArray(vector<int>& 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<int> sortArray(vector<int>& 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<int>& 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<int> sortArray(vector<int>& nums)
	{
		res.resize(nums.size(), 0);
		MerageSort(nums, 0, nums.size() - 1);
		return nums;
	}
	void MerageSort(vector<int>& 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<int> res;
};

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

快速排序

算法步骤:

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

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

class Solution 
{
public:
	vector<int> sortArray(vector<int>& nums)
	{
		QuickSort(nums, 0, nums.size() - 1);
		return nums;
	}
	void QuickSort(vector<int>& 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<int> sortArray(vector<int>& nums)
	{
		QuickSort(nums, 0, nums.size() - 1);
		return nums;
	}
	void QuickSort(vector<int>& 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<int> sortArray(vector<int>& nums) 
	{
		// heapSort 堆排序
		int n = nums.size();
		// 将数组整理成大根堆
		buildMaxHeap(nums);
		for (int i = n - 1; i >= 1; --i) 
		{
			// 依次弹出最大元素,放到数组最后,当前排序对应数组大小 - 1
			swap(nums[0], nums[i]);
			--n;
			maxHeapify(nums, 0, n);
		}
		return nums;
	}
	void buildMaxHeap(vector<int>& nums)
	{
		int n = nums.size();
		for (int i = (n - 1) / 2; i >= 0; --i)//从最后一个节点的父节点开始进行堆排序
		{
			maxHeapify(nums, i, n);
		}
	}

	void maxHeapify(vector<int>& 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<int> sortArray(vector<int>& 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<int> 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<int> sortArray(vector<int>& 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<vector<int>> buckets(bucketNum, vector<int>(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<int> sortArray(vector<int>& nums) 
	{
		// RadixSort 基数排序
		int n = nums.size();
		// 预处理,让所有的数都大于等于0
		for (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<int>& nums, int divisor)//divisor 目标位
	{
		vector<vector<int>> bucket(10, vector<int>(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];
			}
		}
	}
};

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

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

相关文章

洛谷:P1554 梦中的统计 JAVA

思路&#xff1a;定义一个长度为10的数组&#xff0c;数组下标代表数组元素的数字&#xff0c;比如arr[0]代表数字0.用一个for循环&#xff0c;对每个数先取余再取整&#xff0c;知道取整得到的数为0&#xff0c;说明该数字已经被拆解完了。今天又学了一个输入&#xff0c;原来…

2020蓝桥杯真题美丽的2(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小蓝特别喜欢 2&#xff0c;今年是公元 2020 年&#xff0c;他特别高兴。 他很好奇&#xff0c;在公元 1 年到公元 2020 年&#xff08;包含&#xff09;中&#xff…

论文投稿指南——中文核心期刊推荐(电影、电视艺术)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

73. python第三方库安装教程(超详细)

73. python第三方库安装教程&#xff08;超详细&#xff09; 文章目录73. python第三方库安装教程&#xff08;超详细&#xff09;1.知识回顾2. openpyxl 库的作用3. 第三方库的安装步骤【警告内容释义】4. 更新pip5. pip 常用命令1. 什么是pip2. pip --version 查看 pip 版本和…

车载雷达实战之Firmware内存优化

内存&#xff08;Memory&#xff09;是计算机中最重要的部件之一&#xff0c;计算机运时的程序以及数据都依赖它进行存储。内存主要分为随机存储器&#xff08;RAM&#xff09;,只读存储器&#xff08;ROM&#xff09;以及高速缓存&#xff08;Cache&#xff09;。仅仅雷达的原…

树莓派Pico W无线WiFi开发板使用方法及MicroPython编程实践

树莓派Pico W开发板是树莓派基金会于2022年6月底推出的一款无线WiFi开发板&#xff0c;它支持C/C和MicroPython编程。本文介绍树莓派Pico W无线WiFi开发板的使用方法及MicroPython编程示例&#xff0c;包括树莓派Pico W开发板板载LED使用及控制编程示例&#xff0c;Pico W开发板…

Spring——什么是IOC?

一、原则高内聚、低耦合二、什么是IOC&#xff1f;控制反转&#xff0c;把对象创建和对象之间的调用过程&#xff0c;交给spring进行管理三、使用IOC的目的是什么&#xff1f;降低耦合&#xff08;谁和谁的耦合&#xff1f;&#xff1f;如何降低的&#xff1f;&#xff09;原来…

openai-chatGPT的API调用异常处理

因为目前openai对地区限制的原因&#xff0c;即使设置了全局代理使用API调用时&#xff0c;还是会出现科学上网代理的错误问题。openai库 0.26.5【错误提示】&#xff1a;raise error.APIConnectionError(openai.error.APIConnectionError: Error communicating with OpenAI: …

泛型详解.

1 泛型的引入 问题&#xff1a;我们之前实现过的顺序表&#xff0c;只能保存 int 类型的元素&#xff0c;如果现在需要保存 指向 Person 类型对象的引用的顺序表&#xff0c;请问应该如何解决&#xff1f;如果又需要保存指向 Book 对象类型的引用呢&#xff1f; 之前写的顺序表…

红黑树-随记

文章目录1.为什么hashmap用红黑树不用二叉树和平衡二叉树1.1 二叉树&#xff08;Binary Search Tree&#xff09;1.2 红黑树&#xff08;Red Black Tree&#xff09;1.3 平衡二叉树&#xff08;Balence Binary Tree&#xff09;也称AVT2.为什么mysql用b数&#xff0c;不用B数或…

Windows程序员学习Linux环境下VI(VIM)编辑器的使用方法

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Windows程序员如何学习Linux环境知识。由于很多程序在Windows环境下开发好后&#xff0c;还要部署到Linux服务器上去&#xff0c;所以作为Windows程序员有必要学习Linux环境的知识。VI…

为什么KT6368A双模蓝牙芯片焊到板子上,没反应没收到芯片TX上电返回信息呢

目录 一、问题简介 为什么我把KT6368A芯片焊到板子上面&#xff0c;没有收到芯片TX的脚上电返回信息呢&#xff0c;而KT6368A芯片的2脚一直是2点多v的电压&#xff0c;换了好几个芯片都是这样 二、详细说明 一、问题简介 为什么我把KT6368A芯片焊到板子上面&#xff0c;没有…

QWidgetTable获取选中多行数据

QWidgetTable获取选中的多行数据获取选中行的行编号和打印指定第几列功能快捷键插入链接与图片创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能&#xff0c;丰富你的文章UML 图表FLowchart流…

buu [NPUCTF2020]这是什么觅 1

题目描述&#xff1a; 就一个这种文件&#xff0c;用记事本打开后&#xff1a; 题目分析&#xff1a; 打开后就一串看不懂的东西&#xff0c;想想这个东西曾经在 010editor 或 winhex中出现过&#xff08;右端&#xff09;既然如此那么我们就用它打开&#xff0c;得到&#…

使用 JaCoCo 生成测试覆盖率报告

0、为什么要生成测试覆盖率报告 在我们实际的工作中&#xff0c;当完成程序的开发后&#xff0c;需要提交给测试人员进行测试&#xff0c;经过测试人员测试后&#xff0c;代码才能上线到生产环境。 有个问题是&#xff1a;怎么能证明程序得到了充分的测试&#xff0c;程序中所…

线程池和ThreadLocal详解

线程池和ThreadLocal详解线程池池化模式&#xff1a;线程池里的线程数量设定为多少比较合适?添加线程规则&#xff1a;实现原理&#xff1a;线程池实现任务复用的原理线程池状态&#xff1a;Executors 创线程池工具类手动创建&#xff08;更推荐&#xff09;&#xff1a;自动创…

高码率QPSK调制解调方案(FPGA实现篇)

在前面的章节中,已经讲过QPSK调制的方案和Matlab算法仿真,在本篇中,主要讲解基于FPGA的高速QPSK调制的实现。根据前面提到的技术指标,本系统传输的数据速率为500Mbps,中频为720MHz,因此,传统的串行QPSK调制已经不合适在FPGA中实现,需采用全数字的并行方式进行调制,具体…

电商API是什么?为什么要用?主要应用场景有哪些?

电商API是什么&#xff1f;API是application programming interface&#xff08;应用程序接口&#xff09;的简称&#xff0c;实际上是一些预先定义的函数&#xff0c;目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力&#xff0c;而又无需访问源码&#x…

强强联合,再强的英伟达NVIDIA也不落俗套

强强联合&#xff0c;全球科技领域永恒的话题【科技明说 &#xff5c; 每日看点】前些天&#xff0c;我看到GPU领域的英伟达(Nvidia)与微软(Microsoft)做了一项十年期的云计算协议&#xff0c;起初我以为微软Microsoft Azure与英伟达GPU方面有所合作&#xff0c;其实不然&#…

微小目标识别研究(1)——白酒杂质识别

文章目录研究项目简介基于机器视觉技术的白酒杂质检测系统研究&#xff08;大概浏览&#xff09;研究背景国内外研究现状和发展趋势国内国外总结白酒杂质检测算法YOLO V3算法K近邻算法滤波处理动态范围增强形态学图像处理运动目标提取数据集制作数据增强基于机器视觉的液体药品…