常用排序算法(冒泡,选择,插入,快排)

目录

一.冒泡排序

1.基本思想

2.步骤

3.动图过程展示

4.代码示例(效率提高之后的)

二.选择排序

1.基本思想

2.步骤

3.动图过程展示

4.代码示例(效率提高之后的)

三.插入排序

1.基本思想

2.动图过程展示

3.代码示例

四.快速排序

1.基本思想

2.步骤

3.动图过程展示

4.代码示例

五.时间复杂度和空间复杂度对比


一.冒泡排序

1.基本思想

通过对待排序序列从前向后(从下标较小的元素开始),依次对相邻两个元素的值进行两两比较,若发现逆序则交换使值较大的元素逐渐从前移向后部,就如果水底下的气泡一样逐渐向上冒。

2.步骤

<1>比较相邻的元素。如果第一个比第二个大,就交换他们两个。
<2>每趟从第一对相邻元素开始,对每一对相邻元素作同样的工作,直到最后一对。
<3>针对所有的元素重复以上的步骤,除了已排序过的元素(每趟排序后的最后一个元素),直到没有任何一对数字需要比较。

3.动图过程展示

4.代码示例(效率提高之后的)

此代码效率比较高,设置了flag变量。如果flag不再变化,就代表数组已经有序,后续无须再进行排序了。

#include <stdio.h>
void bubble_sort(int arr[], int sz)
{
	for (int i=0;i<sz-1;i++)
	{
		int flag = 1;//假设这一趟已经有序了。
		for (int j=0;j<sz-1-i;j++)
		{
			if (arr[j] > arr[j + 1])
			{
				flag = 0;//发生交换就说明,无序。
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
		if (flag == 1)//这一趟没交换就说明已经有序,后续无需排序了。
		{
			break;
		}
	}
}
int main()
{
	//实现一个对整型数组的冒泡排序
	//冒泡排序的核心思想:两两相邻的元素进行比较
	int arr[] = { 2,45,12,6,24,6,9,13 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

二.选择排序

1.基本思想

它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,继续放在起始位置知道未排序元素个数为0。

2.步骤

<1>首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

<2>再从剩余未排序元素中继续寻找最小(大)元素,然后放到未排序序列的起始位置。

<3>重复第二步,直到所有元素均排序完毕。

3.动图过程展示

4.代码示例(效率提高之后的)

实际上,我们可以一趟选出两个值,一个最大值,一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。

#include <stdio.h>
void swap(int* a, int* b)//形参是指针变量
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
void Select_sort(int arr[], int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left < right)
	{
		int maxi = left;//保存最大值的下标
		int mini = left;//保存最小值的下标
		for (int i = left; i <= right; i++)
		{
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}
		if (mini == right && maxi == left) {
			swap(&arr[left], &arr[right]);
		}
		else if (mini == right) {
			swap(&arr[mini], &arr[left]);//将最小值放在开头
			swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
		}
		else if (maxi == left) {
			swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
			swap(&arr[mini], &arr[left]);//将最小值放在开头
		}
		else {
			swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部
			swap(&arr[mini], &arr[left]);//将最小值放在开头
		}
		left= left+1;
		right= right-1;
	}
}
int main()
{
	int arr[] = { 47,2,45,12,6,24,6,9,13,33 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Select_sort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;

易错点:

代码段的3对if else是必不可少的,否则结果会出错下面我来解释一下。分为四种情况。

情况一:假设每一趟排序中找到的最大值和最小值的下标既不在序列开头,也不在序列末尾,而是处于两者之间,这个时候我们只需要分别把最小值,最大值和序列开头,序列结尾交换就OK。

情况二:假如在一趟排序中找到的最大值下标正好在序列开头,而最小值的下标介于开头和结尾之间,此时我们就不能简单按照情况一那样交换。比如我们代码中的第一趟排序,{47,45,12,6,24,6,9,13,33 };最大值下标在开头,假设按照情况一,我们先把6(最小值)和47(最大值)交换了,然而最大值下标还是转向开头位置,此时开头位置是6(最小值)而不是47(最大值),此时顺序就不会按照我们所要实现的那样进行。所以,我们不妨这样做:先交换最大值,后交换最小值。

情况三:假如在一趟排序中找到的最小值下标正好在序列结尾,而最小值的下标介于开头和结尾之间。此时和情况二大体情况类似,类比,我们可以先交换最小值,再交换最大值。

情况四:假设在一趟排序中找到的最大值恰好在开头位置,找到的最小值恰好在末尾位置,此时我们只需要交换最小值或者最大值一次就好。

这三对if else可以进一步代码优化,如下:

swap(&arr[mini], &arr[left]);//将最小值放在开头
if (left == maxi)//防止最大的数在开头位置被换走
{
   maxi = mini;
}
swap(&arr[maxi], &arr[right]);//将最大值放在序列尾部

三.插入排序

1.基本思想

将整个数组a分为有序和无序的两个部分。前者在左边,后者在右边。开始有序的部分只有a[0] , 其余都属于无序的部分。每次取出无序部分的第一个(最左边)元素,把它加入有序部分。假设插入合适的位置p,则原p位置及其后面的有序部分元素都向右移动一个位置,有序的部分即增加了一个元素。一直做下去,直到无序的部分没有元素。

2.动图过程展示

3.代码示例

#include <stdio.h>
void Insert_sort(int arr[], int sz)
{
	for (int i = 0; i < sz-1; i++)
	{
		int end = i;//记录有序序列最后一个元素的下标
		int temp = arr[end + 1];//待插入的元素
		while (end >= 0)//单趟排序
		{
			if (temp < arr[end])//有序表元素比插入的数大
			{
				arr[end + 1] = arr[end];//使大数往后移
				end--;//从后往前检查比较有序表元素与待插入元素值的大小
			}
			else//比插入的数小,跳出循环
				break;
		}
		//两种情况:(1)待插入元素比当前有序序列中的所有元素都小
		//(2)待插入元素找到插入位置(break跳出循环至此)
		arr[end + 1] = temp;
	}
}
int main()
{
	int arr[] = { 47,2,45,12,6,24,6,9,13,57,33 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Insert_sort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

四.快速排序

1.基本思想

选取一个基准,一趟排序确定两个区间,一个区间全部比基准值小,另一个区间全部比基准值大,接着再选取一个基准值来进行排序,以此类推,最后得到一个有序的数列。

2.步骤

<1>首先设定一个分界值,通过该分界值将数组分成左右两部分。

<2>将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。 

<3>然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。 

<4>重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

3.动图过程展示

4.代码示例

#include <stdio.h>
void swap(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
void Quick_sort(int arr[],int left,int right)
{
	if (left >= right)
	{
		return;
	}
	int i=left, j=right, base=arr[left], temp;
	while (i < j)
	{
		while (arr[j] >= base && i < j)
		{
			j--;
		}
		while (arr[i] <= base && i < j)
		{
			i++;
		}
		if (i < j)
		{
			swap(&arr[i], &arr[j]);
		}
	}
	arr[left] = arr[i];//基准数回归
	arr[i] = base;
	Quick_sort(arr,left,i-1);//递归左边
	Quick_sort(arr,j+1 ,right );//递归右边
}
int main()
{
	int arr[] = { 47,2,45,12,6,24,6,9,13,57,33 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Quick_sort(arr, 0,sz-1);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

五.时间复杂度和空间复杂度对比

完结撒花

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值