数据结构之大/小顶堆--C#版

面试考察频率:⭐⭐⭐⭐

什么是大/小顶堆?

大/小根堆的实现可以看似为一颗完全二叉树,但和完全二叉树还是有区别的(具体的完全二叉树在之后会讲。
堆顶元素为整个堆的最大/小元素。

使用情景?

局部元素排序、实现优先队列、SPFA优化

如何来实现?

构建思路如下
⚪操作只有Push和Pop。
⚪文中通过数组来实现,本文主要讲解大顶堆(小顶堆同理),只要任意保证子节点小于父节点就可以。存储可以从下标从0开始或下标从1开始,个人更偏向于使用从1开始存储的方式操作更方便。
⚪从0开始存储时,获取子节点的公式为:左孩子为parent2+1,右孩子为parent2+2。 获取父节点的公式为:(child-1)/2
⚪从1开始存储时,获取子节点的公式为:左孩子为parent2,右孩子为parent2+1。 获取父节点的公式为:child/2。
在这里插入图片描述
基础结构表示:

        int[] heap = new int[1000];
        int heapSize = 0;




Push元素:

        public void Push(int x)
        {
            //思路:每次先将新元素放到最后,在进行调整。从下向上,比较父元素
            heap[++heapSize] = x;
            int now = heapSize, nxt = 0;

            while (now > 1)
            {
                nxt = now / 2;
                if (heap[now] <= heap[nxt]) break;
                Swap(ref heap[now], ref heap[nxt]);
                now = nxt;
            }
        }
每次讲新元素放到堆的最后,在从下向上进行调整。
如果父节点比子节点小就交换当前子节点和父节点,为了始终保持父节点大于子节点。
如果出现父节点大于子节点的情况退出循环。

Pop元素:

        public int Pop()
        {
            //思路:把顶的元素先存到零时变量中,在将堆末尾的元素覆盖到顶元素完成删除。之后再由顶 
            //      向下进行调整。上一个元素比较子两个元素,其中要比较两个子元素的大小,选择最大/ 小的与上边的元素进行交换
            int res = heap[1];
            int now = 1, nxt = 0;
            heap[1] = heap[heapSize--];

            while (now * 2 <= heapSize)
            {
                nxt = now * 2;
                if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
                if (heap[nxt] <= heap[now])
                {
                    return res;
                }
                Swap(ref heap[nxt], ref heap[now]);
                now = nxt;
            }
            return res;
        }
每次将堆顶元素保存到临时变量中(因为堆顶元素要Pop出去,最后还要返回堆顶元素所以需要保存下来)。
之后把堆尾元素覆盖到堆顶元素去,再从上到下调整整个堆。
其中要注意,由于子节点有两个,一定要选最大的进行于父节点交换。所以注意这个情况的判定。

完整代码如下,更多数据C#数据结构源码欢迎浏览我的仓库:https://github.com/w199753/DataStructural-CSharp 最后附上完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataStructural
{
    /// <summary>
    /// 大根堆。小根堆实现方法相同
    /// </summary>
    public class MaxHeap
    {
        int[] heap = new int[1000];
        int heapSize = 0;

        /// <summary>
        /// 添加元素
        /// </summary>
        /// <param name="x"></param>
        public void Push(int x)
        {
            //思路:每次先将新元素放到最后,在进行调整。从下向上,比较父元素
            heap[++heapSize] = x;
            int now = heapSize, nxt = 0;

            while (now > 1)
            {
                nxt = now / 2;
                if (heap[now] <= heap[nxt]) break;
                Swap(ref heap[now], ref heap[nxt]);
                now = nxt;
            }
        }

        /// <summary>
        /// 删除元素并返回最大值
        /// </summary>
        /// <returns></returns>
        public int Pop()
        {
            //思路:把顶的元素先存到零时变量中,在将堆末尾的元素覆盖到顶元素完成删除。之后再由顶 
            //      向下进行调整。上一个元素比较子两个元素,其中要比较两个子元素的大小,选择最大/ 小的与上边的元素进行交换
            int res = heap[1];
            int now = 1, nxt = 0;
            heap[1] = heap[heapSize--];

            while (now * 2 <= heapSize)
            {
                nxt = now * 2;
                if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
                if (heap[nxt] <= heap[now])
                {
                    return res;
                }
                Swap(ref heap[nxt], ref heap[now]);
                now = nxt;
            }
            return res;
        }

        public void Clear()
        {
            heap = new int[1000];
            heapSize = 0;
        }
        private void Swap(ref int a, ref int b)
        {
            a ^= b;
            b ^= a;
            a ^= b;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/w199753/p/12443097.html