PTA 排序 思路分析及代码解析v0.9
一、前导
1. 需要掌握的知识
- 熟悉各种常见排序算法
2. 题目信息
- 题目来源:PTA / 拼题A
- 题目地址:排序
二、解题思路分析
1. 题意理解
排序算法练习题
1. 1 输入数据
11 //待排序的元素数
4 981 10 -17 0 -20 29 50 8 43 -5 //待排序的元素
1.2 输出数据
输出从小到大排好序的元素,各个元素直接用空格隔开,末尾不能有空格
-20 -17 -5 0 4 8 10 29 43 50 981
2. 思路分析
- 依据各种排序算法的思路实现,通过实际编码进一步熟悉各种排序算法
- 简便方法:调用sort函数。sort函数包含在头文件为 algorithm 的c++标准库中,sort( )的时间复杂度为nlog2n,比冒泡等排序算法的效率要高
三、具体实现
1. 一点感想
- 想起来容易、实现起来简单的排序算法,效率都不会太高;效率高的排序算法,不太好想、也比较难实现。总体而言,还是C++ STL好用
2. 代码框架(重点)
2.1 采用的数据结构
- 数组
#define max 100000
int a[max];
2.2 程序主体框架
程序伪码描述
int main()
{
1.输入数据
2.执行排序算法
3.按要求输出
}
2.3 各排序算法
2.3.1 冒泡排序
冒泡排序具体实现如下:
(1) 比较相邻元素。如果第一个比第二个大,就交换这两个元素
(2) 对每一对相邻元素做同样的操作,从开始第一对到结尾最后一对。比较结束后,最后的元素一定是最大的
(3) 除了最后一个元素,对其他所有元素重复步骤(1) (2)
(4) 每次对越来越少的元素重复上面的步骤,直到没有任何一对元素需要比较
注意:冒泡排序是稳定的排序方法。相同权重的值,在排序后还能保证他们的先后顺序和排序前一致,就是稳定排序
void bubble_sort(int n)
{
int tmp;
bool flag=false;
for (int k = n; k > 1; k--)
{
flag = false;
for (int i = 0; i < k - 1; i++) // One loop mean a trip of bubble_sort. The 'flag==false' is equal all element in the right sequence
{
if (a[i] > a[i + 1])
{
tmp = a[i];
a[i] = a[i + 1];
a[i + 1] = tmp;
flag = true;
}
}
if (!flag) break;
}
return;
}
2.3.2 选择排序
选择排序的基本思路如下:
(1) 从待排序的元素中选出最小值,存放在序列的起始位置(与数组起始位置的元素交换位置)
(2) 再从剩余的未排序元素中找到最小值,放到已排序序列的末尾(与未排序序列的起始元素交换位置)
(3) 循环,直到待排序的数据元素的个数为零
注意:选择排序是不稳定的排序方法,比如序列:2,2,1 选择排序会改变两个数字2的位置
void select_sort(int n)
{
int i, j, min,tmp;
for (i = 0; i < n ; i++)
{
min = i;
for (j = i + 1; j < n; j++)
{
if (a[j] < a[min])
min = j;
}
if (min != i)
{
tmp = a[i];
a[i] = a[min];
a[min] = tmp;
}
}
return;
}
2.3.3 插入排序
插入排序的基本思路:
(1) 将待排序序列看成一副乱序的扑克牌
(2) 依次抓取,插入到合适位置
注意:插入排序是稳定的排序算法
void insert_sort(int n) // The similar as arrange the playing card
{
int P,i,tmp;
for (P = 1; P < n; P++)
{
tmp = a[P]; //draw a card
for (i=P; i > 0 && a[i-1] > tmp; i--)
a[i] = a[i - 1]; //move the sorted card and fine the position
a[i] = tmp; //insert the position
}
return;
}
2.3.4 希尔排序
希尔排序的基本思路:
(1) 希尔排序是插入排序的增强版:增强点 交换一次元素位置可以消去多个逆序对。插入排序交换一次位置只能消去一个逆序对。按从小到大排序,对于序列 3 2 4中的3 2就是一个逆序对
(2) 希尔排序必须使用合适的步长,否则在某些场景下,效率反而会低于插入排序,比如最差的一种情况,只有1步长的排序起作用,其他步长未消去逆序对
(3) 希尔排序常用的步长数组是 Sedgewick数组 { 929, 505, 209, 109, 41, 19, 5, 1, 0 }
(4) 除了步长外(插入排序的步长是1),排序思路与插入排序是一致的
希尔排序是不稳定的排序,例如序列 3 1 2 4 1 起始步长为2
void shell_sort(int n) // Shell sort that use "Sedgewick list"!
{
int wick_index, step, position, tmp, i;
int Sedgewick[] = { 929, 505, 209, 109, 41, 19, 5, 1, 0 };
//Sedgewick list'value can not exceed the Array's number.
for (wick_index = 0; Sedgewick[wick_index] >= n; wick_index++); // Note! Symbol '; ' is necessary!
for (step = Sedgewick[wick_index]; step > 0; step = Sedgewick[++wick_index])
for (position = step; position < n; position++) /* Shell Sort is enhanced Insert Sort*/
{
tmp = a[position];
for (i = position; i >= step && a[i - step] > tmp; i -= step)
a[i] = a[i - step];
a[i] = tmp;
}
return;
}
2.3.5 堆排序
堆排序的基本思路是:
(1) 将待排序序列看成一个堆,通过DownFilter( )下滤函数转换成一个大顶堆
(2) 每次从堆顶取出一个元素,放到待排序序列的最后
(3) 将剩余的n-1个元素通过下滤再变成大顶堆。重复 2 3 ,直到所有元素排序完毕
堆排序是选择排序的加强版;堆排序是不稳定的排序算法,比如序列 1 2 2
void DownFilter(int root, int n) // root mean root Node
{
int Parent, Child, x;
x = a[root]; //gain the root's value
for (Parent = root; (Parent * 2 + 1) < n; Parent = Child)
{
Child = Parent * 2 + 1;
if ( (Child != n - 1) && (a[Child] < a[Child + 1]) )
Child++;
if (x > a[Child])
break;
else
a[Parent] = a[Child];
}
a[Parent] = x;
return;
}
void heap_sort(int n)
{
int i,tmp;
for (i = ( n/2 - 1) ; i >= 0; i--) //Creat MaxHeap through DownFilter Func. 'n/2 - 1'代表最右的那棵子树
DownFilter(i, n); // 'n/2 - 1'代表最右的那棵子树,调整顺序也就是从最后一棵子树开始,从右向左逐渐调整
for (i = n - 1; i > 0; i--)
{
//Swap(A[0], A[i]);
tmp = a[0];
a[0] = a[i];
a[i] = tmp;
DownFilter(0, i);
}
}
2.3.6 合并/归并排序
合并排序/归并排序:用空间换时间,常用于内存外排序。如果你的待排序数据有1G,当使用合并排序时,你需要再申请1G的空间
(1) 基本思想是:将待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列
(2) 该算法是采用分而治之法(Divide and Conquer)的一个非常典型的应用
(3) 合并排序是稳定的排序算法;合并排序每次合并操作的时间复杂度是O(N),而二叉树的深度是log2N,所以总的时间复杂度是O(N*lgN)
void merge(ElementType b[],int Left,int Right,int RightEnd)
{
int LeftEnd, ArrayNumber, index; //index mean b's index ,ArrayNumber mean b's sum.
LeftEnd = Right - 1;
index = Left;
ArrayNumber = RightEnd - Left + 1;
while (Left <= LeftEnd && Right <= RightEnd)
{
if (a[Left] <= a[Right]) //
{
b[index++] = a[Left++];
}
else
b[index++] = a[Right++];
}
while (Left <= LeftEnd)
{
b[index++] = a[Left++];
}
while (Right <= RightEnd)
{
b[index++] = a[Right++];
}
for(int i=0;i< ArrayNumber;i++, RightEnd--) //Here need reflect
a[RightEnd] = b[RightEnd];
return ;
}
void merge_core(ElementType b[], int Left, int RightEnd)
{
int Center;
if (Left < RightEnd)
{
Center = (Left + RightEnd) / 2; //将待排序列一分为二:左 右 两边
merge_core(b, Left, Center); //递归解决左边
merge_core(b, Center + 1, RightEnd); //递归解决右边
merge(b, Left, Center + 1, RightEnd); //合并两段有序序列
}
return;
}
void merge_sort(int n)
{
ElementType *b;
b = (ElementType *)malloc(sizeof(ElementType) * n);
if (b)
{
merge_core(b, 0, n - 1);
free(b);
}
else
cout << "Memory isn't enough!" << endl;
return;
}
3. 完整编码
- 此处排序采用的是STL中的sort( )
- 如果本文帮到你,请点赞鼓励 😊 如有疑问欢迎留言
#include<cstdlib>
#include<algorithm>
#include <iostream>
using namespace std;
typedef int ElementType ;
#define max 100000
int a[max];
/*
void select_sort(int n);
void bubble_sort(int n);
void insert_sort(int n);
void shell_sort(int n);
void heap_sort(int n); //need reinforce
void merge_sort(int n); //need reinforce
*/
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
sort(a, a+n); // STL
//bubble_sort(n); //exceed time
//select_sort(n); //exceed time much than bubble_sort
//insert_sort(n);
//shell_sort(n); //improvment of insert_sort.
//heap_sort(n); //improvment of select sort.
//merge_sort(n);
cout << a[0];
for (int i = 1; i < n; i++)
cout <<" "<<a[i];
return 0;
}
四、参考
各排序算法的性能数据