使用Java实现最大堆
来源:互联网 发布:win7开软件盘 编辑:程序博客网 时间:2024/05/21 20:34
最大堆和最小堆是二叉堆的两种形式。
最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。
堆实际上是一种完全二叉树。最大(小)堆建堆的复杂度是o(n),也就是说可以在线性时间内把一个无序的序列转化为堆。插入的时间复杂度为o(log2(n))(2为底n的对数的时间复杂度),删除的时间复杂度也是这么多,因为对于插入删除,在最坏的情况下,插入将整个树从最低的一端比较到树的最高一端,而删除是从树的最高的一端比较到树最低的一端。
PS、这里有个疑问,就是从算法上大致看过去,会认为建堆的复杂度是o(nlog2(n)),实际计算出来结果是o(n),证明过程如下:
建堆的计算时间为2^i(log2(n)-1)从i=0加到i=log2(n),因为第i层的结点最多2^i个,每个元素在建堆调整位置的时候最坏情况是从最底层调整到最高层,因此第i层最坏情况下的总共调整次数应该是2^i(log2(n)-1),这个结果经过整理计算出来实际上复杂度是o(n)。
在之前我们用Java实现了二叉树的顺序存储和链式存储,这次用Java实现的最大堆是基于前面写的链式存储的二叉树实现的。
在实现最大堆的时候,需要在之前的TreeNode<E>类的基础上加上一个变量num表示当前结点在堆中的编号,而且要求泛型要是整形,所以我定义了一个HeapNode继承了TreeNode<Integer>(优先考虑复用,但是这里明显出现了HeapNode is TreeNode<Integer>的情形,因此采用继承):
package com.maxHeap;import com.linkedTree.TreeNode;public class HeapNode extends TreeNode<Integer> {//HeapNode is TreeNode<Integer> private HeapNode leftChild; private HeapNode rightChild; private int num;//编号 public HeapNode(Integer e) { super(e); } public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public TreeNode<Integer> getRightChild() { return rightChild; } @Override public void setRightChild(TreeNode<Integer> rightChild) { this.rightChild = (HeapNode) rightChild; } @Override public TreeNode<Integer> getLeftChild() { return this.leftChild; } @Override public void setLeftChild(TreeNode<Integer> leftChild) { this.leftChild = (HeapNode) leftChild; } }
紧接着,同理写了一个MaxHeap类继承LinkedTree<Integer>:
package com.maxHeap;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;import com.linkedTree.LinkedTree;import com.linkedTree.TreeNode;public class MaxHeap extends LinkedTree<Integer> {//MaxHeap is LinkedTree<Integer> MaxHeap要求是完全二叉树// private int size;//不用定义size,因为nodeMap.size()就是树的大小 private Map<Integer,HeapNode> nodeMap = new HashMap<Integer,HeapNode>();//编号与结点的映射 private void setRoot(HeapNode root){ this.root = root; } @Override //重写getParent方法 因为节点有编号之后 有更快的方法获得父节点 public TreeNode<Integer> getParent(TreeNode<Integer> node) {//获取node结点的父节点 HeapNode heapNode = (HeapNode)node; int nodeNum = heapNode.getNum(); return (nodeNum%2==0)?nodeMap.get((nodeNum>>1)-1):nodeMap.get((nodeNum-1)>>1);//获取nodeMap中对应结点 采用位运算增加效率 } private void swapNode(HeapNode node1 , HeapNode node2){//交换node1和node2以及它的映射 int item1 = node1.getItem(); node1.setItem(node2.getItem()); node2.setItem(item1); nodeMap.put(node1.getNum(), node1); nodeMap.put(node2.getNum(), node2); } public void add(int data){//添加一个元素 HeapNode heapNode = new HeapNode(data); shiftUpNode(nodeMap.size(),heapNode);//从最新的位置移到应该放的位置 } public void add(Set<Integer> dataSet){//添加一个集合 迭代添加集合中的每个元素 Iterator<Integer> i = dataSet.iterator(); while(i.hasNext()){ add(i.next()); } } public MaxHeap(Set<Integer> set){//通过一个set创建一个MaxHeap Iterator<Integer> i = set.iterator(); int num = 0; while(i.hasNext()){ Integer e = i.next(); HeapNode node = new HeapNode(e); shiftUpNode(num, node);//从最新的一个位置的结点移到他应该在的位置 num++; } } private void shiftUpNode(int num, HeapNode node) {//从最靠后的结点移到准确的位置 if(this.root==null){ this.setRoot(node);//没有根则设置根 node.setNum(num);//设置编号 } else{ boolean right = (num%2 == 0);//是否是父节点的右子树 if(right){ nodeMap.get((num>>1)-1).setRightChild(node); } else{ nodeMap.get((num-1)>>1).setLeftChild(node); } node.setNum(num); HeapNode heapNode = node;//中间量 while(this.getParent(heapNode).getItem()<heapNode.getItem()){//构建最大堆 swapNode((HeapNode) this.getParent(heapNode),heapNode);//交换节点 heapNode = (HeapNode) this.getParent(heapNode);//交换完之后 保存父节点的位置 继续与其父节点进行比较 if(this.getParent(heapNode)==null) break; } } nodeMap.put(num, node);//保存映射 } private void shiftDownNode(){//把根节点下移到准确的位置 HeapNode pointer = (HeapNode) root; while(true){ HeapNode leftChild = (HeapNode) pointer.getLeftChild(); HeapNode rightChild = (HeapNode) pointer.getRightChild(); boolean left = leftChild != null; boolean right = rightChild != null; if(left&&(pointer.getItem()<leftChild.getItem())||(right&&pointer.getItem()<rightChild.getItem())){//有左结点或者右结点并且可交换 if(left&&right){//左右节点都有 if(leftChild.getItem()<rightChild.getItem()){//谁大谁上 swapNode(pointer,(HeapNode) pointer.getRightChild()); pointer = (HeapNode) pointer.getRightChild();//继续跟踪被下放的根节点 } else{ swapNode(pointer,(HeapNode) pointer.getLeftChild()); pointer = (HeapNode) pointer.getLeftChild(); } } else if(!left){//没有左结点 swapNode(pointer,(HeapNode) pointer.getRightChild()); pointer = (HeapNode) pointer.getRightChild(); } else{//没有右结点 swapNode(pointer,(HeapNode) pointer.getLeftChild()); pointer = (HeapNode) pointer.getLeftChild(); } } else return; } } public int peek(){//获取最大的结点 不删除 return this.root.getItem(); } public int pop(){//获取最大的结点 并且删除 int size = nodeMap.size();//映射个数 swapNode((HeapNode) this.root, nodeMap.get(size-1));//交换根结点和最后一个结点 int maxNum = nodeMap.get(size-1).getItem();//最大值 if((size-1)%2==0)//最后一个结点是父节点的右结点 getParent(nodeMap.get(size-1)).setRightChild(null);//将父节点的右结点置空 help gc 下同 else getParent(nodeMap.get(size-1)).setLeftChild(null); nodeMap.remove(size-1);//删除最大的 shiftDownNode();//将根结点调到适当的位置 return maxNum; }}
最后写一个测试类Test来进行测试:
class Test{ public static void main(String[] args){ System.out.println("MaxHeap Test:"); MaxHeap maxHeap = new MaxHeap(set); maxHeap.breadFirstOrder(); maxHeap.preOrder(); System.out.println(maxHeap.getRoot().getItem()); maxHeap.add(9); maxHeap.breadFirstOrder(); Set<Integer> set2 = new HashSet<Integer>(); set2.add(9); set2.add(11); set2.add(10); set2.add(19); maxHeap.add(set2); maxHeap.breadFirstOrder(); maxHeap.pop(); maxHeap.breadFirstOrder(); }}
我的实现思路在注释里。欢迎拍砖。
阅读全文
1 0
- 使用Java实现最大堆
- Java 实现最大堆
- 最大堆java实现
- java实现最大堆数据结构
- java 实现构造最大堆
- java实现最大堆及堆排序
- java实现最大索引堆(最大堆的优化版)
- (五)使用最大堆实现优先级队列
- java实现最大堆及代码测试
- 手写最大堆(Java实现)
- 最大二叉堆的Java实现
- 使用 stl container priority_queue 实现最小堆和最大堆
- 最大堆及堆排序的Java实现_world
- 最小堆和最大堆的JAVA实现
- 最大堆实现堆排序
- 最大堆实现堆排序
- 最大堆的实现
- 最大堆的实现
- LeetCode-Split Linked List in Parts
- [LeetCode] Best Time to Buy and Sell Stock
- 为什么java函数不支持参数默认值?
- angular2
- 在列表中找出两个彼此最接近但不相等的数
- 使用Java实现最大堆
- 九、Docker-compose
- Lintcode76 Longest Increasing Subsequence solution 题解
- Okhttp网络请求post方式 加Json解析
- android studio gradle依赖解析
- SQL SERVER 2008 EXPRESS 安装注意事项
- maven 常用插件
- 老鼠走迷宫
- http://www.cnblogs.com/only-levis/p/7637285.html