冒泡排序、选择排序、插入排序详解

目录

冒泡排序

选择排序

插入排序

冒泡排序

冒泡排序是依次比较数组中相邻的两个数,将两个数中较大的数向后移动,循环一次下来,整个数组中最大的是数一定在最后面,依次循环,第二次循环,数组中次最大数一定在次最后面,如此反复...

图解如下:

 代码如下:

未优化版本:

void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)//每趟都会只会把一个最大的数放到后面,因此需要循环n次
	{
		for (int j = 0; j < n - i - 1; j++)
		{    
            //两两比较,大的后移(交换到右边),以此类推,直到最后一个
			if (a[j] > a[j+1])
			{
				swap(a[j], a[j+1]);
			}
		}
	}
}

优化版本:

我们想一下,按照上面那个方法,如果只排序了一趟,数组里的元素就已经全部有序了,但是他还得继续循环进行,直到把最后一趟循环完毕.这样是不是太浪费时间了.

我们有没有办法解决这个问题呢?

当然有,我们可以做一个标记,如果发生了交换操作,则将标记更改;如果没有,则直接循环结束.排序完毕.

具体代码如下:

void BubbleSort(int* a, int n)
{
	int exchange = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j+1])
			{
				swap(a[j], a[j+1]);
				exchange = 1;
			}
		}
		if (exchange == 0)//如果exchange=0,如果此时没有发生交换,既已经排序好了,退出循环
		{
			break;
		}
	}
}

分析:

最坏是每次循环都执行,因此时间复杂度为O(N^2).

没有开辟额外的空间,因此空间复杂度为O(1).

选择排序

每次从待排序的元素中选出最小(或最大)的一个元素,然后和数组左端的元素进行交换,直到全部排列完成.

图解如下:

 未优化版本:

void SelectSort(int* a, int n)
{
	int i = 0, j = 0;
	int min = 0;
	for (int i = 0; i < n; i++)
	{
		min = i;//先假设最小值的下标为i
		for (int j = i + 1 ; j < n; j++)
		{
			if (a[j] < a[min])//若数组中有的值比最小值还小,则将min下标替换为此下标
			{
				min = j;
			}
		}
		if (min != i)//若不相等,说明此时min下标发生了变化,交换此下标的值和 下标i位置的值;若相等,则没有发生变化,不必交换.
		{
			swap(a[i], a[min]);
		}
	}
}

但是我们转念一想,既然可以每次选出最小值,为什么不同时选出最大值放在数组的右端呢,这样效率不就是原来的2倍了吗?说做就做,我们试着实现它.但同时有一些问题,我们后面说.

优化版本:

void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = begin;//先将最小值,最大值下标初始化为begin.
		for (int i = begin; i <= end; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;//如果有的值比最小值小,则将最小值下标=那个值的下标
			}
			if (a[i] > a[maxi])
			{
				maxi = i;//如果有的值比最大值大,则将最大值下标=那个值的下标
			}
		}
		swap(a[mini], a[begin]);//交换最小值和未排序的最左端的值
		if (begin == maxi)//如果begin的位置恰好是最大值的位置,由于上一条语句mini和begin已经交换,说明此时最大值下标已经不是begin,而是和begin交换后的mini位置,所以将maxi = mini.
		{
			maxi = mini;
		}
		swap(a[maxi], a[end]);//一切完成后,交换最大值和未排序的最右端的下标
		begin++;
		end--;
	}
}

这是选择排序的算法.

下面来看插入排序

插入排序

先来看直接插入排序.

我们平常玩扑克牌的时候,每次摸牌,我们总会把摸到的牌插入到合适的位置,等摸完牌以后,我们的牌的顺序也就好了.

插入排序也是这个道理.

原理:当插入第i个元素时,前面的i-1个元素已经排序完成.

此时将arr[i]分别与arr[i-1],arr[i-2]...比较,每比较一次,将被比较的那个元素向后移动一位,最后找到插入位置

找到插入位置之后,将这个位置元素后移,留出位置,将元素插入.

图解如下:

思路是:

我们不知道数组是否有序,所以通过控制下标的方式进行排序

开始让end的下标为0,每次保存end+1的值x,让x与x前面的值进行比较,判断x的值是否小于某个值,如果小于则end--,大于的话说明此时这个位置之后正是x的插入位置.将x插入即可.

代码如下:

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int x = a[end + 1];//要插入的值保存到x
		while (end >= 0)//若end<0,则说明被插入的元素在第一位,直接a[end+1]即a[0] = x即可
		{
			if (x < a[end])//x小于a[end],则继续向前查找,直到大于
			{
				a[end + 1] = a[end];//如果不符合条件,则把这个位置向后移.
				end--;
			}
			else
			{
				break;//x >= a[end],直接break,将x插入到这个位置end+1这个位置,因为上一次end--
			}
		}
		a[end + 1] = x;
	}
}

最差情况,每次插入要向前移动n-1个元素,所以时间复杂度为O(N)=1+2+3+4+....n-1 = n(n-1)/2=O(N^2)

没有创建额外的空间,所以时间复杂度为O(1)

这就是冒泡排序、选择排序、和插入排序的全部内容了.

下一节我们将讲解希尔排序和归并排序.

排序重要的是看懂思想,理解代码,就会变得很简单啦.

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

次元工程师!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值