【数据结构】二叉堆

1、定义

二叉堆(英语:binary heap)是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树。二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。

— 维基百科

二叉堆分为两种

  • 最大堆
    最大堆的特点是父节点的键值总是大于等于任一子节点的键值
  • 最小堆
    最小堆的特点是父节点的键值总是小于等于任一子节点的键值

2、基本操作

二叉堆通常用数组来表示,有两个基本的操作:上浮下沉

  • 上浮:添加元素时使用,保持堆的平衡
  • 下沉:删除堆定元素时使用,保持堆的平衡

下面展示一个基本的二叉堆(最大堆)

	                    11                          
                    /    	  \ 
                  9            10
                /   \    	 /    \ 
       		  5      6   	7      8
             / \    / \
            1   2  3   4 

用数组来表示的话

[11, 9, 10, 5, 6, 7, 8, 1, 2, 3, 4]

其中,如果父节点下标为i的话,左子节点下标可表示为2 * i + 1,右子节点下标可表示为2 * i + 2

3、上浮(siftUp)

如果要添加一个元素100,100会逐渐上浮,直到到达堆顶

大致流程如下表示:

 1、
	                    11                          
                    /    	  \ 
                  9            10  
                /   \    	 /    \ 
       		  5      6   	7      8
             / \    / \    /
            1   2  3   4 100
 2、
	                    11                          
                    /    	  \ 
                  9            10  
                /   \    	 /    \ 
       		  5      6     100       8
             / \    / \    /
            1   2  3   4  7
 3、
	                    11                          
                    /    	  \ 
                  9            100
                /   \    	 /    \ 
       		  5      6     10       8
             / \    / \    /
            1   2  3   4  7
 3、
	                    100                      
                    /    	  \ 
                  9            11
                /   \    	 /    \ 
       		  5      6     10       8
             / \    / \    /
            1   2  3   4  7

java代码

	int[] arr;//假设已被创建好
	
	public void siftUp(int index) {
		while (index > 0 && arr[parent(index)] < arr[index]) {
			int parent = parent(index);
			int tmp = arr[parent];
			arr[parent] = arr[index];
			arr[index] = tmp;
			index = parent;
		}
	}
	
	private int parent(int index) {
		return (index - 1) >> 1;
	}

4、下沉(siftDown)

以最初的二叉堆为例,假设移出堆顶的11,并将堆尾的4移动到堆顶,接下来就需要下沉来重新形成最大堆

1、	                     4                          
                    /    	  \ 
                  9            10
                /   \    	 /    \ 
       		  5      6   	7      8
             / \    / 
            1   2  3  
2、	                     10                          
                    /    	  \ 
                  9            4  
                /   \    	 /    \ 
       		  5      6   	7      8
             / \    / 
            1   2  3  
3、	                     10                          
                    /    	  \ 
                  9            8  
                /   \    	 /    \ 
       		  5      6   	7      4  
             / \    / 
            1   2  3  

java代码

	int[] arr;//假设已创建好
	int size;//数组中实际的元素个数
	
	private void siftDown(int index) {
		int k = size >> 1;//size是数组实际的元素个数
		while (index < k) {
			int left = 2 * index + 1;//左子节点
			int j = left;
			if (left + 1 < size && arr[left + 1] > arr[left])
				j = left + 1;//右子节点
			if (arr[index] >= arr[j])
				break;
			int tmp = arr[index];
			arr[index] = arr[j];
			arr[j] = arr[index];
			index = j;
		}
	}

5、堆排序

  • 先将数组构建成一个二叉堆,再将堆顶元素和堆尾元素互换,最后将新堆顶元素下沉,重复此过程,就可以得到一个有序数组

	public void heapSort(int[] arr) {
		int length = arr.length;
		for (int i = (length - 1) / 2; i >= 0; i--) {
			siftDown(arr, length, i);
		}
		for (int i = length - 1; i > 0; i--) {
			swap(0, i, arr);
			siftDown(arr, i, 0);
		}
	}

	private void siftDown(int[] arr, int length, int index) {
		while (2 * index < length) {
			int j = 2 * index;
			if (j + 1 < length && arr[j + 1] > arr[j])
				j = j + 1;
			if (arr[index] >= arr[j])
				break;
			swap(index, j, arr);
			index = j;
		}
	}

	private void swap(int i, int j, int[] arr) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值