各种排序算法

作者在洛谷刷题时复习排序算法,介绍了排序算法的稳定性、内外排序概念。重点讲解插入排序,包括直接插入、折半插入、希尔排序;还介绍交换排序,如冒泡排序和快速排序,快速排序是冒泡排序的改进,是内部排序中平均性能最优的算法。

ONE  基本概念

洛谷刷题,写到排序,于是把排序复习了一遍。

排序算法预览:https://www.edrawsoft.cn/viewer/public/s/371d2752967218

排序算法的稳定性:

       如果待排序表中有两个元素Ri,Rj,其对应关键字keyi=keyj,且在排序前Ri在Rj前面,如果使用某一排序算法排序后,Ri仍然在Rj的前面,则称这个排序算法是稳定的,否则称排序算法是不稳定的。需要注意,算法是否稳定不能衡量一个算法的优劣,它主要是对算法的性质进行描述。

内部排序:指的是排序期间元素全部存放在内存中的排序。

外部排序:指在排序期间元素无法全部同时存放于内存中,必须在排序过程中根据需求不断在内外存之间移动的排序。

内部排序算法的性能取决于算法的时间复杂度和空间复杂度,而时间复杂度一般由比较和移动的次数来决定。

 

TWO 插入排序

  • 直接插入排序

       直接插入排序适用于顺序存储和链式存储的线性表。当表为链式存储时,可以从前往后查找指定元素位置。

       注意:大部分排序算法都仅适用于顺序存储的线性表。

//直接插入排序
void InsertSort(vector<int> &buf)			
{
	int i,j;
	int n = buf.size() - 1;
	for (i = 2; i <= n; i++)		//依次将buf[2]到buf[n]插入到前面的已排序序列
	{
		if (buf[i] > buf[i - 1])	//若buf[i]小于其前驱,需将buf[i]插入有序表
		{
			buf[0] = buf[i];		//存入buf[0],(注意,待排序列下标是1 ~ n
			for (j = i - 1; buf[0] > buf[j]; --j)//从后往前查找待插入位置
			{
				buf[j + 1] = buf[j];			//向后挪位
			}
			buf[j + 1] = buf[0];				//复制到插入位置
		}
	}
}
  • 折半插入排序

       因为直接插入排序中有两个操作,查找待插入元素位置和为该元素腾出空间。而查找待插入元素位置的时候查找的表为有序表,因此很容易想到一种优化方式(折半查找)。

       注意:折半查找仅适用于有序顺序表,因此折半插入排序只能用于顺序存储的线性表。

//折半插入排序
void InsertSorts(vector<int> &buf)
{
	int i, j, low, high, mid;
	int n = buf.size() - 1;
	for (i = 2; i <= n; i++)		//依次将buf[2]到buf[n]插入到前面的已排序序列
	{
		buf[0] = buf[i];			//将buf[i]暂存入buf[0]
		low = 1; high = i - 1;		//设置折半查找范围
		while (low <= high)			//折半查找
		{
			mid = (low + high) / 2;	//取中间点
			if (buf[mid] > buf[0])
			{
				high = mid - 1;		//查找左半子表
			}
			else
			{
				low = mid + 1;		//查找右半子表
			}
		}
		for (j = i - 1; j >= high + 1; --j)
		{
		        buf[j + 1] = buf[j];			//向后挪位
		}
		buf[j + 1] = buf[0];				//插入
	}
}
  • 希尔排序

希尔排序又称为缩小增量排序,基本思想是将待排序表分割成若干个子表,分别进行直接插入排序,当整个表中元素已经基本有序时,再对全体记录进行一次直接插入排序。

       希尔排序的时间复杂度受到步长变化方式的影响,当前方式n在一定范围内时约为O(n^1.3),最坏情况下是O(n^2)。

//希尔排序
void ShellSort(vector<int> &buf)				
{
	int i, j;
	int n = buf.size() - 1;
	for (int dk = n / 2; dk >= 1; dk = dk / 2)    //步长变化
	{
		for (i = dk + 1; i <= n; i++)
		{
			if (buf[i] > buf[i - dk])    //将buf[i]插入有序增量子表
			{
				buf[0] = buf[i];     //暂存于buf[0]
				for (j = i - dk; j > 0 && buf[0] > buf[j]; j -= dk)
				{
					buf[j + dk] = buf[j];    //后移元素,查找插入位置
				}
				buf[j + dk] = buf[0];            //插入
			}

		}
	}
}

 

THREE 交换排序

  • 冒泡排序

       就是冒泡,字面意思(@_@)

//冒泡排序
void BubbleSort(vector<int> &buf)				
{
	int n = buf.size();
	for (int i = 0; i < n; i++)
	{
		bool flag = false;				//表示本次冒泡是否发生了交换
		for (int j = buf.size() - 1; j > i; j--)	//一次冒泡过程
		{
			if (buf[j - 1] < buf[j])		//若为逆序
			{
				swap(buf[j - 1], buf[j]);	//交换
				flag = true;			//表示发生了交换
			}
		}
		if (flag = false)	//若本次冒泡没有发生交换,则说明表已经有序
		{
			return;
		}
	}
}
  • 快速排序

       快速排序是对冒泡排序的改进,基本思想是分治法。

       快速排序是所有内部排序算法中平均性能最优的排序算法。

       在待排序列中任选一个元素pivot作为基准,通过一次排序将待排序列分成两部分,使得前一部分所有元素均小于pivot,另一部分所有元素大于pivot。此时pivot已经到达最终位置。这个过程称为一趟快速排序。接下来分别递归地对两个子表重复上述过程,直至每一部分内只有一个元素或空为止。此时所有元素均在其最终位置。

       首先写划分算法。当前比较常用的划分操作为:每次选取当前表的第一个元素作为基准值pivot。代码如下:

//快排--划分操作
int Partition(vector<int> &buf, int lows, int highs)
{
	int pivot = buf[lows];		//将当前表中第一个元素设为基准值,对表进行划分
	while (lows < highs)		//循环跳出条件,当所有元素遍历一遍时lows等于highs
	{
		while (lows < highs&&buf[highs] >= pivot)
		{
			--highs;
		}
		buf[lows] = buf[highs];		//将比基准值小的元素移到左端
		while (lows < highs&&buf[lows] <= pivot)
		{
			++lows;
		}
		buf[highs] = buf[lows];		//将比基准值大的元素移到右端
	}
	buf[lows] = pivot;			//基准值存放到最终位置
	return lows;				//返回存放基准值的位置作为新表划分位置
}

       接下来递归地调用快速排序算法进行排序:

//快速排序
void QuickSort(vector<int> &buf,int low,int high)
{
	if (low < high)		//若low>=high说明此表只有一个元素或空
	{
		int pivotpos = Partition(buf, low, high);	//划分位置
		QuickSort(buf, low, pivotpos - 1);		//将划分后的左表进入递归
		QuickSort(buf, pivotpos + 1, high);		//将划分后的右表进入递归
	}
}

 

FORE 

       断电了,明天继续

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值