day07 堆的插入和删除操作实现

13 篇文章 0 订阅

目录

 

前言

堆的插入操作:

代码实现:

堆的删除操作:

代码实现:

完整代码实现:


前言

     昨天学习了优先队列的基本概念,以及学习了堆的概念以及堆的基本实现。那么我们如何通过堆来完成优先队列最重要的两种操作呢?今天我们就来学习一下

 

优先队列的两种重要操作:出队和入队

 

堆的插入操作:

      堆是一种完全二叉树,所以我们在插入的时候,直接把元素插入到数组最后一个位置即可,这样插入后,树依旧是一棵完全二叉树,至于为什么会这样?主要是因为完全二叉树的性质。这里就不过多的去赘述了。

      但是插入操作到这就结束了吗?

      并没有,细心的同学或许发现了问题。虽然我们将㢝插入到最后一个位置后,堆依旧保持着完全二叉树的结构,但是我们却发现,此时数据的顺序并不满足一个最大堆的数据顺序。最大堆的定义是两个孩子节点的值都小于根节点。

      但是我们插入元素 52 之后,却发现此时的堆已经不满足最大堆的定义了。所以在插入元素之后,我们还需要一步操作,就是将我们新插入的元素位置进行调整。将其放到合适的位置上,使得维持堆的数据有序。

      这也是我们最重要的操作,这一步我起名为ShijtUp

原理

      根据最大堆的定义。孩子节点必须小于父节点,所以我们需要不断的和父节点进行对比,如果大于夫节点,则交换位置。然后重复此前操作,直至找到一个父节点父于它的位置。

 

代码实现:

(代码很简单,有不理解的地方可以参考上边的文字叙述)

//最大堆的插入操作
	public void insert(int value) {
		if(count >= datas.length - 1) {
			throw new RuntimeException("Heap is full !");
		}
		
		datas[count+1] = value;
		count+=1;
		
		//对count位置的元素进行调整
		shitUp(count);
	}
	
	/**
	 * 对指定位置的元素进行重新调整,使其能够符合最大堆的定义
	 * 原理:不断和它的父节点比较,如果都比它大,就退出。反之,则进行交换,然后重复操作
	 * */
	private  void shitUp(int index) {
		while(index > 1 && datas[index/2] < datas[index]) {
			int val = datas[index];
			datas[index] = datas[index/2];
			datas[index/2] = val;
			index = index/2;
		}
	}

堆的删除操作:

      如果理解了上边的插入操作,相信删除操作大家一定觉得很简单。

      与插入操作不同的是,我们的删除元素是按照优先级进行输出的,就是将数值最大的元素(也就是我们的根节点)进行删除,做法就是直接将最后一个元素放到根节点位置上,然后 count 指针后退一位。

      但是仅仅是这样就完成了吗?肯定不会,因为插入和删除一样,我们都需要对堆数据的有序性进行维护,使其能符合我们的定义。由于我们删除操作是将最大的元素(根节点)进行删除,然后将最后一个元素填补上来。那么我们需要对根节点的新元素进行调整。

      在这里我们定义的是最大堆。子孩子都小于父节点。所以我们要不断去和子孩子进行比较,如果子孩子都小于根节点,那么此时是合适的。反之,则需要和左右孩子中最大的那个节点进行对换。然后重复上述操作,直至找到一个合适的位置。

      至于为什么要和左右孩子中最大的那个进行对换。因为根节点必须大于子节点,所以必须把最大的那个元素放在根节点的位置上。

      这步操作我们叫做ShiftDown,向下比较

      将最大的元素 62 进行删除

 

 

      将最后一个元素放到根节点

       然后执行ShiftDown操作

代码实现:


	//弹出最大元素
	public int delete() {
		int result = datas[1];
		datas[1] = datas[count];
		count-=1;
		shitDown(1);
		return result;
	}
	
	/**
	 * 对指定位置的元素进行重新调整,使其能够符合最大堆的定义
	 * 原理:不断和它的左右孩子比较,如果都比它小,就退出。反之,则选择一个最大的进行交换,然后重复操作
	 * */
	private void shitDown(int index) {
		//假如左节点还存在,就循环
		while(index*2 <= count) {
			int j = index*2;
			//判断是否有右孩子,然后判断是否符合交换条件
			if(j+1 <= count && datas[j+1] > datas[j]) {
				if(datas[index] > datas[j+1]) {
					reutrn;
				}
				int val = datas[j+1];
				datas[j+1] = datas[index];
				datas[index] = val;
				j += 1;
			}else {
				if(datas[index] > datas[j]) {
					reutrn;
				}
				int val = datas[j];
				datas[j] = datas[index];
				datas[index] = val;
			}
			
			index = j;
		}
	}

完整代码实现:

package com.smarking.lzy.part2;
/*
 * 用数组实现堆
 * 堆的定义?
 * 二叉堆左右节点和父节点之间的关系?
 * 如何使用数组进行是实现?
 * 
 * 
 * */
public class Heap {
	//定义一个数组用来存储数据
	private int [] datas;
	//定义一个变量来记录当前数据的数量
	private int count;
	
	/*
	 * 二叉堆的构造函数
o	 * int size : 堆的大小
	 * */
	public Heap(int size) {
		if(size <= 0) {
			throw new RuntimeException("size can not be null !");
		}
		count = 0;
		datas = new int [size];
	}
	
	//获取当前堆的大小
	public int size() {
		if(datas == null) {
			throw new RuntimeException("Heap is null !");
		}else {
			return count;
		}
	}
	
	//判断是否为空
	public boolean isEmpty() {
		if(datas == null || datas.length == 0) {
			return true;
		}else {
			return false;
		}
	}
	
	//最大堆的插入操作
	public void insert(int value) {
		if(count >= datas.length - 1) {
			throw new RuntimeException("Heap is full !");
		}
		
		datas[count+1] = value;
		count+=1;
		
		//对count位置的元素进行调整
		shitUp(count);
	}
	
	/**
	 * 对指定位置的元素进行重新调整,使其能够符合最大堆的定义
	 * 原理:不断和它的父节点比较,如果都比它大,就退出。反之,则进行交换,然后重复操作
	 * */
	private  void shitUp(int index) {
		while(index > 1 && datas[index/2] < datas[index]) {
			int val = datas[index];
			datas[index] = datas[index/2];
			datas[index/2] = val;
			index = index/2;
		}
	}
	
	//弹出最大元素
	public int delete() {
		int result = datas[1];
		datas[1] = datas[count];
		count-=1;
		shitDown(1);
		return result;
	}
	
	/**
	 * 对指定位置的元素进行重新调整,使其能够符合最大堆的定义
	 * 原理:不断和它的左右孩子比较,如果都比它小,就退出。反之,则选择一个最大的进行交换,然后重复操作
	 * */
	private void shitDown(int index) {
		//假如左节点还存在,就循环
		while(index*2 <= count) {
			int j = index*2;
			//判断是否有右孩子,然后判断是否符合交换条件
			if(j+1 <= count && datas[j+1] > datas[j]) {
				if(datas[index] > datas[j+1]) {
					reurn;
				}
				int val = datas[j+1];
				datas[j+1] = datas[index];
				datas[index] = val;
				j += 1;
			}else {
				if(datas[index] > datas[j]) {
					reurn;
				}
				int val = datas[j];
				datas[j] = datas[index];
				datas[index] = val;
			}
			
			index = j;
		}
	}
	
	
	public void printHeap() {
		if(isEmpty()) {
			throw new RuntimeException("Heap is null !");
		}
		
		for(int i = 1;i <= count;i++) {
			System.out.println("index:"+i+";value:"+datas[i]);
		}
	}
	
	public static void main(String[] args) {
		Heap heap = new Heap(10);
		heap.insert(3);
		heap.insert(2);
		heap.insert(5);
		heap.insert(4);
		heap.insert(0);
		heap.insert(8);
		
		//heap.printHeap();
		
		for(int i = 1;i <= 6 ;i++) {
			System.out.println(heap.delete());
		}
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值