选择排序、堆排序、冒泡排序

目录

选择排序

堆排序

冒泡排序


选择排序

 选择排序是一种非常简单的排序,多次遍历数组,每次遍历时选出最小的数放到数组的头上就行了(每次选出的最小数不参与下一次遍历)。

我们这里做个优化,每次遍历的时候选出最小和最大的数,分别放头和尾。这样每次选可以选出两个数,使效率略有提升。

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void Selectsort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		//选出小的数放begin位置
		//选出大的数放end位置
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] > a[maxi])
				maxi = i;
			if (a[i] < a[mini])
				mini = i;
		}
		swap(&a[begin], &a[mini]);
		//修正maxi
		if (maxi == begin)
		{
			maxi = mini;
		}
		swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

int main()
{
	int a[] = { 100,1,4,65,11,15,46,23,60,12,9 };
	int sz = sizeof(a) / sizeof(a[0]);
	Selectsort(a, sz);
	for (int i = 0; i < sz; ++i)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

 中间选择的过程都很简单,注意修正maxi这一块,如果最大的数一开始         就在头上,不进行修正会出现错误。  通过调试来看:

 a[0] 是最大的数100,没有数能和他交换,所以begin和mini交换后(注意此时a[0] 和 a[1]已经交换,100不在头上了),要交换end和maxi,交换到最后去的就不是最大的100而是其他数了,所以end和maxi在交换前,加一句修正,如果一开始头上的数是最大的,那么begin和mini交换完后将mini的位置给maxi就可以了。

选择排序可以说是最差的排序之一了,时间复杂度为O(N^2),并且无论最好情况还是最坏情况都是O(N^2),也就是说不管有序无序,都那么慢,比起上节讲的插入排序要差,插排起码序列部分有序的情况下效率还可以。

堆排序

实现堆排序之前需要先写一个堆出来吗?————没有必要,如果需要先实现一个堆就太过麻烦了,而且这种方式空间复杂度就变成了O(N),原本只需要在数组中选数,现在将数据传入堆里,需要开辟额外空间。所以我们直接将要排序的数组建堆,再选数。这里的原数组基础上建堆就是模拟建堆的过程

这里有两种思路:


一、向上调整模拟建堆

void HeapSortUp(int* a, int n)
{
	for (int i = 1; i < n; ++i)
	{
		AdjustUp(a, i);
	}
}
 
int main()
{
	int a[] = { 6,2,5,4,13,7,9,16,56 };
	int n = sizeof(a) / sizeof(a[0]);
	HeapSortUp(a, n);
	int i = 0;
	for (i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
    return 0;
}

 这种建堆方式时间复杂度是O(N*logN)

不是很推荐,我们还有更好的方法。

二、向下调整模拟建堆

void AdjustDown(HPDataType* a, int n, int parent)
{
	int minchild = parent * 2 + 1;
	while (minchild < n)
	{
		if (minchild + 1 < n && a[minchild] < a[minchild + 1])
		{
			minchild++;
		}
		if (a[minchild] > a[parent])
		{
			swap(&a[minchild], &a[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
			break;
	}
}

void HeapSortDown(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
}
 
int main()
{
	int a[] = { 6,2,5,4,13,7,9,16,56 };
	int n = sizeof(a) / sizeof(a[0]);
	int i = 0;
    HeapSortDown(a, n);
	for (i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
 
	return 0;
}

这种方式时间复杂度是O(N)。

所以建堆的时候一般选用向下调整建堆的方法。

证明时间复杂度:

向下调整:

 向上调整:

 与向下调整一样,这里就不算了,其实很容易想,二叉树越往下节点越多,最后一层就占了一半,向下调整最后一层调整h-1次,比向上调整多了太多,因此时间复杂度大。

堆排序选数

完成模拟建堆后,就是进行排序选数了,这里有个非常关键的点:如果要排成升序,是建大堆还是建小堆?降序又如何呢?

这是个非常容易出错的点,可能大家会觉得要排升序,小的数在前面,那就建小堆,降序大的数在前面,那就建大堆,其实恰恰相反,升序是建大堆,降序是建小堆。

我来证明一下:以升序为例,假设数组是这样的:2,4,5,6,13,7,9,16,56
 

模拟建成小堆

这时选出次小的数4,堆的形态就发生了变化: 

本来4和5是兄弟,现在成了父子,关系全乱了,此时只能重新建堆选数,那么堆排序就没有任何意义了。因此得反过来建大堆。

模拟建成大堆: 

这时将堆顶的数56 和最后一个数2 交换,然后不算最后一个数56,向下调整选出次大的(在堆顶),再重复上面操作,就能排出升序了,实际上就是倒着将数组排出来,降序也一样。

 代码如下:

typedef int HPDataType;
 
void AdjustDown(HPDataType* a, int n, int parent)
{
	int minchild = parent * 2 + 1;
	while (minchild < n)
	{
		if (minchild + 1 < n && a[minchild] < a[minchild + 1])
		{
			minchild++;
		}
		if (a[minchild] > a[parent])
		{
			swap(&a[minchild], &a[parent]);
			parent = minchild;
			minchild = parent * 2 + 1;
		}
		else
			break;
	}
}
 
void HeapSortDown(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
	int i = 1;
	while (i < n)
	{
		swap(&a[0], &a[n - i]);
		AdjustDown(a, n-i, 0);
		i++;
	}
}
 
 
int main()
{
	int a[] = { 6,2,5,4,13,7,9,16,56 };
	int n = sizeof(a) / sizeof(a[0]);
	int i = 0;
    HeapSortDown(a, n);
	for (i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
 
	return 0;
}

 降序也一样,改下建小堆就可以了。

冒泡排序

冒泡排序也非常简单,大家接触应该也非常多,直接上代码(略加点优化)

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void Bubblesort(int* a, int n)
{
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < n - j; ++i)
		{
			if (a[i - 1] > a[i])
			{
				swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

int main()
{
	int a[] = { 100,1,4,65,11,15,46,23,60,12,9 };
	int sz = sizeof(a) / sizeof(a[0]);
	Bubblesort(a, sz);
	for (int i = 0; i < sz; ++i)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值