java实现最大堆的程序代码_使用Java实现最大堆

最大堆和最小堆是二叉堆的两种形式。

最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

堆实际上是一种完全二叉树。最大(小)堆建堆的复杂度是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类的基础上加上一个变量num表示当前结点在堆中的编号,而且要求泛型要是整形,所以我定义了一个HeapNode继承了TreeNode(优先考虑复用,但是这里明显出现了HeapNode is TreeNode的情形,因此采用继承):

package com.maxHeap;

import com.linkedTree.TreeNode;

public class HeapNode extends TreeNode {//HeapNode is TreeNode

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 getRightChild() {

return rightChild;

}

@Override

public void setRightChild(TreeNode rightChild) {

this.rightChild = (HeapNode) rightChild;

}

@Override

public TreeNode getLeftChild() {

return this.leftChild;

}

@Override

public void setLeftChild(TreeNode leftChild) {

this.leftChild = (HeapNode) leftChild;

}

}

紧接着,同理写了一个MaxHeap类继承LinkedTree:

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 {//MaxHeap is LinkedTree MaxHeap要求是完全二叉树

// private int size;//不用定义size,因为nodeMap.size()就是树的大小

private Map nodeMap = new HashMap();//编号与结点的映射

private void setRoot(HeapNode root){

this.root = root;

}

@Override

//重写getParent方法 因为节点有编号之后 有更快的方法获得父节点

public TreeNode getParent(TreeNode 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 dataSet){//添加一个集合 迭代添加集合中的每个元素

Iterator i = dataSet.iterator();

while(i.hasNext()){

add(i.next());

}

}

public MaxHeap(Set set){//通过一个set创建一个MaxHeap

Iterator 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()

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()

if(left&&right){//左右节点都有

if(leftChild.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 set2 = new HashSet();

set2.add(9);

set2.add(11);

set2.add(10);

set2.add(19);

maxHeap.add(set2);

maxHeap.breadFirstOrder();

maxHeap.pop();

maxHeap.breadFirstOrder();

}

}

我的实现思路在注释里。欢迎拍砖。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值