交换排序-冒泡排序/快速排序

一.排序算法概述

十种排序算法可以分为两类:

  • 比较类的排序:通过比较来决定元素间的相对次序,时间复杂度不能突破O(nlogn),又被称为非线性时间比较类排序
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此又被称为线性时间非比较类排序。

tstmp_20211211234136.png 上面的文字与图来自[ 十大经典排序算法(动图演示)](www.cnblogs.com/onepixel/ar…)

1.1 时间复杂度

image.png

二.冒泡排序

2.1 思想

从前往后比较相邻元素,如果排序要求从小到大,则将大的数往后排,直到将最大的数排到最后

image.png 上图中有6个元素,需要比较5趟就结束了,每一趟比较的次数为数据长度-比较的趟数-1

2.2 算法步骤

  • 比较相邻两个元素,如果逆序则交换他们
  • 每一对相邻的元素都做这样的操作,比较完成之后最大的元素会是最后一个
  • 最好的情况是序列中元素都为正序,只需要比较n-1次,最坏的情况是序列中的元素都为逆序
  • 稳定排序
void bubbleSort(int a[],int len)
{
	int i = 0;
	int j = 0;
	int temp;

	for (i = 0; i < len - 1; i++)
	{
		for (j = 0; j < len -1 -i; j++)//排了一趟之后最后一个数为最大值,就不需要排序了
		{
			if (a[j + 1] < a[j])
			{
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
}
复制代码

代码优化,如果某一趟排序没有发生交换,则说明这趟排序已经将数据顺序排序好,不需要进行下一次排序,增加一个标志位即可

void bubbleSort2(int a[], int len)
{
	int i = 0;
	int j = 0;
	int temp;
	bool flag = false;

	for (i = 0; i < len - 1; i++)
	{
		for (j = 0; j < len - 1 - i; j++)//排了一趟之后最后一个数为最大值,就不需要排序了
		{
			if (a[j + 1] < a[j])
			{
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
				flag = true;
			}
		}

		if (!flag)//比较某趟完成之后判断flag是否为flase,表示没有发生交换
		{
			break;//如果某一次排序时没有发生交换,提前结束排序
		}
	}
}
复制代码

三.快速排序

3.1 思想

  • 从要排序的数据中选择一个基准数
  • 通过一趟排序将比基准数小的数据排左边,比基准数大的数据排右边
  • 再通过步骤2将中的两个部分进行排序,排序过程递归进行,最后整个数据变成有序序列

想可以概括为:挖坑填数 + 分治法

3.2 分治法

分治:分而治之,通过将一个复杂的问题分为多个子问题,再把子问题分割成多个相似的更小的问题,直到最后子问题可以简单的直接求解,原问题的解就是多个子问题解的合并

3.3 例子

下面通过一个例子来看一下快速排序是怎么工作的,以第一个元素为基准数,红框框住的的位置表示需要填的坑,绿色框住的元素表示已经移动赋值过的元素

image.png

  • 开始时:i和j分别指向需要排序的数组的头部和尾部,i = 0,j = 7,取基准数为左边的数,base = array[0] = array[i] = 20,这时候从后往左边开始找,即从j=7的位置开始往左边,每一次移动j都要减1,当j = 6时,符合条件,此时将array[6]的值直接填充到array[0]处,并且将i向右边移动一个位置,即i++.

伪代码为:

while (left < right && array[right] >= base)//从右往左寻找到一个比基准数小的值
{
	right--;
}

//找到了比基准数小的值,直接填充到left所在的位置
if (left < right)
{
	array[left] = array[right];
	left++; //left的下标加1,即往右移动
}
复制代码

此时图为

image.png

  • 这时array[0]的坑被array[6]给填充了,array[6]的所在的地方又重新成了一个坑,此时从左往右开始寻找一个大于base值的数填充到array[6]的地方,于是将array[1]赋值为给array[6],同时将j往左边移动一个位置,即j--

代码为:

while (left < right && array[left] < base)//从左往右寻找到一个比基准数大的值
{
	left++;
}

//找到了比基准数大的值,直接填充到rigth所在的位置
if (left < right)
{
	array[right] = array[left];
	right--; //right的下标加1,即往左移动
}
复制代码

此时图为:

image.png

  • 最后图示为

image.png 接着只需要对array[5]两边的元素进行操作即可

image.png 分治的代码为:

quickSort(array,left,left - 1);
quickSort(array,left + 1,right);
复制代码

3.4 代码为

void quickSort(int array[], int low, int high)
{
	if (low >= high)
	{
		return;
	}

	int left = low;
	int right = high;
	int base = array[left];

	while (left < right)
	{
		while (left < right && array[right] >= base)//从右往左寻找到一个比基准数小的值
		{
			right--;
		}

		//找到了比基准数小的值,直接填充到left所在的位置
		if (left < right)
		{
			array[left] = array[right];
			left++; //left的下标加1,即往右移动
		}

		while (left < right && array[left] < base)//从左往右寻找到一个比基准数大的值
		{
			left++;
		}

		//找到了比基准数大的值,直接填充到rigth所在的位置
		if (left < right)
		{
			array[right] = array[left];
			right--; //right的下标减1,即往左移动
		}

		array[left] = base;//将基准数填入最后的坑
		quickSort(array, low, left - 1);
		quickSort(array, left + 1, high);
	}
}

复制代码

猜你喜欢

转载自juejin.im/post/7040843393752121375