直接选择排序和堆排序都属于选择排序

ps:仍以升序为例!

一、直接选择排序

直接选择排序的思想:

选择排序就是通过n-i次关键字的比较,每次从n-i+1个记录中找出最小的关键字,将其与第i个位置的数据交换。


void SelectSort(int* a, size_t size)
{
	assert(a);

	int max = 0;
	for (size_t i=0; i<size; ++i)
	{
		max = i;
		for (size_t j=i+1; j<size; ++j)
		{
			if (a[j] > a[max])
			{
				max = j;
			}
		}
		if (i != max)
			swap(a[i],a[max]);
	}
}

直接选择排序的优化:

上边简单的选择排序是每次选出一个数来交换,优化后的做法是从数组的两边开始遍历,每次选出一个最大的数一个最小的数来进行交换

void SelectSort(int* a,int size)
{
	assert(a);

	int left = 0;
	int right = size-1;
	while(left < right)
	{
		int min = left;
		int max = left;
		//在[left,right]区间内找最大最小
		for (int i=left; i<=right; ++i)
		{
			if (a[i] < a[min])
				min = i;		//找到[left,right]内最小值的下标
			if (a[i] > a[max])
				max = i;		//找到[left,right]内最大值的下标
		}
		swap(a[left],a[min]);	//将最小值放在区间最左边
		if (left == max)	
		{
			max = min;	//更换max的位置
		}
		swap(a[right],a[max]);	//将最大值放在区间最右边
		++left;
		--right;
	}
}
直接选择排序的 时间复杂度分析

最好的情况下,时间复杂度为O(0),最坏的情况是当数组逆序或接近于逆序时,此时共需要比较n-1 + n-2 + ......,其结果也是O(N^2)


二、堆排序

堆排序是利用建堆来进行排序的一种算法。

排序的思想:

(假若要得到的数组排序以后是升序的)先建大堆,每次取出堆顶的数据与堆数组的末尾元素进行交换,然后再接着向下调整使其重新成为一个大堆,然后再重复上面的工作,这样就可以得到一个有序的数组。需要提醒的是:每次交换完数据以后,只需要size-1个数据进行调整,也就是已经在堆数组末尾的元素不进堆,这样才能得到结果!

之前写了堆排序的代码,在这篇文章里:《堆排序算法》

堆排序的时间复杂度:

我们比较的时候是从倒数第一个非叶子结点开始向前遍历的,并将其与孩子结点进行比较和交换,对于每一个非叶子节点来说,我们最多需要比较和交换两次,因此建堆时的时间复杂度是O(N)

进行排序时,每次取堆顶数据时的实践复杂度为O(logN)(longN是以2为底N的对数),总共需要取N-1次堆顶数据进行重建堆,因此时间复杂度为O(N*logN)

因此,堆排序的时间复杂度为O(N*logN)


三、冒泡排序

冒泡排序是我们开始编程接触的第一个排序算法,这里我就不详细写了,之前写了一篇博客:《冒泡排序及其优化》,这里边有一般的冒泡排序,以及优化以后的。

冒泡排序的时间复杂度

最好的情况是数组数据接近于有序或其本身就是有序的,时间复杂度为O(N),最坏的情况是逆序,冒泡排序需要比较和移动的次数是1+2+3+.....+(n-1),其时间复杂度为O(N^2)。因此,冒泡排序的时间复杂度为O(N^2)


冒泡排序与插入排序的比较

冒泡排序虽然简单,其时间复杂度与插入排序也是一样的,这样看不出什么差别,但当数据比较多时,冒泡排序就要比插入排序差一些,比如当有N个(若N=1万)数据:1,2,3....,9,7......这组数据是有序的,利用插入排序只需要多比较2次,但如果用冒泡排序,首先会遍历一遍(N),接着交换完成后,需要再遍历一遍(N)检查是否需要有数据继续交换。

 


本文转载:CSDN博客