堆是一种图的树形结构

其实就是把线型的存储方式装换为树型的存储方式。

我们平常看到的都是以线型方式来存在的,就像下图这样子
线形图
那么树的存储结构就从线型变成了树状结构。
树状结构图

树的结构是一种非线型存储结构,存储的是具有“一对多”关系的数据元素的集合。

树的节点

结点:使用树结构存储的每一个数据元素都被称为“结点”。
父节点:像上图中6、5的父节点就是7。
子节点:那么7的子节点就是5、6。
根节点:每一个非空树都有且只有一个被称为根的结点。 像上图中7就是跟节点。

树根的判断依据为:如果一个节点没有父节点,那么这个节点就是整棵树的根节点。

叶子节点:如果节点没有任何子节点,那么此节点称为叶子节点(叶节点)。

堆又分为顶堆和顶堆
小顶堆子节点必须大于父节点。如果父节点大于子节点,那么就要交换,(要从最后面开始) 直到符合规则。如果继续往里面添加数字,则要放到最后面,然后继续依次比较。
大顶堆则反之

来看一个大顶堆的动图吧
大顶堆动图

树的结构的特性如下:

  1. 一个父节点最多有2个子节点
  2. 如果是小顶堆,那么它的父节点 <= 子节点
    如果是大顶堆,那么它的父节点 >= 子节点 就好比这样
    小: 父节点 <= 子节点
    大: 父节点 >= 子节点
  3. 它的结构都是从上到下,从左到右的。
    上 -> 下
    左 -> 右
  4. 该行不足就加一行

堆中最顶端的数据始终最小,所以无论数据量有多少,取出最小值的时间复杂度都为0(1)。
另外,因为取出数据后需要将最后的数据移到最顶端,然后一边比较它与子结点数据的大小,一边往下移动,所以取出数据需要的运行时间和树的高度成正比。假设数据量为n,根据堆的形状特点可知树的高度为log₂n,那么重构树的时间复杂度便为O(logn)
添加数据也一样。在堆的最后添加数据后,数据会一边比较它与父结点数据的大小,一边往上移动,直到满足堆的条件为止,所以添加数据需要的运行时间与树的高度成正比,也是O(logn)。


/**
 * Java数组实现堆结构 下标格式如下 0 1 2 3 4 5 6
 */
public class MyHeap {
	// find the index of left children
	// 下标为i的节点的左侧子节点的下标
	public static int left(int i) {
		return (i + 1) * 2 - 1;
	}

	// find the index of right children
	// 下标为i的节点的右侧子节点的下标
	public static int right(int i) {
		return (i + 1) * 2;
	}

	// find the index of parent
	// 下标为i的节点的父节点的下标
	public static int parent(int i) {
		return (i - 1) / 2;
	}

	// build the max heap
	// 构建某一数组的最大堆结构
	public static void buildMaxHeap(int[] array, int length) {
		// the last node index is array.length-1
		// and his parent index is (array.length-1-1)/2
		for (int i = parent(length - 1); i >= 0; i--) {
			keepMaxHeap(array, i, length);
		}
	}

	public static void keepMaxHeap(int[] array, int k, int length) {
		int l = left(k);// k节点的左子节点
		int r = right(k);// k节点的右子节点
		int largest = k;// 假定k节点是k-l-r中的最大值节点
		if (l < length && array[l] > array[largest]) {// 左子节点>最大值节点
			largest = l;
		}
		if (r < length && array[r] > array[largest]) {// 右子节点>最大值节点
			largest = r;
		}
		if (largest != k) {// 发现最大值节点不是k节点时 是左或右子节点 交换
			swap(array, k, largest);
			// int temp = array[k];
			// array[k]= array[largest];// 新k节点为最大值节点
			// array[largest] = temp;// 左或右子节点为原k节点值
			// 迭代左或右子节点上的原k节点值
			// 此时这个节点往下判断左右子节点
			keepMaxHeap(array, largest, length);
		}
	}

	// heap sort the array
	public static void heapSort(int[] array) {
		int length = array.length;
		for (int i = 1; i <= length; i++) {
			swap(array, length - i, 0);
			buildMaxHeap(array, length - i);
		}
	}

	// swap the value of index i and j in the array
	public static void swap(int[] array, int i, int j) {
		int temp = array[i];
		array[i] = array[j];
		array[j] = temp;
	}

	// print the array
	public static void printArray(int[] array) {
		for (int i : array) {
			System.out.print(i + " ");
		}
		System.out.println("");
	}
}

测试类:


public class HeapTest {
 public static void main(String[] args) {
  int[] array = {6,10,1,2,4,9,7,8,5,3};
  // 从小到大排序使用大顶堆
  // 从大到小排序使用小顶堆
  MyHeap.buildMaxHeap(array,array.length);
  System.out.println("生成大顶堆后的数据是: ");
  MyHeap.printArray(array);
  MyHeap.heapSort(array);
  System.out.println("使用堆排序后的数据是: ");
  MyHeap.printArray(array);
 }
}

运行结果图:
运行结果图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值