分析插入排序,选择排序和冒泡排序
总觉得直接写希尔排序不太好,这里把插入排序,选择排序和冒泡排序合起来分析一下。
插入排序
不说别的直接贴代码
#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个以上键值相等的元素时,这些元素排序处理前后顺序不变。
举例子
排序前
ID | A | B |
---|---|---|
player1 | 70 | 80 |
player2 | 90 | 95 |
player3 | 95 | 60 |
player4 | 80 | 95 |
按b的得分排序(稳定)
ID | A | B |
---|---|---|
player2 | 90 | 95 |
player4 | 80 | 95 |
player1 | 70 | 80 |
player3 | 60 | 60 |
按B的得分排序(不稳定)
ID | A | B |
---|---|---|
player4 | 80 | 95 |
player2 | 90 | 95 |
player1 | 70 | 80 |
player3 | 95 | 60 |
在这组数据中,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(N2−N)次移动,所以当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(N2−N)次比较,所以算法的复杂度的量级为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(N2−N)次比较,来搜索未排序部分的最小值。所以选择排序的复杂度量级也为 O (
N
2
N^2
N2).
对于不含flag的简单冒泡排序和选择排序不依赖数据,而插入排序法在执行时却依赖数据,处理某些数据时具有很高的效率。