2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 二叉堆算法具体实现细节- Java实现

二叉堆算法具体实现细节- Java实现

时间:2019-10-14 14:36:21

相关推荐

二叉堆算法具体实现细节-  Java实现

前言:之前在学习二叉堆算法时,感觉自己明白了。但是当我自己去写代码的时候,或者回想的时候,却很难系统的描述出来。在仔细研究源码之后,发现了一些之前没有发现的新东西,特地,记录下来,供大家参考。

文章目录

二叉堆原理二叉堆的应用二叉堆操作具体实现总结

二叉堆原理

二叉堆类似于完全二叉树。不过与二叉树不同之处在于,二叉堆底层是数组,其左右子节点和父节点的关联是通过索引建立。例如当前节点的元素为arr[index],如果该元素有子节点,那么左子节点为arr[2*index + 1], 右子节点为arr[2*index + 2].同理 ,如果index > 0, 那么该元素的父节点为arr[(index- 1)/2].

这是二叉树的精髓所在:通过数组索引将父节点和子节点建立其联系

下面用一个图,清晰表示二叉树元素之间的关系

二叉堆的应用

之所以要谈到二叉堆的应用,在于二叉堆有个非常独特的特征。二叉堆可以分为大根堆小根堆。所谓大根堆,就是,父节点的值大于等于左右子节点的值,这样大根堆的堆顶(即数组的第一个数)是数组的最大值,如上图所示。而小根堆就是,父节点的值小于等于左右子节点的值,这样小根堆的堆顶(即数组的第一个数)是数组的最小值。由于这样的特性, 二叉堆被用于实现优先队列(Java中有源码)。

二叉堆操作

二叉堆的操作可以分为两大部分,所以代码也是围绕这两个操作进行。

添加元素

当添加元素时,首先会在堆尾处添加,为了使添加后的新堆也满足大根堆(或者小根堆)的特征,就需要对新添加的元素进行操作(此处命名为“上浮”),大致的过程为:以大根堆为例,将该元素与其父节点进行比较,如果比父节点大,则于其交换位置,并接着和上面的父节点再对比,依次类推,直到堆顶元素。

删除元素(注意:通常指删除堆顶元素)

删除元素时,进行的操作为: 将堆顶元素,替换为堆尾元素,接着将堆尾元素下沉,和左右子节点比较, 如果比两者都大,就将堆尾元素存放在堆顶,否则将子节点中的最大值放在堆顶,接着和原先子节点所在索引位置的子节点进行比较,重复以上操作。使其满住大根堆的特征。

过程,以图片形式展现一下,图中是以小根堆为例

将数组[23,56,40,62,38,55,10,16]的中的元素依次插入堆中的过程。

删除堆顶的操作,具体流程如下

具体实现

首先从添加元素开始,实现二叉堆(以最大堆为例)。

public class BinaryStackTest {private Integer[] queen; // 数组队列, 实现最大堆private int size; // 即记录当前堆尾部的位置也记录堆中的元素,初始值为0,未添加元素private final int DEFAULT_INITIAL_CAPACITY = 11; //默认初始容量public BinaryStackTest (){// 构造器this(DEFAULT_INITIAL_CAPACITY );}public BinaryStackTest (int capacity){// 构造器重载this.queen = new Integer[capacity];}}

上面代码已经将基本属性全部赋值。

接下来为具体方法实现。

向堆中添加元素

/*** 向堆中添加元素* @param e* @return*/public boolean add(int e){int k = size; //当前元素存放在数组中的位置,如果第一次添加,此处size = 0;// // 存在容量不足问题,此处不去关注,可以去看java中源码,非常简单siftUp(k, e, queen); // 将当前元素上浮size++; // 当前队列中的元素个数 + 1return true;}

接下来实现上浮操作

/*** 将元素进行上浮,使得堆满足最大堆* @param k* @param e* @param es*/private void siftUp(int k, int e, int[] es) {while (k > 0){ // 一直循环到堆顶元素int parent = (k-1) >>> 1; // 找到父节点的索引,通过移位的方式等效除以2;if (es[parent] > e){break;}es[k] = es[parent]; // 将其父节点的值 存放在当前位置k = parent; // 将指针指向父节点位置}es[k] = e; // 找到新添加元素位置后, 存放该元素}

这样当向堆中添加元素时,就会满住最大堆的性质。

为了观察数组的元素排列,添加这方法:

public int[] getQueen(){return queen;}

测试一下效果:

BinaryStackTest BinaryStackTest = new BinaryStackTest ();int[] arr = new int[]{1, 2, 3, 4, 5};for (int e: arr){stackSortTest.add(e); // 依次往堆中添加元素}int[] queen = stackSortTest.getQueen();System.out.println(Arrays.toString(queen));

输出结果

[5, 4, 2, 1, 3, 0, 0, 0, 0, 0, 0]

ok,至此,添加元素的操作,我们就解决了。

第二步,解决删除操作

/*** 删除操作: 删除堆顶元素* @return*/public int poll(){final int[] es;int result = 0;if(size > 0){// 判断数组中是否有元素result = (es = queen)[0] // 堆顶元素int n = --size; // 数组中元素总量少1,此处n表示堆尾元素的索引int x = es[n]; // 堆尾元素if(n > 0){// 判断是否删除后,数组中没有元素了siftDown(0, es, x, n); // 下沉此元素}}return result;}

接着解决最后一个方法,下沉操作.。这个方法细节还比较多,多看几遍。

public void siftDown(int k, int[] es, int x, int n){int half = n>>>1 ; // 确保下面循环的节点非叶节点while(k < half){int child = (k <<1) + 1; // 左节点int c = es[child]; // 左节点元素int right = child +1 ; // 右节点元素if(right < n && es[right] > c){child = right; // 确保child为子节点最大值的索引c = es[child] // 确保c为子节点最大值}es[k] = c;k = child; //此处将k指向下个子节点位置}es[k] = x; //最终x存放的位置}

上面要注意这一行的意思:

int half = n>>>1 ; // 确保下面循环的节点非叶节点

通过图片来说明:

假设现在删除一下节点的堆顶元素。

那么程序的执行过程为:

总结

二叉堆,最重要的两个操作就是上浮和下沉上浮用于二叉堆插入数据的情况下沉用于二叉堆删除堆顶元素时。只要理解这两个操作,便就理解了二叉堆。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。