索引 堆

为什么引入索引堆

首先给定一个数组,通过heapify过程,将该数组构造成一个最大堆,

构建前和构建后,我们关注的数组,数组元素位置发生了改变,正式由于改变, 才可以仍然将数组看成堆,但是,我们这里是以整数为例的,改变过程无论是交换还是赋值操作都相对简单,但如果元素类型是字符串这样相对复杂的结构呢?那么交换操作花费的时间相对比较大,这种性能消耗还是可以解决的,另一个问题是整个元素在堆中的位置发生了改变,使得堆建成以后很难索引到,索引不到,就无法改变。举个系统任务优先级的例子,假设数组中元素索引号为1的系统任务的优先级是15,索引为2的系统任务的优先级是17....把这个数组数组构建成堆以后,索引和优先级之间的对应关系发生了改变,如果再想改变原来某个索引对应的任务的优先级,因为没有对应关系,而发生的改变也没有规律可循,就没办法实现目标了,所以,可以在堆这个类中引入一个index属性,保存原来data的索引号,并且将索引构建成堆

两种构建堆的方式对比

data属性构建堆

 

 构建前                                                                                          构建后

图(1)

index属性构建堆

 

构建前                                                                        构建后

图(2)

这里,我在纸上手动构建了一遍,直接从index构建堆,就是图(2)中的左图,一步步构建成最大堆,显然结果是右图,同时,图(1)中的data构建堆后,图(1)的右图和图(2)的右图是对应的。但是,在index heap中,构建堆后,data位置并没有发生改变,发生改变的是index,还是举个例子方便理解。,堆顶元素的index为10,表示堆顶元素是10这个索引所指向的元素data:62,它的左右子节点分别是9和7,9和7这两个索引分别指向41和28......

这种构建堆堆的方式的优点:

1.构建堆的过程只是索引发生变化,交换,索引是int型,操作相对简单

2.如果想对堆中数据进行操作,比如想改变进程号为7的任务的的优先级(这里是28)改变成20,那么改变后,就需要重新维护队的性质,这时只需要根据新的data来改变index,这就是index_heap

创建index heap

创建index heap的思路和创建heap的思路是一样的,只是,元素比较的时候是比较的data,但是交换的是index

 完整代码:

 1 template<typename Item>
 2 class IndexMaxHeap{
 3 private:
 4     Item* data;
 5     int* indexes;
 6     int count;
 7     int capacity;
 8 private:
 9     void shiftUp(int k){
10         while(k>1){
11             if(data[indexes[k/2]]<data[indexes[k]]){
12                 swap(indexes[k/2],indexes[k]);
13                 k/=2;
14             }
15         }
16     }
17 
18     void shiftDown(int k){
19         shilw(2*k<=count){
20             int j = 2*k;
21             if(data[indexes[j]]<data[indexes[j+1]])
22                 j+=1;
23             if(data[indexes[k]]<data[indexes[j]]){
24                 swap(indexes[k],indexes[j]);
25                 k=j;
26             }
27         }
28     }
29 public:
30     IndexMaxHeap(int capacity){
31         data = new Item[capacity+1];
32         indexes = new int[capacity];
33         this->capacity = capacity;
34         count =0;
35     }
36     ~IndexMaxHeap(){
37         delete[] data;
38         delete[] indexes;
39     }
40     int size(){return count;}
41     bool isEmpty(){return count==0;}
42 
43     void insert(int i,Item item){
44         assert(count+1<=capacity);
45         assert(i+1>0 && i+1<=capacity);
46         i+=1;
47         count+=1;
48         data[i] = item;
49         indexes[count]=i;
50         shiftUp(count);
51     }
52 
53     Item extractMax(){
54         assert(count>0);
55         Item item = data[indexes[1]];
56         swap(indexes[1],indexes[count]);
57         count--;
58         shiftDown(1);
59         return item;
60     }
61 
62     int extractMaxIndex(){
63         assert(count>0);
64         int idx = indexes[1];
65         swap(indexes[1],indexes[count]);
66         count--;
67         shiftDown(1);
68         return idx;
69     }
70 
71     Item getMax(){return data[indexes[1]];}
72     int getMaxIndex(){
73         assert(count>0);
74         return indexes[1]-1;
75     }
76 // get the data value whose index is i
77     Item getItem(int i){
78         assert(i+1>0 && i+1<=capacity);
79         return data[i+1];
80     }
81     // assign a new value for the data whose index is i
82     void change(int i,Item item){
83         i+=1;
84         data[i] = item;
85         for(int j= 1;j<=count;j++)
86             if(indexes[j]==i){
87                 shiftDown(j);
88                 shiftUp(j);
89             }
90     }
91 };
View Code

优化

前面的index heap 虽然解决了改变堆中元素,由于data位置是不改变的,所以可以通过 data[i] = newItem; 但是,改变index数组以维护队的性质这个操作就无法通过一步实现了,此时,需要先遍历indexes数组,时间复杂度是O(N),再调整的时间复杂度是O(logN),所以T(N) = O(N0+O(logN),比如,我们想要改变所以位置4的data,更改后要维护这个堆,这个堆中存储的元素是上一行的索引,所以要在indexes数组中找到4所在位置,需要遍历indexes数组,找到是第9个位置,然后对滴9个位置调整,如果,我们在对这个类中添加一个reverse属性,关系如下:

  

 

  1 template<typename Item>
  2 class IndexMaxHeapO{
  3 private:
  4     Item* data;
  5     int* indexes;
  6     int* reverse;
  7     int count;
  8     int capacity;
  9 private:
 10     void shiftUp(int k){
 11         while(k>1){
 12             if(data[indexes[k/2]]<data[indexes[k]]){
 13                 swap(indexes[k/2],indexes[k]);
 14                 reverse[indexes[k]]= k;
 15                 reverse[indexes[k/2]] = k/2;
 16                 k/=2;
 17             }
 18         }
 19     }
 20 
 21     void shiftDown(int k){
 22         while(2*k<=capacity){
 23             int j = 2*k;
 24             if(data[indexes[j]]<data[indexes[j+1]])
 25                 j+=1;
 26             if(data[indexes[k]]<data[indexes[j]]){
 27                 swap(indexes[j],indexes[k]);
 28                 reverse[[indexes[k]]] = k;
 29                 reverse[indexesj]=j;
 30                 k=j;
 31             }
 32         }
 33     }
 34 public:
 35     IndexMaxHeapO(int capacity){
 36         data = new Item[capacity+1];
 37         indexes = new int[capacity+1];
 38         reverse = new inr[capacity+1];
 39         //initialize reverse[i]=0:index i does not exsit in heap
 40         for(int i =0;i<=capacity;i++)
 41             reverse[i] =0; // initialize reverse befor
 42         this->capacity = capacity;// initialize the capacity
 43         count = 0;       // and count
 44     }
 45     ~IndexMaxHeapO(){
 46         delete[] data;
 47         delete[] indexes;
 48         delete[] reverse;
 49     }
 50 
 51     int size(){return count;}
 52     bool isEmpty(){return count==0;}
 53 
 54     void insert(int i,Item item){
 55         assert(count+1<=capacity);
 56         assert(i+1<=capacity && i+1>0);
 57         assert(!contain(i));
 58         i+=1;
 59         data[i] = item;
 60         indexes[count+1] =i;
 61         reverse[i] = count+1;
 62         count++;
 63         shiftUp(count);
 64     }
 65     bool contain(int i){
 66         assert(i+1<=capacity && i+1>0);
 67         return reverse[i+1]!=0;
 68     }
 69 
 70     Item extractMax(){
 71         Item item = data[indexes[1]];
 72         swap(indexes[1],indexes[count]);
 73         reverse[indexes[count]]=0;
 74         count--;
 75         if(count){
 76             reverse[indexes[1]]=1;
 77             shiftDown(1);
 78         }
 79         return item;
 80     }
 81 
 82     int extractMax(){
 83         int idx = indexes[1]-1;
 84         swap(indexes[1],indexes[count]);
 85         reverse[indexes[count]]=0;
 86         count--;
 87         if(count){
 88             reverse[indexes[1]]=1;
 89             shiftDown(1);
 90         }
 91         return idx;
 92     }
 93     Item getMax(){
 94         assert(count>0);
 95         return data[indexes[1]];
 96     }
 97     Item getItem(int i){
 98         assert(contain(i));
 99         return data[i+1];
100     }
101 
102     void change(int i,Item newItem){
103         assert(contain(i));
104         i+=1;
105         data[i] = newItem;
106         shiftDown(reverse[i]);
107         shiftUp(reverse[i]);
108     }
109 };
View Code

总结

对于堆这种数据结构,尤其是index heap和i加入reverse 属性的index heap,如果直观上不好分析,可以在纸上画画,手动实现一遍操作过程,也可以像我那样将分析过程写下来,写的过程有助于分析

   

 

posted @ 2018-07-19 22:35  Holly_U  阅读(499)  评论(0编辑  收藏  举报