经典排序算法总结

排序算法

常说的十大排序算法为:冒泡、选择、插入、希尔、归并、快速、堆、计数、桶、基数

冒泡排序

冒泡排序的本质在于交换,即每次通过交换的方式把当前剩余元素的最大值移动到一端。而当剩余元素减少为0时,排序结束。这个算法名字的由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

算法步骤

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

动图演示:

动图演示

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int n;//元素个数
	int m;//输入元素大小
	cin >> n;
	vector<int> a;//动态数组
	for (int i = 0; i < n; i++)
	{
		cin >> m;
		a.push_back(m);
	}
//冒泡排序	
	for (int i = 1; i < a.size(); i++)
	{
		for (int j = 0; j < a.size() - i; j++)
		{
			if (a[j] > a[j + 1])
			{
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}
选择排序

选择排序是指,对一个序列A中的元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[1,i-1]形成新的有序区间[1,i]形成。于是在n趟操作后,所有元素就会是有序的。时间复杂度是O(n²)

算法步骤:

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

动图演示:

动图演示

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int n;//元素个数
	int m;//输入元素大小
	cin >> n;
	vector<int> a;//动态数组
	for (int i = 0; i < n; i++)
	{
		cin >> m;
		a.push_back(m);
	}
	//选择排序,
	for (int i = 0; i < a.size()-1; i++)
	{
		int min = i;
		for (int j = i+1; j < a.size(); j++)
		{
			if (a[j] < a[min])
				min = j;//记录目前能找到的最小值元素的下标
		}

		if(i!= min)//将找到的最小值和i进行交换
		{
			int temp = a[i];
			a[i] = a[min];
			a[min] = temp;
		}
		
	}

	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}
插入排序

插入排序是将待插入元素一个个插入初始已有序部分中的过程,而插入位置的选择遵循了使插入后仍然保持有序的原则,具体的做法一般是从后往前枚举已有序部分来确定插入位置。

算法步骤:

  1. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

动图演示:

动图演示

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int n;//元素个数
	int m;//输入元素大小
	cin >> n;
	vector<int> a;//动态数组
	for (int i = 0; i < n; i++)
	{
		cin >> m;
		a.push_back(m);
	}
	//插入排序
	for (int i = 1; i < a.size(); i++)
	{
		int temp = a[i];//待插入的数据
		int j = i;
		while (j > 1 && temp < a[j - 1])
		{
			a[j] = a[j - 1];//将待插入的数据不停的比较,向左移动
			j--;
		}
		if (i != j)
		{
			a[j] = temp;//插入数据
		}
	}

	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}
快速排序

快速排序是排序算法中平均时间复杂度为O(nlogn)的一种算法,其实现需要先解决这样一个问题:对一个序列A[0]、A[1]、…、A[n-1],调整序列中元素的位置,使得A[0](原序列的A[1],下同)的左侧所有元素都不超过A[1]、右侧所有元素都大于A[1]。例如对序列{5,3,9,6,4,1}来说,可以调整序列中元素的位置,形成序列{3,1,4,5,9,6},这样就让A[0]=5左侧的所有元素都不超过它、右侧的所有元素都大于它。

快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

算法步骤:

  1. 从数列中挑出一个元素,称为 “基准”
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

也就是:

①先将A[0]存至某个临时变量temp,并令两个下标left、right分别指向序列首尾(如令left=0、right=n-1)。
②只要right 指向的元素A[right]大于temp,就将right 不断左移;当某个时候A[right]
≤temp时,将元素A[right]挪到left指向的元素A[left]处。
③只要left指向的元素A[left]不超过temp,就将left不断右移;当某个时候A[left]>temp时,将元素A[left]挪到right指向的元素A[right]处。
④重复②③,直到left与right相遇,把temp(也即原A[0])放到相遇的地方。

动图演示:

动图演示

代码:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1000;

int partition(int a[], int left, int right)
{
	int temp = a[left];
	while (left < right) {
		while (left < right && temp < a[right]) right--;
		a[left] = a[right];
		while (left < right && temp >= a[left]) left++;
		a[right] = a[left];
		
	}

	a[left] = temp;
	return left;
}

void quicksort(int a[], int left, int right)
{
	if (left < right) {
		int pos = partition(a, left, right);
		quicksort(a, left, pos - 1);
		quicksort(a, pos + 1, right);
	}
}

int main()
{
	int n;//元素个数
	int m;//输入元素大小
	cin >> n;
	//vector<int> a;//动态数组
	int a[maxn];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
		//a.push_back(m);
	}
	//希尔排序
	/*for (int gap = a.size() / 2; gap > 0; gap /= 2) {
		for (int i = gap; i < a.size(); i++) {
			int key = a[i];
			int j;
			for (j = i - gap; j > 0 && a[j] > key; j = j - gap)
				a[j + gap] = a[j];
				a[j + gap] = key;
		}
	}*/
	quicksort(a, 0, n-1);


	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}
归并排序

两路归并排序代码:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 100;


//2路归并排序
void merge(int a[], int L1, int R1, int L2, int  R2)
{
	int i = L1;
	int j = L2;
	int temp[maxn], index = 0;
	while (i <= R1 && j <= R2) {
		if (a[i] <= a[j])
		{
			temp[index++] = a[i++];
		}
		else {
			temp[index++] = a[j++];
		}
	}
	while (i <= R1) {
		temp[index++] = a[i++];
	}
	while (j <= R2) {
		temp[index++] = a[j++];
	}
	//合并后的序列赋值回数组a
	for (int i = 0; i < index; i++)
		a[i + L1] = temp[i];
}

void mergesort(int a[], int left, int right) {
	if (left < right) {
		int mid = (left + right) / 2;
		mergesort(a, left, mid);
		mergesort(a, mid + 1, right);
		merge(a, left, mid, mid + 1, right);//两个有序的子区间合并
	}
}

int main()
{
	int n;//元素个数
	//int m;//输入元素大小
	cin >> n;
	//vector<int> a;//动态数组
	int a[maxn];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
		//a.push_back(m);
	}
	mergesort(a, 0, n - 1);

	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}
// sort.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#pragma
//#include "pch.h"
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1000;

/* int partition(int a[], int left, int right)
{
	int temp = a[left];
	while (left < right) {
		while (left < right && temp < a[right]) right--;
		a[left] = a[right];
		while (left < right && temp >= a[left]) left++;
		a[right] = a[left];		
	}

	a[left] = temp;
	return left;
} */

//2路归并排序
void merge(int a[], int L1, int R1, int L2, int  R2)
{
	int i = L1;
	int j = L2;
	int temp[maxn], index = 0;
	while (i <= R1 && j <= R2) {
		if (a[i] <= a[j])
		{
			temp[index++] = a[i++];
		}
		else {
			temp[index++] = a[j++];
		}
	}
	while (i <= R1) {
		temp[index++] = a[i++];
	}
	while (j < R2) {
		temp[index++] = a[j++];
	}
	//合并后的序列赋值回数组a
	for (int i = 0; i < index; i++)
		a[i + L1] = temp[i];
}

void mergesort(int a[], int left, int right) {
	if (left < right) {
		int mid = (left + right) / 2;
		mergesort(a, left, mid);
		mergesort(a, mid + 1, right);
		merge(a, left, mid, mid + 1, right);//两个有序的子区间合并
	}
}

int main()
{
	int n;//元素个数
	//int m;//输入元素大小
	cin >> n;
	//vector<int> a;//动态数组
	int a[maxn];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
		//a.push_back(m);
	}
	mergesort(a, 0, n - 1);

	for (int i = 0; i < n; i++)
	{
		cout << a[i] << endl;
	}
	return 0;

}


// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值