排序篇

分析插入排序,选择排序和冒泡排序

总觉得直接写希尔排序不太好,这里把插入排序,选择排序和冒泡排序合起来分析一下。

插入排序

不说别的直接贴代码

#include<iostream>

//插入排序法

using namespace std;

void display(int a[],int N);
void sort(int a[], int N);

int main()
{
	int a[100] = { 0 }, N = 0;
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> a[i];
	}
	display(a, N);
	cout << endl;
	sort(a, N);
	display(a, N);
	return 0;
}

void display(int a[], int N)
{
	for (int i = 0; i < N; i++) 
	{
		cout << a[i] << " ";
	}

}

void sort(int a[], int N)
{
	int v = 0;
	int j = 0;
	for (int i = 1; i < N; i++)
	{
		v = a[i];
		j = i - 1;
		while (j >= 0 && a[j] > v)
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = v;
	}
}

代码分析

插入排序作为理解希尔排序的基础,掌握牢固还是有必要的。
不过插入排序作为一个很容易想到的排序方法,理解起来还是很容易的。
这里主要分析一下函数部分。
以输入数据为
5 2 4 6 1 3
在sort函数中外循环进行数组遍历。在第一轮循环中,用变量v记住a[1]的值,j=0;a[j]=5>v=2,所以,while循环的条件成立,接下来让a[1]=a[0]=5,j–后j为-1,while循环条件不成立,跳出循环,然后把v的值(也就是a[1]的值)赋给a[j+1](也就是a[0])。之后,随着i的值的增加,while循环用来找出数组中下标为i的数在0~i-1中的位置,并把它插入进去。

冒泡排序

#include<iostream>

//冒泡排序

using namespace std;

void display(int a[], int N);
int bubbleSort(int a[], int N);

int main()
{
	int a[100], N, swap;
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> a[i];
	}
	display(a, N);
	cout << endl;
	swap = bubbleSort(a, N);
	display(a, N);
	cout << endl;
	cout << "交换次数(体现数列错乱程度)" << swap << endl;
	return 0;
}

void display(int a[], int N)
{
	for (int i = 0; i < N; i++)
	{
		cout << a[i] << " ";
	}
}

int bubbleSort(int a[], int N)
{
	int sw = 0;
	int flag = 1;                                 //设定存在排序项
	int i = 0;                                    //设定为排序部分起始下标
	while (flag)
	{
		flag = 0;
		for (int j = N-1; j > i; j--)
		{
			if (a[j] < a[j - 1])
			{
				int temp;
				temp = a[j - 1];
				a[j - 1] = a[j];
				a[j] = temp;
				flag = 1;
				sw++;
			}
		}
		i++;
	}
	return sw;
}

代码分析

冒泡排序作为一种经典排序方式,应当深刻理解其过程。
还是以讲解bubbleSort函数为主。
首先由main函数传入数组和长度。
在函数中变量sw用来记录排序次数,设定bool类型变量flag将其定为1,假定第一次循环有可交换项。进入while循环中现将flag置为0,防止陷入死循环。for循环中从数组的最后一个元素开始遍历,比较a[j]与a[j-1],如果条件成立,就把两数交换。for循环遍历完一次便将剩余数中的最小数放于乱序部分的最前端。以此类推,最后达到排序的目的。

选择排序

#include<iostream>

//选择排序

using namespace std;

void display(int a[], int N);
int selectionSort(int a[], int N);

int main()
{
	int a[100] = { 0 }, N, sw;
	cin >> N;
	for (int i = 0; i < N; i++)
	{
		cin >> a[i];
	}
	display(a, N);
	cout << endl;
	sw = selectionSort(a, N);
	display(a, N);
	cout << endl;
	cout << "交换次数为" << sw << endl;
	return 0;
}

void display(int a[], int N)
{
	for (int i = 0; i < N; i++)
	{
		cout << a[i] << " ";
	}
}

int selectionSort(int a[], int N)
{
	int min, count = 0, i, j;
	for (i = 0; i < N; i++)
	{
		min = i;
		for (j = i + 1; j < N; j++)
		{
			if (a[j] < a[min])
			{
				min = j;
			}
		}
		int temp;
		temp = a[i];
		a[i] = a[min];
		a[min] = temp;
		count++;
	}
	return count;
}

代码分析

相比于插入排序,选择排序应该是最容易想到的一种排序方式了,通过两层循环来实现,外循环遍历数组,内层循环用来找到未排序部分的最小数记下其下标,当内循环遍历完后,在外循环进行swap,这样一步一步,将最小的数交换到未排序部分的最前端,从而达到排序的目的。

三种排序算法的总结

(以下分析基于挑战程序设计竞赛2)
对这三种算法的总结,需要引入时间复杂度空间复杂度这两个概念。

时间复杂度:评估执行程序所需的时间,可以估算程序对计算机处理器的使用程度。

空间复杂度:评估执行程序所需的储存空间,可以估算程序对计算机内存的使用程度。

设计或选择算法时,复杂度是衡量标准之一,不过,对于排序算法,还需要将“稳定排序”,考虑在内。

稳定排序(Stable Sort):当数据中存在2个或2个以上键值相等的元素时,这些元素排序处理前后顺序不变。

举例子

排序前

IDAB
player17080
player29095
player39560
player48095

按b的得分排序(稳定)

IDAB
player29095
player48095
player17080
player36060

按B的得分排序(不稳定)

IDAB
player48095
player29095
player17080
player39560

在这组数据中,player2和player4的B项得分相同,输入时player2在前,player4在后。稳定的排序算法能保证player2到player4的顺序输出,但不稳定的排序算法有可能输出palyer4到player2的情况。

下面具体分析:

插入排序

在插入排序中,只是将比v大的元素取出来向后移动,并没有改变不相邻元素之间的相对位置。
所以插入排序属于稳定排序
再分析插入排序的复杂度,按照最坏的可能,也就是每个i循环都需要执行i次移动,则总共需要1+2+……+N-1= ( N 2 − N ) 2 \frac{(N^2-N)}{2} 2(N2N)次移动,所以当N足够大时,算法的复杂度为O( N 2 N^2 N2).

插入排序根据输入数据的顺序其复杂度会有很大的差异,上面分析按照降序排列的情况。而如果输入数据为升序时,数组中的元素无需任何移动,只需要经历N次比较即可,这时,其复杂度为O(N).
插入排序的优点就是能够快速处理相对有序的数据

冒泡排序

冒泡排序对数组相邻元素进行比较和交换,因此并不会改变键值相同的元素的顺序,所以冒泡排序也属于稳定排序。但如果将代码中a[j]<a[j-1]改为a[j]<=a[j-1],那么在当a[j]=a[j-1]时,也会交换两数的位置,此时算法就会失去稳定性。

对于最坏的情况来说,冒泡排序需要对未排序部分的相邻的元素进行(N-1)+(N-2)+……+1= ( N 2 − N ) 2 \frac{(N^2-N)}{2} 2(N2N)次比较,所以算法的复杂度的量级为O( N 2 N^2 N2).
补充一点,冒泡排序中的交换次数又称反序数或逆序数,可以用来体现数列的错乱程度。

选择排序

这里参考书中的分析。
我们先看对于以下数据的排序
0.
3H5S3D1S
1.
将3H和1S交换
3H5S3D1S
交换后为
1S5S3D3H
2.
将5S和3D交换
1S5S3D3H
交换后
1S3D5S3H
3.
将5S和3H交换
1S3D5S3H
交换后
1S3D3H5S
我们可以看到对于这组数据键值开始为3H到3D。排序后变成了3D到3H。
即选择排序会直接交换两个不相邻的元素,所以属于不稳定的排序算法。
对于选择排序来说,无论在哪种情况下,选择排序需要进行N-1)+(N-2)+……+1= ( N 2 − N ) 2 \frac{(N^2-N)}{2} 2(N2N)次比较,来搜索未排序部分的最小值。所以选择排序的复杂度量级也为 O ( N 2 N^2 N2).

对于不含flag的简单冒泡排序和选择排序不依赖数据,而插入排序法在执行时却依赖数据,处理某些数据时具有很高的效率。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲困难的代码猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值