概要

前面分别通过C和C++实现了斜堆,本章给出斜堆的Java版本。还是那句老话,三种实现的原理一样,择其一了解即可。

目录
1. 斜堆的介绍
2. 斜堆的基本操作
3. 斜堆的Java实现(完整源码)
4. 斜堆的Java测试程序

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3638552.html


更多内容:数据结构与算法系列 目录

 

斜堆的介绍

斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列;作为一种自适应的左倾堆,它的合并操作的时间复杂度也是O(lg n)。
它与左倾堆的差别是:
(01) 斜堆的节点没有"零距离"这个属性,而左倾堆则有。
(02) 斜堆的合并操作和左倾堆的合并操作算法不同。

斜堆的合并操作
(01) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
(02) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
(03) 合并后,交换新堆根节点的左孩子和右孩子。
        第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。

 

斜堆的基本操作

1. 基本定义

public class SkewHeap<T extends Comparable<T>> {

    private SkewNode<T> mRoot;    // 根结点

    private class SkewNode<T extends Comparable<T>> {
        T key;                // 关键字(键值)
        SkewNode<T> left;    // 左孩子
        SkewNode<T> right;    // 右孩子

        public SkewNode(T key, SkewNode<T> left, SkewNode<T> right) {
            this.key = key;
            this.left = left;
            this.right = right;
        }

        public String toString() {
            return "key:"+key;
        }
    }

    ...
}

SkewNode是斜堆对应的节点类。
SkewHeap是斜堆类,它包含了斜堆的根节点,以及斜堆的操作。

 

2. 合并

/*
 * 合并"斜堆x"和"斜堆y"
 */
private SkewNode<T> merge(SkewNode<T> x, SkewNode<T> y) {
    if(x == null) return y;
    if(y == null) return x;

    // 合并x和y时,将x作为合并后的树的根;
    // 这里的操作是保证: x的key < y的key
    if(x.key.compareTo(y.key) > 0) {
        SkewNode<T> tmp = x;
        x = y;
        y = tmp;
    }

    // 将x的右孩子和y合并,
    // 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
    SkewNode<T> tmp = merge(x.right, y);
    x.right = x.left;
    x.left = tmp;

    return x;
}

public void merge(SkewHeap<T> other) {
    this.mRoot = merge(this.mRoot, other.mRoot);
}

merge(x, y)是内部接口,作用是合并x和y这两个斜堆,并返回得到的新堆的根节点。
merge(other)是外部接口,作用是将other合并到当前堆中。


3. 添加

/* 
 * 新建结点(key),并将其插入到斜堆中
 *
 * 参数说明:
 *     key 插入结点的键值
 */
public void insert(T key) {
    SkewNode<T> node = new SkewNode<T>(key,null,null);

    // 如果新建结点失败,则返回。
    if (node != null)
        this.mRoot = merge(this.mRoot, node);
}

insert(key)的作用是新建键值为key的节点,并将其加入到当前斜堆中。

 

4. 删除

/* 
 * 删除根结点
 * 
 * 返回值:
 *     返回被删除的节点的键值
 */
public T remove() {
    if (this.mRoot == null)
        return null;

    T key = this.mRoot.key;
    SkewNode<T> l = this.mRoot.left;
    SkewNode<T> r = this.mRoot.right;

    this.mRoot = null;          // 删除根节点
    this.mRoot = merge(l, r);   // 合并左右子树

    return key;
}

remove()的作用是删除斜堆的最小节点。

 

注意关于斜堆的"前序遍历"、"中序遍历"、"后序遍历"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!

 

斜堆的Java实现(完整源码)

斜堆的实现文件(SkewHeap.java)

  1 /**
  2  * Java 语言: 斜堆
  3  *
  4  * @author skywang
  5  * @date 2014/03/31
  6  */
  7 
  8 public class SkewHeap<T extends Comparable<T>> {
  9 
 10     private SkewNode<T> mRoot;    // 根结点
 11 
 12     private class SkewNode<T extends Comparable<T>> {
 13         T key;                // 关键字(键值)
 14         SkewNode<T> left;    // 左孩子
 15         SkewNode<T> right;    // 右孩子
 16 
 17         public SkewNode(T key, SkewNode<T> left, SkewNode<T> right) {
 18             this.key = key;
 19             this.left = left;
 20             this.right = right;
 21         }
 22 
 23         public String toString() {
 24             return "key:"+key;
 25         }
 26     }
 27 
 28     public SkewHeap() {
 29         mRoot = null;
 30     }
 31 
 32     /*
 33      * 前序遍历"斜堆"
 34      */
 35     private void preOrder(SkewNode<T> heap) {
 36         if(heap != null) {
 37             System.out.print(heap.key+" ");
 38             preOrder(heap.left);
 39             preOrder(heap.right);
 40         }
 41     }
 42 
 43     public void preOrder() {
 44         preOrder(mRoot);
 45     }
 46 
 47     /*
 48      * 中序遍历"斜堆"
 49      */
 50     private void inOrder(SkewNode<T> heap) {
 51         if(heap != null) {
 52             inOrder(heap.left);
 53             System.out.print(heap.key+" ");
 54             inOrder(heap.right);
 55         }
 56     }
 57 
 58     public void inOrder() {
 59         inOrder(mRoot);
 60     }
 61 
 62     /*
 63      * 后序遍历"斜堆"
 64      */
 65     private void postOrder(SkewNode<T> heap) {
 66         if(heap != null)
 67         {
 68             postOrder(heap.left);
 69             postOrder(heap.right);
 70             System.out.print(heap.key+" ");
 71         }
 72     }
 73 
 74     public void postOrder() {
 75         postOrder(mRoot);
 76     }
 77 
 78     /*
 79      * 合并"斜堆x"和"斜堆y"
 80      */
 81     private SkewNode<T> merge(SkewNode<T> x, SkewNode<T> y) {
 82         if(x == null) return y;
 83         if(y == null) return x;
 84 
 85         // 合并x和y时,将x作为合并后的树的根;
 86         // 这里的操作是保证: x的key < y的key
 87         if(x.key.compareTo(y.key) > 0) {
 88             SkewNode<T> tmp = x;
 89             x = y;
 90             y = tmp;
 91         }
 92 
 93         // 将x的右孩子和y合并,
 94         // 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
 95         SkewNode<T> tmp = merge(x.right, y);
 96         x.right = x.left;
 97         x.left = tmp;
 98 
 99         return x;
100     }
101 
102     public void merge(SkewHeap<T> other) {
103         this.mRoot = merge(this.mRoot, other.mRoot);
104     }
105 
106     /* 
107      * 新建结点(key),并将其插入到斜堆中
108      *
109      * 参数说明:
110      *     key 插入结点的键值
111      */
112     public void insert(T key) {
113         SkewNode<T> node = new SkewNode<T>(key,null,null);
114 
115         // 如果新建结点失败,则返回。
116         if (node != null)
117             this.mRoot = merge(this.mRoot, node);
118     }
119 
120     /* 
121      * 删除根结点
122      * 
123      * 返回值:
124      *     返回被删除的节点的键值
125      */
126     public T remove() {
127         if (this.mRoot == null)
128             return null;
129 
130         T key = this.mRoot.key;
131         SkewNode<T> l = this.mRoot.left;
132         SkewNode<T> r = this.mRoot.right;
133 
134         this.mRoot = null;          // 删除根节点
135         this.mRoot = merge(l, r);   // 合并左右子树
136 
137         return key;
138     }
139 
140     /*
141      * 销毁斜堆
142      */
143     private void destroy(SkewNode<T> heap) {
144         if (heap==null)
145             return ;
146 
147         if (heap.left != null)
148             destroy(heap.left);
149         if (heap.right != null)
150             destroy(heap.right);
151 
152         heap=null;
153     }
154 
155     public void clear() {
156         destroy(mRoot);
157         mRoot = null;
158     }
159 
160     /*
161      * 打印"斜堆"
162      *
163      * key        -- 节点的键值 
164      * direction  --  0,表示该节点是根节点;
165      *               -1,表示该节点是它的父结点的左孩子;
166      *                1,表示该节点是它的父结点的右孩子。
167      */
168     private void print(SkewNode<T> heap, T key, int direction) {
169 
170         if(heap != null) {
171 
172             if(direction==0)    // heap是根节点
173                 System.out.printf("%2d is root\n", heap.key);
174             else                // heap是分支节点
175                 System.out.printf("%2d is %2d's %6s child\n", heap.key, key, direction==1?"right" : "left");
176 
177             print(heap.left, heap.key, -1);
178             print(heap.right,heap.key,  1);
179         }
180     }
181 
182     public void print() {
183         if (mRoot != null)
184             print(mRoot, mRoot.key, 0);
185     }
186 }
View Code

相关文章:

  • 2021-10-24
  • 2021-12-25
  • 2021-12-25
  • 2022-12-23
  • 2021-09-06
猜你喜欢
  • 2021-08-28
  • 2021-10-10
  • 2021-09-07
  • 2021-09-08
相关资源
相似解决方案