精华内容
下载资源
问答
  • 二叉堆
    2022-03-28 10:56:04

    二叉堆的构建

    对于二叉堆,有三种操作
    插入节点,插入是节点上升,数组的最后一个叶子节点

    删除节点,删除是节点下沉
    意思是删除的是栈顶的节点,从头开始下沉。为了维持完全二叉树结构,会把堆的最后一个节点临时补到原来栈顶的位置

    构建二叉堆
    本职是所有非叶子节点依次下沉
    第一个非叶子节点 下标计算:(len(array) - 2 ) // 2

    关于代码优化部分,# 无需真正交换,单向赋值即可
    是指的对于插入,上浮,循环每次只更新孩子节点,最终确定的父节点最后循环后赋值,这个赋值是在循环开始之前:用一个temp 保存插入的叶子节点值,用于最后的赋值。 上到最上面,把temp赋值最上面的孩子节点

    对于删除,下沉,循环每次只更新父节点,循环之前的temp 保存父节点的值,用于最后的赋值。下到最下面,把temp赋值给最下面的父亲节点

    文字解释

    代码

    def up_adjust(array=[]):
    	"""
    	二叉堆的尾节点上浮操作 小根堆,小的值上浮,相当于插入操作
    	:param array:原数组
    	"""
    	child_index = len(array) - 1
    	parant_index = (child_index - 1) // 2
    	# temp 保存插入的叶子节点值,用于最后的赋值
    	temp = array[child_index]
    	# 循环的条件是 孩子下标大于0并且孩子节点值小于父亲节点值(小的上浮)
    	while child_index > 0 and temp < array[parant_index]:
    		# 无需真正交换,单向赋值即可,意思是交换后,父节点暂时不更新,只更新孩子节点,而此时的父节点不是最终的
    		array[child_index] = array[parant_index] # 更新孩子节点的值
    		# 更新判断条件
    		child_index = parant_index # 孩子节点上移
    		parant_index = (parant_index - 1) // 2 # 重新计算新的父节点下标
    	array[child_index] = temp
    
    def down_adjust(parant_index, length, array=[]):
    	"""
    	二叉堆的节点下沉操作,相当于删除操作
    	删除堆顶元素,把最后一个叶子节点临时补到原本栈顶的位置
    	:param parant_index:待下沉节点坐标
    	:param length:堆的长度范围
    	:param array:原数组
    	"""
    	# temp 保存父节点的值,用于最后的赋值,其实相当于待下沉的坐标,就是最后一个叶子节点,数组的最后一位,现在补到了栈顶
    	temp = array[parant_index] # 栈顶父节点值
    	child_index = 2 * parant_index + 1 # 左孩子下标
    	while child_index < length:
    		# 如果有右孩子,且右孩子的值小于左孩子的值,则定位到右孩子
    		if child_index + 1 < length and array[child_index + 1] < array[child_index]:
    			child_index += 1
    		# 如果父节点的值小于任何一个孩子的值,直接跳出
    		if temp <= array[child_index]:
    			break
    		# 无需真正交换,单向赋值即可
    		array[parant_index] = array[child_index]  #更新父节点值
    		parant_index = child_index # 父节点下移
    		child_index = 2 * child_index + 1 # 重新计算新右孩子节点下标
    	array[parant_index] = temp
    
    def build_heap(array = []):
    	"""
    	二叉堆的构建操作
    	:param array:原数组
    	"""
    	# 从最后一个非叶子节点开始,依次下沉调整
    	for i in range((len(array) - 2 )// 2 , -1 , -1):
    		print(i) # range(3,-1,-1)  # 3,2,1,0
    		down_adjust(i, len(array), array)
    
    
    my_array = list([1,3,2,6,5,7,8,9,10,0])
    up_adjust(my_array)
    print(my_array)
    
    my_array = list([7,1,3,10,5,2,8,9,6])
    build_heap(my_array)
    print(my_array)
    
    
    
    
    
    

    输出结果

    [0, 1, 2, 6, 3, 7, 8, 9, 10, 5]
    3
    2
    1
    0
    [1, 5, 2, 6, 7, 3, 8, 9, 10]

    更多相关内容
  • 前言 在以往工作或者面试的时候常会碰到一个问题,如何实现海量TopN,就是在一个...二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树,二叉堆有两种,最大堆 和 最小堆,最大堆:父结点的键值总是大于
  • 此代码包含多个文件,AStar.c、 AStar.h、main.c以及makefile相关文件,例子默认是在linux下编译实现,也可直接将三个代码文件移植到window开发平台下编译实现
  • 二叉堆的实现数据结构中如何使用,我任务主要是在操作系统中的任务优先级调度问题,当然也可以用于实现堆排序问题,比如找出数组中的第K个最小值问题,采用二叉堆能够快速的实现,今天我就采用C语言实现了一个简单的...
  • 易语言二叉堆

    2020-07-22 04:25:30
    易语言二叉堆源码,二叉堆,加入二叉,减出二叉,寻找数值
  • 本源代码借助标准C++ STL中的vector,list和heap等已封装的数据结构,优化了A星算法搜索地图、检索开始列表过程,减小了程序的时间和空间花费。经检验,检索20000*20000的随机障碍物地图时,程序在规划路径部分的平均...
  • 下面小编就为大家带来一篇python下实现二叉堆以及堆排序的示例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • dijkstra算法的三种实现:数组,二叉堆,斐波那契堆 + 部分实验报告 dijkstra算法的三种实现:数组,二叉堆,斐波那契堆 + 部分实验报告 dijkstra算法的三种实现:数组,二叉堆,斐波那契堆 + 部分实验报告
  • 主要介绍了Java语言实现二叉堆的打印代码分享,具有一定借鉴价值,需要的朋友可以了解下。
  • 二叉堆

    2021-02-09 16:38:37
    二叉堆 定义:二叉堆是完全二叉树或近似完全二叉树。 二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值, 且每个节点的左子树和右子树都是一个二叉堆。 它分为两种:最大堆和最小堆。 ...
    • 二叉堆

    定义:二叉堆是完全二叉树或近似完全二叉树。
    二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,
    且每个节点的左子树和右子树都是一个二叉堆。
    它分为两种:最大堆和最小堆。
    最大堆:父结点的键值总是大于或等于任何一个子节点的键值;
    最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

    最大堆示意图,数组中的存储形式在下面

     

    • 插入

    插入的数值先放在堆的最后,然后与其父节点进行比较交换

    • 删除

    用最后的数值来替换需要删除的数值,然后与其子节点中最大的值比较交换

     

    //MaxHeap.h
    #pragma once
    
    /*
    二叉堆
    定义:二叉堆是完全二叉树或近似完全二叉树。
    二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,
    且每个节点的左子树和右子树都是一个二叉堆。
    它分为两种:最大堆和最小堆。
    最大堆:父结点的键值总是大于或等于任何一个子节点的键值;
    最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
    */
    
    #include <iostream>
    template <typename T>
    class MaxHeap
    {
    public:
    	MaxHeap(int data):capacity(data), m_num(0), pHeap(nullptr)
    	{
    		pHeap = new T[capacity];
    		memset(pHeap, 0, sizeof(T) * capacity);
    	}
    	~MaxHeap()
    	{
    		if (pHeap != nullptr)
    		{
    			delete pHeap;
    			pHeap = nullptr;
    		}
    	}
    	void Insert(T data);//插入
    	void Delete(T data);//删除
    	void Print();		//打印
    	void InsertAmend(int index);//插入修正,调整为最大堆
    	void DeleteAmend(int index);//删除修正,调整为最大堆
    private:
    	int capacity;//二叉堆的容量
    	int m_num;//实际存放个数
    	T *pHeap;
    };
    
    template<typename T>
    void MaxHeap<T>::Insert(T data)
    {
    	if (m_num > capacity)
    		return;
    	pHeap[m_num] = data;
    	
    	InsertAmend(m_num);
    	m_num++;
    }
    
    /*
    1、找到当前需删除data的index
    2、用最后的那个数值来代替index处的数值
    3、调整成二叉堆的形式
    */
    template<typename T>
    void MaxHeap<T>::Delete(T data)
    {
    	int index = 0;
    	
    	while (index < m_num)
    	{
    		//找到则跳出循环
    		if (data == pHeap[index])
    			break;
    		index++;
    	}
    	//未找到则返回
    	if (index == m_num)
    	{
    		std::cout << "No find" << std::endl;
    		return;
    	}
    	pHeap[index] = pHeap[--m_num];
    
    
    	DeleteAmend(index);
    	//删除最后的节点
    
    }
    
    /*调整思路:因为用最后的插入的孩子进行的替换,所以需要向下调整,
    与孩子节点比较,小于孩子节点则进行交换数值
    1、与孩子节点中最大的那个进行比较,孩子节点大则将孩子节点的值付给当前index
    2、一直比较到最后
    3、将index处的数值val付给最后找到的位置
    */
    template<typename T>
    void MaxHeap<T>::DeleteAmend(int index)
    {
    	int val = pHeap[index];
    
    	int iChild = 2 * index + 1;//左孩子
    	while (iChild < m_num)
    	{
    		if (pHeap[iChild] < pHeap[iChild + 1])
    			iChild++;//变为右孩子的index
    		if (pHeap[iChild] < pHeap[index])
    		{
    			break;
    		}
    		//调整
    		pHeap[index] = pHeap[iChild];
    		index = iChild;
    		iChild = 2 * index + 1;
    	}
    	pHeap[index] = val;
    }
    
    template<typename T>
    void MaxHeap<T>::Print()
    {
    	for (int i = 0; i < m_num; i++)
    		std::cout << pHeap[i] << " ";
    	std::cout << std::endl;
    }
    
    /*
    调整的思路:向上与父节点比较、调整
    1、插入的数值是插在数组的后面的
    2、该数值与其父节点的数值比较,比父节点大的话则将父节点值付给该index
    3、一直比较到根节点,直到找到小于父节点的index
    4、将val付给该index
    */
    
    template<typename T>
    void MaxHeap<T>::InsertAmend(int index)
    {
    	T data = pHeap[index];
    	while (index > 0)
    	{
    		int iParentIndex = (index - 1) / 2;
    		//满足大顶堆,跳出循环
    		if (pHeap[iParentIndex] > data)
    		{
    			break;
    		}
    		//不满足,父节点的值付给当前index,index变为父节点的
    		else
    		{
    			pHeap[index] = pHeap[iParentIndex];
    			index = iParentIndex;
    		}
    	}
    	pHeap[index] = data;//data付给最新的索引处
    }
    
    
    
    //main.cpp
    #include "MaxHeap.h"
    
    int main()
    {
    	MaxHeap<int> heap(6);
    
    	for (int i = 0; i < 6; i++)
    	{
    		heap.Insert(i);
    	}
    	heap.Print();
    
    	heap.Delete(5);
    	heap.Print();
    	return 0;
    }

     

    展开全文
  • 主要介绍了java编程实现优先队列的二叉堆代码分享,具有一定参考价值,需要的朋友可以了解下。
  • 优先队列底层的实现原理就是对二叉堆这个数据结构的使用,因为优先队列底层使用的是二叉堆,所以我们可以用O(1)的时间复杂度去获取优先队列中的最大值(这里我们以最大堆来说,所以获取的是最大值) 好,到

    什么是优先队列?

    优先队列这个数据结构的特点就是在我们执行插入或者删除元素的操作时候,优先队列自己会来维护队列中元素的顺序,不用我们自己去重新对队列中的数据进行排序,所以优先队列中就有了两个主要的 API,分别是 insert 插入一个元素和 delMax 删除最大元素(如果底层用最小堆,那么就是 delMin
    优先队列底层的实现原理就是对二叉堆这个数据结构的使用,因为优先队列底层使用的是二叉堆,所以我们可以用O(1)的时间复杂度去获取优先队列中的最大值(这里我们以最大堆来说,所以获取的是最大值)

    好,到这里我们对优先队列建立起了大概的认知:

    1. 优先队列在我们插入或者删除元素的时候会帮我们自动排序
    2. 优先队列底层使用的就是对二叉堆
    3. 我们可以很高效的获取到优先队列中的最值

    什么是二叉堆?

    现在我们再回过头来说,什么是二叉堆。
    二叉堆其实就是以数组的方式存储完全二叉树并且满足每个父节点都大于他的两个子节点,然后这个数组就叫做二叉堆。二叉堆在逻辑上就是一个完全二叉树,只不过存放在了数组当中,我们在操作平衡二叉树的时候通过链表来操作,而在这里我们操作二叉堆则是通过索引的方式来操作二叉堆。
    先来看一个二叉堆的图,加深印象:
    在这里插入图片描述
    索引为0的位置用*占位,并没有什么实际意义,我们可以直接按照顺序从索引为0的位置开始存储

    相信大家一定能看明白完全二叉树和二叉堆之间的对应关系

    二叉堆还分为最大堆最小堆。最大堆的性质是:每个节点都大于等于它的两个子节点。类似的,最小堆的性质是:每个节点都小于等于它的子节点。
    所以显然我们想要获取数组中最大的值非常的高效可以直接通过索引1就获取到了

    好,到这里我们也建立起了二叉堆的基本认知了:

    1. 二叉堆分为最大堆最小堆
    2. 二叉堆在逻辑上就是一个完全二叉树
    3. 二叉堆获取最大值(或者说是最值)非常高效

    所以说优先队列和二叉堆的关系是什么?

    注意!!!这里是直接复制的labuladong的代码,仅用于学习。
    原文地址:https://labuladong.gitee.io/algo/2/21/62/
    下面我们实现一个简化的优先级队列,先看下代码框架:

    这里用到 Java 的泛型,Key 可以是任何一种可比较大小的数据类型,比如 Integer 等类型。

    public class MaxPQ
        <Key extends Comparable<Key>> {
        // 存储元素的数组
        private Key[] pq;
        // 当前 Priority Queue 中的元素个数
        private int size = 0;
    
        public MaxPQ(int cap) {
            // 索引 0 不用,所以多分配一个空间
            pq = (Key[]) new Comparable[cap + 1];
        }
    
        /* 返回当前队列中最大元素 */
        public Key max() {
            return pq[1];
        }
    
        /* 插入元素 e */
        public void insert(Key e) {...}
    
        /* 删除并返回当前队列中最大元素 */
        public Key delMax() {...}
    
        /* 上浮第 x 个元素,以维护最大堆性质 */
        private void swim(int x) {...}
    
        /* 下沉第 x 个元素,以维护最大堆性质 */
        private void sink(int x) {...}
    
        /* 交换数组的两个元素 */
        private void swap(int i, int j) {
            Key temp = pq[i];
            pq[i] = pq[j];
            pq[j] = temp;
        }
    
        /* pq[i] 是否比 pq[j] 小? */
        private boolean less(int i, int j) {
            return pq[i].compareTo(pq[j]) < 0;
        }
    
        /* 还有 left, right, parent 三个方法 */
    }
    
    

    可以看到我们可以非常高效的获取到优先队列中的最值,就是因为优先队列的底层使用的是二叉堆,我们可以直接使用max()方法就可以以O(1)的时间复杂度获取到这个最值

    列出重要的方法:

    1. 插入元素insert(Key e)
    2. 删除并返回当前队列中最大元素delMax()
    3. 上浮第x个元素,以维护最大堆的性质swim(int x)
    4. 下沉第x个元素,以维护最大堆的性质sink(int x)

    swim方法和sink方法

    为什么要有这两个方法呢?
    因为我们在插入元素到优先队列中的时候是插入到数组的末尾,这就会出现一种情况就是这样会破坏优先队列中的一个顺序,此时我们就需要swim方法和sink方法进行维护这个队列的一个顺序。
    对于最大堆,会破坏堆性质的有两种情况:

    # 如下内容参考labuladong算法小抄
    1、如果某个节点 A 比它的子节点(中的一个)小,那么 A 就不配做父节点,应该下去,下面那个更大的节点上来做父节点,这就是对 A 进行下沉。
    
    2、如果某个节点 A 比它的父节点大,那么 A 不应该做子节点,应该把父节点换下来,自己去做父节点,这就是对 A 的上浮。
    
    当然,错位的节点 A 可能要上浮(或下沉)很多次,才能到达正确的位置,恢复堆的性质。所以代码中肯定有一个 while 循环。
    

    上面这段内容什么意思呢?
    说白就是为了维护最大堆的性质,需要将破坏顺序的元素,下沉或者上浮到一定的位置,使数组又满足最大堆的一个性质。

    swim上浮

    举个例子解释上浮:

          	   10
        	/      \
    	  8          12   (注意这个12就破坏了最大最的特性了,此时如果从下向上的顺序看的话,就应该将他上浮 )
        /   \      /    \
      7     4     5      6
    
    			||
    			||     将10和12交换位置
    			\/
    			
          	   12
        	/      \
    	  8         10  
        /   \      /    \
      7     4     5      6		
    

    那代码应该怎么写呢?

    // 代码逻辑
    private void swim(int x) {
        while (如果x没有到堆顶 && x大于它的父亲节点) {
            交换x和它的父亲节点
            此时将x的父亲节点再作为x继续while循环
        }
    }
    
    // 伪代码参考labuladong算法小抄
    private void swim(int x) {
        // 如果浮到堆顶,就不能再上浮了
        while (node > 1 && less(parent(x), x)) {
            // 如果第 x 个元素比上层大
            // 将 x 换上去
            exch(parent(x), x);
            x = parent(x);
        }
    }
    
    

    sink下沉

    下沉于上浮是有区别的,因为上浮的话,父节点只有一个,而下沉子节点有两个,需要都进行比较,如果待下沉的节点比任意一个子节点小,那么就进行下沉,如果比子节点都大,那么就没必要进行下沉了

          	   10 (注意这个10就破坏了最大最的特性了,此时如果从上向下的顺序看的话,就应该将它进行下沉 )
        	/      \
    	  8          12   
        /   \      /    \
      7     4     5      6
    
    			||
    			||     下沉的过程比较做子节点8,发现比8大,然后比较12发现比它小,进行然后进行交换
    			\/
    			
          	   12
        	/      \
    	  8         10  
        /   \      /    \
      7     4     5      6		
    

    代码如下:

    // 代码逻辑
    private void sink(int x) {
        while (当前x是否到最底下) {
            获取两个子节点中较大的一个设为tmp
            如果当前x大于tmp,说明不用继续找了,break
            
            反之需要将当前的x跟tmp进行交换
    		然后将tmp作为x继续下沉,继续执行while循环
        }
    }
    // 代码实现参考labuladong
    private void sink(int x) {
        // 如果沉到堆底,就沉不下去了
        while (left(x) <= size) {
            // 先假设左边节点较大
            int older = left(x);
            // 如果右边节点存在,比一下大小
            if (right(x) <= N && less(older, right(x)))
                older = right(x);
            // 结点 x 比俩孩子都大,就不必下沉了
            if (less(older, x)) break;
            // 否则,不符合最大堆的结构,下沉 x 结点
            exch(x, older);
            x = older;
        }
    }
    

    然后再来看delMax 和 insert方法就简单了

    这两个方法就是建立在 swim 和 sink 上的
    insert代码如下:

    public void insert(Key e) {
        N++;
        // 先把新元素加到最后
        pq[N] = e;
        // 然后让它上浮到正确的位置
        swim(N);
    }
    
    

    delMax 方法先把堆顶元素 A 和堆底最后的元素 B 对调,然后删除 A,最后让 B 下沉到正确位置。

    public Key delMax() {
        // 最大堆的堆顶就是最大元素
        Key max = pq[1];
        // 把这个最大元素换到最后,删除之
        exch(1, N);
        pq[N] = null;
        N--;
        // 让 pq[1] 下沉到正确位置
        sink(1);
        return max;
    }
    
    

    此时一个最大堆的核心方法就都说明完了,我们就获取到了一个插入和删除时间复杂度为O(logk)的一个数据结构了,K 为当前二叉堆(优先级队列)中的元素总数。因为我们时间复杂度主要花费在 sink 或者 swim 上,而不管上浮还是下沉,最多也就树(堆)的高度,也就是 log 级别。


    最后

    强烈推荐labuladong的算法小抄:
    地址如下:https://labuladong.gitee.io/algo/
    没收广告钱,真心推荐!!!

    展开全文
  • 由两部分组成,my_map.cpp用OpenCV实现读取地图等图像处理操作,main.cpp实现A*算法。二叉堆为类,格子为结构体。生成结果后进行优化,使原本只能走8个方向的结果优化为任意角度和方向,也就是真正的全局最短路径。
  • php 的二叉堆操作

    2018-11-21 17:19:27
    PHP对于二叉堆的操作,以及个人的理解,可进行插入、删除操作等
  • 二叉堆(最小堆)+二项堆+斐波那契堆 根基算法导论C++实现
  • 二叉堆的概念及其应用,里面包含果子合并、堆排序、鱼塘钓鱼、最小函数值、Sequence等重要题型的详细解析
  • 优先队列与二叉堆

    2022-02-06 17:54:07
    优先队列与二叉堆 优先队列 优先队列是队列的一种变型,它又有一个别名:堆 我们大家肯定都知道队列,队列遵循FIFO的原则:先出先进。 但是优先队列则不再遵循先入先出的原则,而是分为两种情况: 最大优先队列,...

    优先队列与二叉堆

    优先队列

    优先队列是队列的一种变型,它又有一个别名:堆

    我们大家肯定都知道队列,队列遵循FIFO的原则:先出先进。

    但是优先队列则不再遵循先入先出的原则,而是分为两种情况:
    最大优先队列,无论入队顺序如何,都是当前最大的元素优先出队
    最小优先队列,无论入队顺序如何,都是当前最小的元素优先出队
    例如有一个最大优先队列,其中的最大元素是8,那么虽然8并不是队头元素,但出队时仍然让元素8首先出队。
    在这里插入图片描述

    那么如何实现呢?就用二叉堆来实现:

    二叉堆其实和完全二叉树类似

    二叉堆有两种:大顶堆(最大堆的任何一个父节点的值,都大于或等于它左、右孩子节点的值)、小顶堆(最小堆的任何一个父节点的值,都小于或等于它左、右孩子节点的值)

    因此,可以用最大堆来实现最大优先队列,这样的话,每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点。

    这样入队就是让将元素插入到二叉树中,出队就是删除二叉树的根节点,然后再进行调整,将次之元素补到根节点位置。

    二叉堆的自我调整

    对于二叉堆,有如下几种操作。

    1. 插入节点。

    2. 删除节点。

    3. 构建二叉堆。

    这几种操作都基于堆的自我调整。所谓堆的自我调整,就是把一个不符合堆性质的完全二叉树,调整成一个堆。下面让我们以最小堆为例,看一看二叉堆是如何进行自我调整的。

    1、插入节点

    当二叉堆插入节点时,插入位置是完全二叉树的最后一个位置。

    例如插入一个新节点,值是 0:

    在这里插入图片描述

    这时,新节点的父节点5比0大,显然不符合最小堆的性质。于是让新节点“上浮”,和父节点交换位置。

    在这里插入图片描述

    继续用节点0和父节点3做比较,因为0小于3,则让新节点继续“上浮”。

    在这里插入图片描述
    然后1再与根节点比较,继续上浮。
    在这里插入图片描述

    1. 删除节点

    二叉堆删除节点的过程和插入节点的过程正好相反,所删除的是处于堆顶的节点。例如删除最小堆的堆顶节点1。
    在这里插入图片描述

    这时,为了继续维持完全二叉树的结构,我们把堆的最后一个节点10临时补到原本堆顶的位置。

    在这里插入图片描述

    接下来,让暂处堆顶位置的节点10和它的左、右孩子进行比较,如果左、右孩子节点中最小的一个(显然是节点2)比节点10小,那么让节点10“下沉”。

    在这里插入图片描述

    继续让节点10和它的左、右孩子做比较,左、右孩子中最小的是节点7,由于10大于7,让节点10继续“下沉”。
    在这里插入图片描述

    这样一来,二叉堆重新得到了调整。

    3、二叉堆的调整

    构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质就是让所有非叶子节点依次“下沉”。
    下面举一个无序完全二叉树的例子,如下图所示。

    在这里插入图片描述

    首先,从最后一个非叶子节点开始,也就是从节点10开始。如果节点10大于它左、右孩子节点中最小的一个,则节点10“下沉”。

    在这里插入图片描述

    接下来轮到节点3,如果节点3大于它左、右孩子节点中最小的一个,则节点3“下沉”。

    在这里插入图片描述

    然后轮到节点1,如果节点1大于它左、右孩子节点中最小的一个,则节点1“下沉”。事实上节点1小于它的左、右孩子,所以不用改变。
    接下来轮到节点7,如果节点7大于它左、右孩子节点中最小的一个,则节点7“下沉”。
    在这里插入图片描述

    节点7继续比较,继续“下沉”。

    在这里插入图片描述

    经过上述几轮比较和“下沉”操作,最终每一节点都小于它的左、右孩子节点,一个无序的完全二叉树就被构建成了一个最小堆。

    复杂度

    堆的插入操作是单一节点的“上浮”,堆的删除操作是单一节点的“下沉”,这两个操作的平均交换次数都是堆高度的一半,所以时间复杂度是O(logn)。

    而构建堆的时间复杂度却是O(n)。这涉及数学推导过程,有兴趣的话,可以自己琢磨一下。

    ps

    还需要明确一点:二叉堆虽然是一个完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二
    叉堆的所有节点都存储在数组中。

    在这里插入图片描述

    在数组中,在没有左、右指针的情况下,如何定位一个父节点的左孩子和右孩子呢?
    像上图那样,可以依靠数组下标来计算。
    假设父节点的下标是parent,那么它的左孩子下标就是2×parent+1;右孩子下标就是2×parent+2。

    例如上面的例子中,节点6包含9和10两个孩子节点,节点6在数组中的下标是3,节点9在数组中的下标是7,节点10在数组中的下标是8。
    那么,
    7 = 3×2+1,
    8 = 3×2+2,
    刚好符合规律。

    /**
    
    “上浮”调整
    
    @param array 待调整的堆
    
    */
    
    public static void upAdjust(int[] array) {
    
        int childIndex = array.length-1;
    
        int parentIndex = (childIndex-1)/2;
    
        // temp 保存插入的叶子节点值,用于最后的赋值
    
        int temp = array[childIndex];
    
        while (childIndex > 0 && temp < array[parentIndex]) {
    
            //无须真正交换,单向赋值即可
    
            array[childIndex] = array[parentIndex];
    
            childIndex = parentIndex;
    
            parentIndex = (parentIndex-1) / 2;
    
        }
    
        array[childIndex] = temp;
    
    }
    
    /**
    
    “下沉”调整
    
    @param array 待调整的堆
    
    @param parentIndex 要“下沉”的父节点
    
    @param length 堆的有效大小
    
    */
    
    public static void downAdjust(int[] array, int parentIndex,int length) {
    
        // temp 保存父节点值,用于最后的赋值
    
        int temp = array[parentIndex];
    
        int childIndex = 2 * parentIndex + 1;
    
        while (childIndex < length) {
    
            // 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
    
            if (childIndex + 1 < length && array[childIndex + 1] <array[childIndex]) {
    
                childIndex++;
    
            }
    
            // 如果父节点小于任何一个孩子的值,则直接跳出
    
            if (temp <= array[childIndex]){
                break;
            }
            //无须真正交换,单向赋值即可
    
            array[parentIndex] = array[childIndex];
    
            parentIndex = childIndex;
    
            childIndex = 2 * childIndex + 1;
    
        }
    
        array[parentIndex] = temp;
    
    }
    
    
    
    /**
    
    构建堆
    
    @param array 待调整的堆
    
    */
    
    public static void buildHeap(int[] array) {
    
        // 从最后一个非叶子节点开始,依次做“下沉”调整
    
        for (int i = (array.length-2)/2; i>=0; i--) {
    
            downAdjust(array, i, array.length);
    
        }
    
    }
    
    
    
    public static void main(String[] args) {
    
        int[] array = new int[] {1,3,2,6,5,7,8,9,10,0};
    
        upAdjust(array);
    
        System.out.println(Arrays.toString(array));
    
        array = new int[] {7,1,3,10,5,2,8,9,6};
    
        buildHeap(array);
    
        System.out.println(Arrays.toString(array));
    
    }
    
    展开全文
  • 二叉堆 二叉堆是一种特殊的树,有两种特性: 堆是一个完全二叉树 二叉堆中任意一个父节点的值都大于等于(或小于等于)其左右孩子节点的值。 根据第二条特性,可以将二叉堆分为两类: 最大堆:父节点的值总是...
  • 二叉堆结构的特点 1.二叉堆提供的接口 添加元素 获取最大值 删除最大值 public interface Heap<E> { int size(); // 元素的数量 boolean isEmpty(); // 是否为空 void clear(); // 清空 void add(E ...
  • Python二叉堆(binary heap)二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树。二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉...
  • 图解二叉堆:堆排序

    2021-01-20 17:04:16
    二叉堆的存储方式是数组,为什么我们总是把二叉堆画成二叉树呢?其实二叉堆是一种特殊的二叉树:完全二叉树。由于完全二叉树的性质所以我们更适合使用数组来存储,用数组的索引来操作子节点。而对于一般二叉树我们...
  • 什么是二叉堆二叉堆本质上是一种完全二叉树,它分为两个类型: 最大堆:任何一个父节点的值,都大于或等于它左、右孩子节点的值。 最小堆:任何一个父节点的值,都小于或等于它左、右孩子节点的值。 二叉堆的根...
  • 二叉堆(c++实现)

    2020-05-17 14:20:50
    二叉堆的介绍 二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:最大堆和最小堆。 最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一...
  • 二叉堆是一种特殊的堆,其特点是堆顶的元素为整个堆的最大值或最小值。因此,堆有两种实现形式,一种是最大堆,另一种是最小堆。本文采用C风格实现最基础的堆操作,并分析其时间复杂度。
  • 二叉堆简单实现与应用
  • 二叉查找树和二叉堆

    2021-02-24 10:49:21
     二叉查找树(Binary Search Tree),又称为有序二叉树,排序二叉树,满足以下性质: 1)没有键值相等的节点。 2)若左子树不为空,左子树上节点值均小于根节点的值。 3)若右子树不为空,右子树上节点值均大于根...
  • python二叉堆

    万次阅读 2020-08-31 16:09:32
    二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值; 最小堆:父结点的键值总是小于...
  • Java实现二叉堆

    2020-11-25 18:26:00
    二叉堆中,从任意结点向上,我们都能得到一列非递减的元素;从任意结点向下,我们都能得到一列非递增的元素。 在堆中有两个特殊的操作: 上浮(swim): 如果堆的有序状态因为某个结点变得比它父结点更大而打破...
  • 1 二叉堆的原理 1.1 关键概念 属于完全二叉树,一般用数组表示。 又名优先队列,满足一般队列的操作原则:出队(删除)操作在队头(堆顶),入队(插入)操作在队尾(堆底)。 分为大根堆和小根堆,前者需满足堆中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,084
精华内容 17,633
关键字:

二叉堆

友情链接: addendum_bkz7qx.rar