PTA 排序 思路分析及代码解析

一、前导

1. 需要掌握的知识

  1. 熟悉各种常见排序算法

2. 题目信息

  1. 题目来源:PTA / 拼题A
  2. 题目地址:排序

二、解题思路分析

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. 思路分析

  1. 依据各种排序算法的思路实现,通过实际编码进一步熟悉各种排序算法
  2. 简便方法:调用sort函数。sort函数包含在头文件为 algorithm 的c++标准库中,sort( )的时间复杂度为nlog2n,比冒泡等排序算法的效率要高

三、具体实现

1. 一点感想

  1. 想起来容易、实现起来简单的排序算法,效率都不会太高;效率高的排序算法,不太好想、也比较难实现。总体而言,还是C++ STL好用

2. 代码框架(重点)

2.1 采用的数据结构

  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. 完整编码

  1. 此处排序采用的是STL中的sort( )
  2. 如果本文帮到你,请点赞鼓励 😊 如有疑问欢迎留言
#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;
}

四、参考

  1. 浙江大学 陈越、何钦铭老师主讲的数据结构

各排序算法的性能数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值