一、堆数据结构的特点
堆是基于完全二叉树的数据结构,其中父节点的值大于子节点的,称为最大堆,父节点小鱼子节点的,称为最小堆。
堆是添加数据都是在堆尾添加,取数据都是在堆顶取数据。每个对堆的修改操作,都有义务保持堆的结构。
堆的顺序是指:每个父节点的值都比左右子节点的值小(最小堆)。其他顺序没有做强制要求。
二、代码实现
定义了三个类,一个是节点信息Node、一个是堆Heap、另一个是测试主函数HeapTester。代码如下:
Node.java
package com.chen.heap;
public class Node
{
public int value;
public Node(int value)
{
this.value = value;
}
}
Heap.java
package com.chen.heap;
public class Heap
{
private Node[] heap = new Node[64];
private int tail = 0;
//将待插入的数据放到堆尾,并堆调整
public void insert(int value)
{
if (tail >= heap.length)
{
expend();
}
heap[tail] = new Node(value);
tail++;
heapize(getParent(tail - 1));
}
private int getParent(int idx)
{
return (idx - 1) / 2;
}
private int getLeft(int idx)
{
return idx * 2 + 1;
}
private int getRigth(int idx)
{
return idx * 2 + 2;
}
private void expend()
{
Node[] tmp = new Node[heap.length * 2];
for (int i = 0; i < heap.length; ++i)
{
tmp[i] = heap[i];
}
tail = heap.length;
heap = tmp;
}
//取堆顶的数据,并将堆尾的数据放到堆顶,然后堆调整
public Node pop()
{
if (tail > 0)
{
Node result = heap[0];
heap[0] = heap[tail - 1];
tail--;
heapize(0);
return result;
}
return null;
}
private void heapize(int parent)
{
Integer left = null;
if (getLeft(parent) < tail)
left = heap[getLeft(parent)].value;
Integer right = null;
if (getRigth(parent) < tail)
right = heap[getRigth(parent)].value;
Integer pvalue = heap[parent].value;
//两边是叶子节点或者值都比父节点大,不用调整
if ((left == null || left >= heap[parent].value) && (right == null || right >= pvalue))
{
return;
}
//左节点不用调整,右节点需要
else if ((left == null || left >= heap[parent].value) && right != null && right < pvalue)
{
heap[getRigth(parent)].value = pvalue;
heap[parent].value = right;
}
//右节点不用调整,左节点需要
else if ((right == null || right >= pvalue) && left != null && left < pvalue)
{
heap[getLeft(parent)].value = pvalue;
heap[parent].value = left;
}
//左右节点需要调整
else if (left < pvalue || right < pvalue)
{
if (left > right)
{
heap[getRigth(parent)].value = pvalue;
heap[parent].value = right;
}
else
{
heap[getLeft(parent)].value = pvalue;
heap[parent].value = left;
}
}
//保险起见,对三个可能有变动的节点都进行了递归调整
if (parent != 0)
{
heapize(getParent(parent));
}
if (left != null)
{
heapize(getLeft(parent));
}
if (right != null)
{
heapize(getRigth(parent));
}
}
public void print()
{
int idx = 0;
int line = 1;
int lineIdx = 0;
while (idx < tail)
{
System.out.print(heap[idx].value + "\t");
idx++;
lineIdx++;
if (lineIdx == line)
{
System.out.println();
line *= 2;
lineIdx = 0;
}
}
}
}
编写完主要的数据结构后,写了个测试的函数运行,插入若干数据并取数据,查看数据是否有序
HeapTester.java
package com.chen.heap;
public class HeapTester
{
public static void main(String[] args)
{
Heap heap = new Heap();
heap.insert(10);
heap.insert(11);
heap.insert(0);
heap.insert(5);
heap.insert(7);
heap.insert(111);
heap.insert(50);
heap.insert(61);
heap.insert(23);
heap.insert(4);
heap.insert(19);
heap.print();
System.out.println("\n***************************************");
Node result = heap.pop();
while(result != null){
System.out.print(result.value+"\t");
result = heap.pop();
}
}
}
运行结果如下:
0
4 10
11 5 111 50
61 23 7 19
***************************************
0 4 5 7 10 11 19 23 50 61 111
三、复杂度分析
插入和取出的操作,都只会调整堆的高度的次数,而堆的是完全二叉树,所以堆的高度是log2(n),由此可推,时间复杂度是O(logN)。
在插入和取出操作中,并没有用到额外的空间,所以空间复杂度是O(1)。