常见的排序算法 【复习笔记】

注意:

  1. 后面的排序算法实现都只考虑升序,对于逆序,只有知道原理,实现很容易

  2. 案例题:

题目描述:将读入的 N 个数从小到大输出 ( 1 <= N <=10e5)

输入描述:第一行一个正整数 N

第二行 N 个空格隔开的正整数 ai

输出描述:给定的 N 个数从小到大输出,空格隔开,行末换行无空格

输入:5

4 2 4 5 1

输出:1 2 4 4 5

1. 插入排序

1.1 算法思想

每次将一个待排序元素按照关键字大小插入到已排序好的序列中

时间复杂度:1. 当整个序列升序,时间复杂度O(n)

  1. 当整个序列逆序,时间复杂度O(N^2)

1.2 代码实现

cpp 复制代码
​#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

void insert_sort()
{
	//从第二个元素开始,依次枚举待排序元素
	for (int i = 2; i <= n; i++)
	{
		//先存储要排序的元素
		int tmp = a[i];
		//依次和前面的元素比较
		int j = i - 1;
		while ( a[j] > tmp&&j >= 1)
		{
			a[j+1] = a[j];
			j--;
		}
		//当出循环时,要么a[j]<=tmp,要么遍历完已排序序列
		//直接在j+1的位置放入元素
		a[j+1] = tmp;
	}
}
int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//插入排序
	insert_sort();
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}

​

2. 选择排序

2.1 算法思想

每次找出未排序序列的最小元素,然后放入有序序列后面

时间复杂度:总会遍历一遍所有未排序序列,时间复杂度O(n^2)

2.2 代码实现

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

void selection_sort()
{
	//待排序序列为 [i,n]
	for (int i = 1; i < n; i++)
	{
		//从 [i,n] 中选择最小值的位置
		int pos = i;
		for (int j = i+1; j <= n; j++)
		{
			if (a[pos] > a[j]) pos = j;
		}
		//进行交换,将最小元素放入有序序列后面
		swap(a[i], a[pos]);
	}
}

int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//选择排序
    selection_sort();
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}

3. 冒泡排序

3.1 算法思想

进行 n -1 趟操作,每一趟进行多次比较,每次比较相邻的两个元素,如果满足排序条件,就交换;每趟可以将一个元素放到正确位置,像气泡一样,气泡大的慢慢冒出来,较大的数慢慢到最终的位置上

对于最朴素的冒泡排序,如果数组本身就是升序,还是会进行 n -1 趟操作,时间复杂度O(n^2)

但我们可以进行优化,如果有一趟操作,所有的元素都没有交换,证明数组已经是升序了,不用进行接下来的操作

优化后的冒泡排序时间复杂度:

  1. 数组升序,只扫描一遍,时间复杂度O(n)

  2. 数组逆序,时间复杂度O(n^2)

3.2 代码实现

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

void bubble_sort()
{
	//进行 n - 1 趟排序
	for (int i = n; i > 1; i--)
	{
		//标记是否交换元素
		bool flag = false;
		// [ 1,i] 为待排序区间
		for (int j = 1; j < i; j++)
		{
			//每一趟比较相邻元素
			if (a[j] > a[j + 1])
			{
				swap(a[j], a[j + 1]);
				flag = true;
			}
			
		}
		//如果没交换元素,直接结束
		if (flag==false) return;
	}
}

int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//冒泡排序
    bubble_sort();
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}

4. 堆排序

4.1 算法思想

本质是优化选择排序,将数据放入堆(堆和STL ------ priority------queue)中,可以快速得到待排序元素的最小值或最大值

步骤:1. 建堆,升序大根堆,降序小根堆。

注意: 建堆不是新建一个堆结构,将数据放入新建的堆结构中。而是将数组的逻辑结构化为堆结构

具体方法:从倒数第一个非叶子结点开始,倒着到根结点位置,每个结点向下调整

  1. 排序,每次将堆顶元素和堆最后一个元素交换,堆元素就少一个,将堆顶元素向下调整。直到堆剩一个元素

时间复杂度:1. 建堆的时间复杂度通过每个结点的调整层数计算约为O(n)

  1. 排序的时间复杂度为O(nlog n)

所以总的时间复杂度为O(nlog n)

4.2 代码实现

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

//向下调整
void down(int parent, int len)
{
	int child = parent * 2;
	while (child <= len)
	{
		if (child + 1 <= len && a[child] < a[child+1])child++;
		if (a[parent] >= a[child])return;
		swap(a[parent], a[child]);

		parent = child;
		child = parent * 2;
	}
}

void heap_sort()
{
	//建堆,从第一个非叶结点开始
	for (int i = n / 2; i >= 1; i--)
	{
		down(i, n);
	}
	//排序
	for (int i = n; i > 1; i--)
	{
		swap(a[i], a[1]);
		//堆元素减去1
		down(1, i-1);
	}
}
int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//堆排序
    heap_sort();
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}

5. 快速排序

5.1 算法思想

5.1.1 常规快排

在待排序区间任取一个元素作为基准元素,按照这个基准元素的大小将区间元素分为左右两个部分,左边区间元素小于基准元素,右边区间元素大于等于基准元素。再递归排序基准值左区间和基准值右区间(其实,快速排序有点像树的先序遍历)

5.1.2 优化

有两种极端情况:

a. 如果每次选择基准元素为待排序序列的最左边元素,那如果整个序列升序,右区间每次划分都会很长,递归层数就是 n 层,那时间复杂度就退化为O(n^2)

b. 如果每次选择基准元素为待排序序列的中间元素,那如果整个序列为相同元素,那时间复杂度依旧会退化为O(n^2)

优化一:随机选择基准元素

cpp 复制代码
#include<iostream>
#include<ctime>
using namespace std;

int main()
{
	//种下一个种子
	srand(time(0));
	//获得一个随机数
	rand();

	return 0;
}

优化二:数组分三块(数组分块问题 【刷题反思】-CSDN博客

按基准元素,小于基准元素的放入左边区间,等于基准元素的放入中间区间,大于基准元素的放入右边区间。中间区间不用管,递归处理左右区间即可

经过优化,块排的时间复杂度为O(nlog n)

5.2 代码实现

cpp 复制代码
#include<iostream>
#include<ctime>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

int get_rand(int left,int right)
{
	//保证随机元素在区间内
	return  a[rand() % (right - left + 1) + left];
}

void quick_sort(int left, int right)
{
    //递归结束条件
    if (left >= right) return;
	//得到随机基准元素
	int q = get_rand(left,right);

	//排序,数组分三块
	int l = left - 1, i = left, r = right + 1;
	while (i < r)
	{
		if (a[i] < q) swap(a[++l], a[i++]);
		else if (a[i] == q)i++;
		else swap(a[--r], a[i]);
	}
	
	//递归处理左右区间
	quick_sort(left, l);
	quick_sort(r, right);
}
int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//快速排序
    //种下一个随机数种子
    srand(time(0));
    quick_sort(1,n);
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}

6. 归并排序

6.1 算法思想

归并排序的思想是分治思想,先将整个数组区间从中间一分为二,然后把左右区间排序合并。这是递归实现的,每次都将先将区间一分为二,再排序(这一过程类似树的后序遍历)

时间复杂度:每次都是一分为二,时间复杂度可以稳定O(nlog n)

6.2 代码实现

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int n;

//创建辅助数组合并两个有序序列
int tmp[N];
void merge_sort(int left, int right)
{
	//递归结束条件
	if (left >= right) return;
	//先一分为二
	int mid = (left + right) / 2;
	//左右区间先有序
	merge_sort(left, mid);
	merge_sort(mid + 1, right);

	//合并左右两个有序区间
	int cur1 = left, cur2 = mid+1, i = left;
	while (cur1 <= mid && cur2 <= right)
	{
		if (a[cur1] <= a[cur2]) tmp[i++] = a[cur1++];
		else tmp[i++] = a[cur2++];
	}
	while (cur1 <= mid)tmp[i++] = a[cur1++];
	while(cur2 <= right)tmp[i++] = a[cur2++];
	for (int j = left; j <= right; j++)
	{
		a[j] = tmp[j];
	}
}
int main()
{
	cin >> n;
	//从a[1]开始放入元素
	for (int i = 1; i <= n; i++) cin >> a[i];

	//归并排序
    merge_sort(1,n);
	
	for (int i = 1; i <= n; i++)cout << a[i] << " ";

	return 0;
}
相关推荐
汇能感知5 分钟前
光谱相机的光谱数据采集原理
经验分享·笔记·科技
人人题28 分钟前
汽车加气站操作工考试答题模板
笔记·职场和发展·微信小程序·汽车·创业创新·学习方法·业界资讯
小脑斧爱吃鱼鱼43 分钟前
鸿蒙项目笔记(1)
笔记·学习·harmonyos
uhakadotcom1 小时前
Apache Airflow入门指南:数据管道的强大工具
算法·面试·github
跳跳糖炒酸奶1 小时前
第四章、Isaacsim在GUI中构建机器人(2):组装一个简单的机器人
人工智能·python·算法·ubuntu·机器人
绵绵细雨中的乡音2 小时前
动态规划-第六篇
算法·动态规划
程序员黄同学2 小时前
动态规划,如何应用动态规划解决实际问题?
算法·动态规划
march_birds2 小时前
FreeRTOS 与 RT-Thread 事件组对比分析
c语言·单片机·算法·系统架构
lulinhao2 小时前
HCIA/HCIP基础知识笔记汇总
网络·笔记
斯汤雷3 小时前
Matlab绘图案例,设置图片大小,坐标轴比例为黄金比
数据库·人工智能·算法·matlab·信息可视化