三种改良排序(希尔排序,堆排序,快速排序)

一:改良的直接插入排序(希尔排序)
优化考虑:因为直接插入排序对于大致有顺序的数列效率较高,希尔排序即将数列先进行一定排序
希尔:

for (step = count/2; step; step /= 2) {
		for (start = 0; start < step; start++) {
			shellOneSort(data, count, start, step);
		}
	}

一次:

for (i = start + step; i < count; i += step) {
		tmp = data[i];
		for (j = start; j < i && tmp >= data[j]; j += step) {
		}
		for (t = i; t > j; t -= step) {
			data[t] = data[t - step];
		}
		data[j] = tmp;
	}

原理:希尔排序对于初始n个元素的数列,第一轮以n/2为步长(即0, n/2两个数据),将两个数据进行直接插入排序;
第二轮以n / 2 ^ 2为步长,进行直接插入排序
在这里插入图片描述

即将一长串数列按固定间距划分成几个数据块,在数据块中进行直接插入排序。

二:改良的选择排序(堆排序)
基于二叉树;
升序(大根堆):每一个非叶子节点值都大于其子节点
二叉树一些计算:
①最大非叶子节点编号:n / 2 - 1
②编号为t的节点,其左孩子编号:t * 2 + 1

堆排序首先将数列表示成一个完全二叉树(即最多有一个只有左孩子的节点,切最后一个节点删除,不影响前面序号的连续性)
将这个二叉树都调整为大根堆(即将非叶子节点和左孩子右孩子值作比较,最大的放到非叶子节点上)
调整后根节点就是值最大的节点,最后一个节点是值最小的节点,将根节点和最后一个节点值交换,最后一个节点上的值即为最大值,
再进行大根堆调整,但此时二叉树的数据个数要减一(因为最后一个节点已经排序好了)

void adjustHeap(int *data, int count, int root) {
	int left;
	int right;
	int maxNode;
	int tmp;

	while (root <= count / 2 - 1) {
		left = 2*root + 1;
		right = left + 1;

		maxNode = right >= count ? left
				: (data[left] > data[right] 
				? left : right);
		maxNode = data[root] > data[maxNode]
				? root : maxNode;
		if (maxNode == root) {
			return;
		}
		tmp = data[root];
		data[root] = data[maxNode];
		data[maxNode] = tmp;

		root = maxNode;
	}
}

void heapSort(int *data, int count) {
	int root;
	int tmp;

	for (root = count / 2 - 1; root > 0; root--) {
		adjustHeap(data, count, root);
	}
	while (count) {
		adjustHeap(data, count, 0);

		tmp = data[0];
		data[0] = data[count - 1];
		data[count - 1] = tmp;
		--count;
	}
}

首先拿到二叉树,取出最后一个非叶子节点,计算它的左右孩子下标,将该左右子树调整为大根堆,
最后一个左右子树调整好后,再取上一个左右子树进行调整,直到取到根节点并调整完,即整个树都调整完毕
在这里插入图片描述

调整完后就将①取根节点数,②和最后一个节点交换,③总节点数-1,④在进行一次大根堆调整
这样直到节点数归0,即数列排序完毕

三:改良的交换排序(快速排序)
取出数列第一个元素,既然第一个元素已经取出保护,则第一个元素可以视为空,
分别从第一个元素和最后一个元素开始,用非空的那个元素进行比较,当非空元素小于取出得第一个元素,则将非空元素放到head上,反之则放到tail上

int tmp = data[head];
int start = head;
int end = tail;
if (head >= tail) {
		return;
	}

	while (head < tail) {
		while (head < tail && data[tail] >= tmp) {
			--tail;
		}
		if (head < tail) {
			data[head] = data[tail];
			++head;
		}
		while (head < tail && data[head] <= tmp) {
			++head;
		}
		if (head < tail) {
			data[tail] = data[head];
			--tail;
		}
	}
	data[head] = tmp;

	quickOnce(data, count, start, head - 1);
	quickOnce(data, count, head + 1, end);

开始head为0;
1.用tail与tmp比较:
1.1 若tail >= tmp, tail–,重复第一步
1.2 若tail < tmp, head与tail值互换
1.3 tail–
2.用head与tmp比较:
2.1 若head <= tmp, head++,重复第二步
2.2 若head > tmp, head与tail值互换
2.3 head++
第一步和第二步重复执行,直到head >= tail
在这里插入图片描述

这是一次快速排序,但这一次结束后,中间元素是最开始的第一个元素,该元素左侧和右侧分别为比它小和大的元素,
左侧右侧又该重复进行排序
可以发现这种排序一直在进行重复的工作,只是head和tail还有tmp在变化(即起始、终点位置变化,基准元素变化)
所以可以用函数递归调用实现

不用递归也可以用堆栈代替,因为递归是基于新的位置进行重复操作,那么用堆栈去记录新的起始、终点位置,在每次处理前进行
出栈,处理后进行新位置入栈即可
例:一开始入栈0和count-1
接下来入栈(start和head -1) 和 (head +1和end)
若堆栈非空则重复上述操作,就可以替代递归

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魔幻音

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

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

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

打赏作者

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

抵扣说明:

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

余额充值