Java数据结构和算法(十四)——堆

  在Java数据结构和算法(五)——队列中咱们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列能够用有序数组来实现,这种实现方式尽管删除最大数据项的时间复杂度为O(1),可是插入仍是须要较长的时间 O(N),由于每次插入平均须要移动一半的数据项,来保证插入后,数组依旧有序。html

  本篇博客咱们介绍另一种数据结构——堆,注意这里的堆和咱们Java语言,C++语言等编程语言在内存中的“堆”是不同的,这里的堆是一种树,由它实现的优先级队列的插入和删除的时间复杂度都为O(logN),这样尽管删除的时间变慢了,可是插入的时间快了不少,当速度很是重要,并且有不少插入操做时,能够选择用堆来实现优先级队列。java

一、堆的定义

  ①、它是彻底二叉树,除了树的最后一层节点不须要是满的,其它的每一层从左到右都是满的。注意下面两种状况,第二种最后一层从左到右中间有断隔,那么也是不彻底二叉树。node

  

  ②、它一般用数组来实现。算法

  

  这种用数组实现的二叉树,假设节点的索引值为index,那么:编程

  节点的左子节点是 2*index+1,数组

  节点的右子节点是 2*index+2,数据结构

  节点的父节点是 (index-1)/2。数据结构和算法

  ③、堆中的每个节点的关键字都大于(或等于)这个节点的子节点的关键字。编程语言

  这里要注意堆和前面说的二叉搜索树的区别,二叉搜索树中全部节点的左子节点关键字都小于右子节点关键字,在二叉搜索树中经过一个简单的算法就能够按序遍历节点。可是在堆中,按序遍历节点是很困难的,如上图所示,堆只有沿着从根节点到叶子节点的每一条路径是降序排列的,指定节点的左边节点或者右边节点,以及上层节点或者下层节点因为不在同一条路径上,他们的关键字可能比指定节点大或者小。因此相对于二叉搜索树,堆是弱序的。大数据

二、遍历和查找

  前面咱们说了,堆是弱序的,因此想要遍历堆是很困难的,基本上,堆是不支持遍历的。

  对于查找,因为堆的特性,在查找的过程当中,没有足够的信息来决定选择经过节点的两个子节点中的哪个来选择走向下一层,因此也很难在堆中查找到某个关键字。

  所以,堆这种组织彷佛很是接近无序,不过,对于快速的移除最大(或最小)节点,也就是根节点,以及能快速插入新的节点,这两个操做就足够了。

三、移除

  移除是指删除关键字最大的节点(或最小),也就是根节点。

  根节点在数组中的索引老是0,即maxNode = heapArray[0];

  移除根节点以后,那树就空了一个根节点,也就是数组有了一个空的数据单元,这个空单元咱们必须填上。

  第一种方法:将数组全部数据项都向前移动一个单元,这比较费时。

  第二种方法:

    ①、移走根

    ②、把最后一个节点移动到根的位置

    ③、一直向下筛选这个节点,直到它在一个大于它的节点之下,小于它的节点之上为止。

  具体步骤以下:

  

  图a表示把最后一个节点移到根节点,图b、c、d表示将节点向下筛选到合适的位置,它的合适位置在最底层(有时候可能在中间),图e表示节点在正确位置的情景。

  注意:向下筛选的时候,将目标节点和其子节点比较,谁大就和谁交换位置。

四、插入

  插入节点也很容易,插入时,选择向上筛选,节点初始时插入到数组最后第一个空着的单元,数组容量大小增一。而后进行向上筛选的算法。

  注意:向上筛选和向下不一样,向上筛选只用和一个父节点进行比较,比父节点小就中止筛选了。

  

五、完整的Java堆代码

  首先咱们要知道用数组表示堆的一些要点。若数组中节点的索引为x,则:

  节点的左子节点是 2*index+1,

  节点的右子节点是 2*index+2,

  节点的父节点是 (index-1)/2。

  注意:"/" 这个符号,应用于整数的算式时,它执行整除,且获得是是向下取整的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package  com.ys.tree.heap;
 
public  class  Heap {
     
     private  Node[] heapArray;
     private  int  maxSize;
     private  int  currentSize;
     
     public  Heap( int  mx) {
         maxSize = mx;
         currentSize =  0 ;
         heapArray =  new  Node[maxSize];
     }
     
     public  boolean  isEmpty() {
         return  (currentSize ==  0 )?  true  false ;
     }
     
     public  boolean  isFull() {
         return  (currentSize == maxSize)?  true  false ;
     }
     
     public  boolean  insert( int  key) {
         if (isFull()) {
             return  false ;
         }
         Node newNode =  new  Node(key);
         heapArray[currentSize] = newNode;
         trickleUp(currentSize++);
         return  true ;
     }
     //向上调整
     public  void  trickleUp( int  index) {
         int  parent = (index -  1 ) /  2 //父节点的索引
         Node bottom = heapArray[index];  //将新加的尾节点存在bottom中
         while (index >  0  && heapArray[parent].getKey() < bottom.getKey()) {
             heapArray[index] = heapArray[parent];
             index = parent;
             parent = (parent -  1 ) /  2 ;
         }
         heapArray[index] = bottom;
     }
     
     public  Node remove() {
         Node root = heapArray[ 0 ];
         heapArray[ 0 ] = heapArray[--currentSize];
         trickleDown( 0 );
         return  root;
     }
     //向下调整
     public  void  trickleDown( int  index) {
         Node top = heapArray[index];
         int  largeChildIndex;
         while (index < currentSize/ 2 ) {  //while node has at least one child
             int  leftChildIndex =  2  * index +  1 ;
             int  rightChildIndex = leftChildIndex +  1 ;
             //find larger child
             if (rightChildIndex < currentSize &&   //rightChild exists?
                     heapArray[leftChildIndex].getKey() < heapArray[rightChildIndex].getKey()) {
                 largeChildIndex = rightChildIndex;
             }
             else  {
                 largeChildIndex = leftChildIndex;
             }
             if (top.getKey() >= heapArray[largeChildIndex].getKey()) {
                 break ;
             }
             heapArray[index] = heapArray[largeChildIndex];
             index = largeChildIndex;
         }
         heapArray[index] = top;
     }
     //根据索引改变堆中某个数据
     public  boolean  change( int  index,  int  newValue) {
         if (index <  0  || index >= currentSize) {
             return  false ;
         }
         int  oldValue = heapArray[index].getKey();
         heapArray[index].setKey(newValue);
         if (oldValue < newValue) {
             trickleUp(index);
         }
         else  {
             trickleDown(index);
         }
         return  true ;
     }
     
     public  void  displayHeap() {
         System.out.println( "heapArray(array format): " );
         for ( int  i =  0 ; i < currentSize; i++) {
             if (heapArray[i] !=  null ) {
                 System.out.print(heapArray[i].getKey() +  " " );
             }
             else  {
                 System.out.print( "--" );
             }
         }
     }
}
class  Node {
     private  int  iData;
     public  Node( int  key) {
         iData = key;
     }
     
     public  int  getKey() {
         return  iData;
     }
     
     public  void  setKey( int  key) {
         iData = key;
     }
}
相关文章
相关标签/搜索