五分钟掌握选择排序与其改良排序(希尔排序),内含详细手工过程图解与代码注释

1、直接选择排序

·手工过程:

例:
()9 5 1 4 6 7 8 5 3 0 9
(0)9 5 1 4 6 7 8 5 3 9
(0 1)9 5 4 6 7 8 5 3 9
(0 1 3)9 5 4 6 7 8 5 9
(0 1 3 4)9 5 6 7 8 5 9
(0 1 3 4 5)9 6 7 8 5 9
(0 1 3 4 5 5)9 6 7 8 9
(0 1 3 4 5 5 6)9 7 8 9
(0 1 3 4 5 5 6 7)9 8 9
(0 1 3 4 5 5 6 7 8)9 9
(0 1 3 4 5 5 6 7 8 9)9
(0 1 3 4 5 5 6 7 8 9 9)
时间复杂度:总共需要选择n-1次,每次都要需要固定的比较操作和可能的交换操作。
总比较次数:n-1+n-2+n-3+…+2+1 = O(n^2/2)
最优情况:完全顺序,整个过程只有比较,没有交换,比较次数固定。
最差情况:完全逆序

void directSelectSort(int* arr, int count) {
	int i;//待排数据下标
	int j;//待排数据后的数据下标,从i+1开始
	int tmp;
	int minIndex;

	for(i = 0; i < count; i++) {
		minIndex = i;
		for(j = i + 1; j < count; j++) {
			if(arr[j] < arr[minIndex]) {
				minIndex = j; //更新本轮持有的最小数据的下标
			}
		}
		if(i != minIndex) {//一轮比较完成,判断是否需要交换
			tmp = arr[minIndex];
			arr[minIndex] = arr[i];
			arr[i] = tmp;
		}
	}
}

·实例验证

void main() {
	int arr[11] = {9, 5, 1, 4, 6, 7, 8, 5, 3, 0, 9,};
	printf("排序前:");
	show(arr, 11);
	directSelectSort(arr, 11);
	printf("排序后:");
	show(arr, 11);
}

运行结果:
在这里插入图片描述

2、改良的直接选择排序——堆排序

堆的本质是完全二叉树,满足以下要求:
完全二叉树的任意一节点(非叶子节点)值都比左右孩子的值大。

·手工过程与详细图解

4 8 2 9 1 0 5 7 6 3

在这里插入图片描述
首先,从最后一个非叶子节点依次往前,作为根节点,进行所在二叉树的节点的比较与交换。若存在交换,则,先交换数据,再将交换后孩子节点作为根节点,再进行二叉树节点的比较与交换。直到不存在交换或,节点为叶子节点即停止。

在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

最后我们发现,本轮将树中的最大值交换到了该完全二叉树的根(下标为0)。于是,将该最大值与最后一个叶子接点交换位置并剔除;再从当前二叉树的根节点出发执行一次比较与交换操作。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
第二轮将8剔出,以此类推至剔除至剩1个节点,堆排序完成。

·算法介绍与具体代码实现

针对“从当前根节点,进行所在二叉树的节点的比较与交换。若存在交换,则,先交换数据,再将交换后孩子节点作为当前根节点,再进行该根节点所在二叉树的比较与交换。直到不存在交换或,当前根节点为叶子节点即停止”这一操作,给出函数adjustOneRoot(int * arr, int count, int root)。
其中用到的完全二叉树算法解释:
左上角为该节点在数据中的下标(左上角为该节点在数组中的下标)
整个节点数为count,最后一个非叶子节点下标为lastIndex,根节点下标为root,左右孩子下标分别为leftChild、rightChild,有:
lastIndex = count/2 - 1;
leftChild = 2
root + 1;
rightChild = 2root + 2;若rightChild >= count,则不存在右孩子;
判断该节点为非叶子节点的条件:root < count/2成立则满足;

//针对单个根节点进行比较与交换
void adjustOneRoot(int * arr, int count, int root) {
int leftChildIndex;
int rightChildIndex;
int maxChildIndex;
int tmp;

	while(root < count/2) {
		leftChildIndex = 2*root + 1;
		rightChildIndex = 2*root + 2;
		maxChildIndex = (
			rightChildIndex < count ? 
			(arr[rightChildIndex] > arr[leftChildIndex] ? rightChildIndex : leftChildIndex) : 
			leftChildIndex);//找出最大孩子并考虑右孩子不存在的可能
		if(arr[root] < arr[maxChildIndex]) {//执行交换,并更新根节点
			tmp = arr[root];
			arr[root] = arr[maxChildIndex];
			arr[maxChildIndex] = tmp;
			root = maxChildIndex;
		} else {
			
			return;//无需交换,结束本轮操作
		}
	}
}
//堆排序,还原手工过程
void heapSort(int *arr, int count) {
	//从最后一个非叶子节点依次往前,作为根节点,进行节点判断
	int root;
	int tmp;

	for(root = count/2 - 1; root >= 0; root--) {
		adjustOneRoot(arr, count, root);
	}
	//接下来交换并剔除下表为0的根
	tmp = arr[count - 1];
	arr[count - 1] = arr[0];
	arr[0] = tmp;
	count--;
	
	for( ; count > 1; count--) {
		adjustOneRoot(arr, count, 0);
		
		tmp = arr[count - 1];
		arr[count - 1] = arr[0];//滞后自减
		arr[0] = tmp;
	}
}

·实例验证

void main() {
	int arr[10] = {4, 8, 2, 9, 1, 0, 5, 7, 6, 3,};
	printf("排序前:");
	show(arr, 10);
	heapSort(arr, 10);
	printf("排序后:");
	show(arr, 10);
}

运行结果
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值