排序算法二----选择排序和堆排序

时间:2022-09-13 22:08:52

1、选择排序
用maxpos标记第一个元素,与该序列中的所有元素进行比较,若比maxpos大,则交换这两个元素的位置,否则,不改变这个元素的位置,直至将maxpos移置最后一个元素的位置处。然后比较除了最后一个元素之外的前n-1个元素,找次大原素的位置,重复进行。
优化:最大的元素放到最后的位置,最小的元素放在起始位置。时间复杂度O(n^2),空间复杂度O(1)。
选择排序达到最优即标记位减少,减少重复性的元素
适用于元素比较小,且重复性元素比较多的场景,不稳定,存在跨区间交换
法一:将最大的元素放在最后一个位置处,将最小元素放在起始位置处。(设置一个标记位maxpos表示最大元素,或者选择一个最小元素)需注意最小的元素恰好在最后一个元素的位置处和最大的元素恰好在第一个元素的位置处的情况。
时间复杂度:一次只能选择一个最大的,然后选择次大的,有多少个元素选多少次,所以时间复杂度为O(n^2)
空间复杂度:O(1)没有借助辅助空间
跨区间的插入,跨区间的交换都是不稳定的。
缺陷:不稳定。进行了一些重复性的工作。
改进:将比较过的元素记录下来。堆排序,优化选择排序
希尔排序适用于数据元素很大,不接近于有序的数据
void Swap(int *px, int *py)
{
	int tmp = 0;
	assert(px);
	assert(py);
	tmp = *px;
	*px = *py;
	*py = tmp;
}
void SelectSort(int *array, int size)
{
	int j = 0;
	int maxpos = 0;//最大的标记位,设置为第一个元素
 	int i = 0;
	for (i = 0; i < size-1;++i)//比较次数n-1次
	{
		//找最大元素所在的位置(处理单个数据)
		for (j = 1; j < size-i; ++j)
		{
			if (array[j]>array[maxpos])
				maxpos = j;
		}
		//最大元素已经在自己的位置上了,则不需要交换
		if (maxpos != size - 1)
			Swap(&array[maxpos], &array[size - 1]);//将最大元素的位置放在最后的位置上
	}
}


法二:优化:找两个标记元素
需要注意maxpos在最前位置上,或者最minpos在最的后位置上,交换后需要和标记位一起交换位置
优化后的时间复杂度为
要找n/2次(一次排序好两个数字)O((n/2)^2)即O(n^2)
void SelectSortOp(int *array, int size)
{
	//给两个标记位
	int begin = 0, end = size-1;
	while (begin < end){
		int index = begin + 1;
		//默认begin即为最大元素开始的位置,也为最小元素开始的位置
		int maxpos = begin;
		int minpos = begin;
		while (index < end)//begin后面有元素
		{
			if (array[index]>array[maxpos])
				maxpos = index;
			if (array[index] < array[minpos])
				minpos = index;
		}
		//检查最大最小元素是否在应该在的位置上,若是不在则交换位置
		if (maxpos != end)
			Swap(&array[maxpos], &array[end]);
		//maxpos在最前位置上,或者最minpos在最的后位置上
		if (minpos == end)
			minpos = maxpos;//交换后需要和标记位一起交换位置,即更新minpos的值
		if (minpos != begin)
			Swap(&array[minpos], &array[begin]);
		begin++;
		end--;
	}
}

二、堆排序
采用(升序)大堆排序,选择完全二叉树
时间复杂度:调整一个节点,最坏的情况下,为一颗树的高度,lgn;上面需要调整n/2次(调整的非叶子节点:在任何一棵完全二叉树里面,度为0的节点比度为1的节点多一个)
下面需要调整n次,所以O(1.5lgN)即O(lgn)。
空间复杂度:没有借助辅助空间:O(1)
稳定性:不稳定(隔着元素交换)
排序算法二----选择排序和堆排序
//堆的调整,向下调整
void HeapAdjust(int *array, int parent, int size)
{
	//找到孩子节点(默认左孩子大于右孩子)
	int child = parent * 2 + 1;
	while (child<size)//child<size,则左孩子存在
	{
		if (child+1<size&&array[child + 1] > array[child])//child+1<size保证右孩子存在
			child += 1;
		if (array[parent] < array[child])
		{
			Swap(&array[parent], &array[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else//双亲节点大于孩子节点,则不用处理,直接返回
			return;
	}
}
void HeapSort(int *array, int size)
{
	//创建堆(升序---大堆;降序---小堆)
	//从倒数第一个非叶子节点开始创建
	int end = size - 1;
	int root = ((size - 2) >> 1);
	for (; root >=0; --root)
		HeapAdjust(array, root, size);
		//排序(堆的删除操作)
	while (end)
	{
		//用堆顶元素与最后一个元素交换
		Swap(&array[0], &array[end]);
		//排好序
		HeapAdjust(array, 0, end);
		--end;
	}
}