自己实现一个堆
以大根堆为例
前置准备:
创建一个数组来接收
创建一个遍历usedSize 统计堆的元素个数
initElem方法遍历传入的数组
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem = new int[10];
}
public void initElem(int[] array){
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
}
建堆:
private void shiftDown(int parent,int len){
int child = 2*parent + 1;
while (child < len){
// 判断是否有左孩子
if( child + 1 < len && elem[child] < elem[child + 1]){
child++;
}
// 比较孩子与父亲结点 谁大
if( elem[child] > elem[parent] ){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
// 结点还要继续往下面的结点比较
parent = child;
child = 2*parent + 1;
}else {
break;
}
}
}
public void createHeap(){
for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
shiftDown(parent,usedSize);
}
}
插入元素:
插入元素从最末尾插入,然后向上调整 堆
// 向上调整建堆的时间复杂度为 N*logN
// 堆添加一个元素
public void offer(int val){
// 判断是否会溢出
if(isFul()){
// 扩容
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize++] = val;
// 向上调整
shiftUp(usedSize-1);
}
public boolean isFul(){
return usedSize == elem.length;
}
// 向上调整
public void shiftUp(int child){
int parent = (child-1)/2;
while (child > 0){
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child - 1) / 2;
}else {
break;
}
}
}
删除元素:
堆的删除 是删除堆顶元素
只需要将堆顶元素与最后一个元素交换,65换到28
再将usedSize-1即可 (usedSize表示统计堆中的元素个数)
再将下标为0的元素 向下调整即可 (即堆顶元素)
// 堆删除一个元素
public void pop(){
if (ifEmpty()){
return;
}
int tmp = elem[0];
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;
shiftDown(0,usedSize);
}
public boolean ifEmpty(){
return usedSize == 0;
}
private void shiftDown(int parent,int len){
int child = 2*parent + 1;
while (child < len){
// 判断是否有左孩子
if( child + 1 < len && elem[child] < elem[child + 1]){
child++;
}
// 比较孩子与父亲结点 谁大
if( elem[child] > elem[parent] ){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
// 结点还要继续往下面的结点比较
parent = child;
child = 2*parent + 1;
}else {
break;
}
}
}
总结
创建两个类
测试类:
public static void main(String[] args) {
TestHeap testHeap = new TestHeap();
int[] array = { 27,15,19,18,28,34,65,49,25,37};
testHeap.initElem(array);
testHeap.createHeap();
//testHeap.offer(80);
testHeap.pop();
System.out.println("sdasadasd");
}
}
堆的方法类:
package dataStructDui;
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem = new int[10];
}
public void initElem(int[] array){
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
}
private void shiftDown(int parent,int len){
int child = 2*parent + 1;
while (child < len){
// 判断是否有左孩子
if( child + 1 < len && elem[child] < elem[child + 1]){
child++;
}
// 比较孩子与父亲结点 谁大
if( elem[child] > elem[parent] ){
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
// 结点还要继续往下面的结点比较
parent = child;
child = 2*parent + 1;
}else {
break;
}
}
}
public void createHeap(){
for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
shiftDown(parent,usedSize);
}
}
// 向上调整建堆的时间复杂度为 N*logN
// 堆添加一个元素
public void offer(int val){
// 判断是否会溢出
if(isFul()){
// 扩容
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize++] = val;
// 向上调整
shiftUp(usedSize-1);
}
public boolean isFul(){
return usedSize == elem.length;
}
// 向上调整
public void shiftUp(int child){
int parent = (child-1)/2;
while (child > 0){
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child - 1) / 2;
}else {
break;
}
}
}
// 堆删除一个元素
public void pop(){
if (ifEmpty()){
return;
}
int tmp = elem[0];
elem[0] = elem[usedSize-1];
elem[usedSize-1] = tmp;
usedSize--;
shiftDown(0,usedSize);
}
public boolean ifEmpty(){
return usedSize == 0;
}
}
堆的使用
堆的构造方式
static void TestPriorityQueue(){
// 创建一个空的优先级队列,底层默认容量是11
PriorityQueue<Integer> q1 = new PriorityQueue<>();
// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了三个元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());
System.out.println(q3.peek());
}
堆的常用方法:
static void TestPriorityQueue2(){
int[] arr = {4,1,9,2,8,0,7,3,6,5};
// 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
// 否则在插入时需要不多的扩容
// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低
PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
for (int e: arr) {
q.offer(e);
}
System.out.println(q.size()); // 打印优先级队列中有效元素个数
System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素之和,再次获取优先级最高的元素
q.poll();
q.poll();
System.out.println(q.size()); // 打印优先级队列中有效元素个数
System.out.println(q.peek()); // 获取优先级最高的元素
q.offer(0);
System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空
q.clear();
if(q.isEmpty()){
System.out.println("优先级队列已经为空!!!");
}
else{
System.out.println("优先级队列不为空");
}
}
Top-k 问题:
求最小k个数 建大堆
求最大k个数 建小堆