堆排序

注:该篇文章会与我的个人博客同步更新。欢迎移步https://cqh-i.github.io/体验更好的阅读效果。

  堆排序依赖的是一种堆的数据结构。什么是堆呢?
  堆要满足两个特点:

  • 堆是一棵完全二叉树
  • 任意父节点大于它的两个子节点(大根堆)

  堆是顺序储存,也就是说它的所有节点存放在数组中。因为它是连续的,可以通过下标来获取它的父节点和子节点。对于一个下标为i节点,它的父节点为(i - 1) / 2, 左孩子节点为2i + 1, 右孩子节点为2i + 2
  堆排序的步骤:

  1. 构建堆。将完全二叉树进行堆化(heapify), 对于完全乱序的完全二叉树,可以最后一个非叶子节点((tree.length - 1) - 1) / 2开始, 也就是最后一个子节点的父节点。
  2. 将最上面的节点与最后一个节点进行交换,然后将最后一个节点"砍掉"(这里可以用树节点的个数表示,也就是数组的长度),对最上面的节点进行堆化。
  3. 重复2,直到树中没有节点。
    注:堆化的意思就是将不满足堆特点的非叶子点转换为堆的过程。
    堆排序的时间复杂度O(nlogn), 空间复杂度O(1),是不稳定的。
public class Heap {

	/**   
	 * @Description: 交换两个节点的值
	 * @return: void      
	 */
	public static void swap(int[] tree, int max, int i) {
		int temp = tree[i];
		tree[i] = tree[max];
		tree[max] = temp;
	}

	/**   
	 * @Description: 对非叶子节点进行一次堆化
	 * @param tree 待堆化的完全二叉树
	 * @param length 完全二叉树节点的数目
	 * @param i   对哪一个节点进行heapify(堆化)操作,大根堆
	 * @return: void      
	 */
	public static void heapify(int[] tree, int length, int i) {
		if (i >= length)
			return;
		int n1 = 2 * i + 1;// 左孩子节点下标
		int n2 = 2 * i + 2;// 右孩子节点下标
		int max = i; // 假设的最大节点下标
		if (n1 < length && tree[n1] > tree[max]) {
			max = n1;
		}
		if (n2 < length && tree[n2] > tree[max]) {
			max = n2;
		}
		if (max != i) {
			swap(tree, max, i);
			heapify(tree, length, max);
		}
	}

	/**   
	 * @Description: 构建堆
	 * (对于完全乱序的完全二叉树,可以最后一个非叶子节点((tree.length - 1) - 1) / 2开始, 
	 * 也就是最后一个子节点的父节点)
	 * @param tree      
	 * @return: void      
	 */
	public static void buildHeap(int[] tree) {
		int lastNode = tree.length - 1;// 最后一个节点的下标
		int parent = (lastNode - 1) / 2;
		for (int i = parent; i >= 0; i--) {
			heapify(tree, tree.length, i);
		}
	}

	/**   
	 * @Description: 堆排序,结果从小到大
	 * @param tree      
	 * @return: void      
	 */
	public static void heapSort(int[] tree) {
		buildHeap(tree);
		for (int i = tree.length - 1; i >= 0; i--) {
			swap(tree, i, 0);
			heapify(tree, i, 0);
		}
	}

	public static void main(String[] args) {
		int[] tree = new int[] { 1, 5, 4, 9, 8, 3, 10 };
		heapSort(tree);
		System.out.println(Arrays.toString(tree));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值