C#数据结构之最小堆的实现方法
作者:鹅厂程序小哥 发布时间:2023-07-15 01:59:10
最小堆
基本思想:堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最小(大)堆,依次类推,最终得到排序的序列。
堆排序分为大顶堆和小顶堆排序。大顶堆:堆对应一棵完全二叉树,且所有非叶结点的值均不小于其子女的值,根结点(堆顶元素)的值是最大的。而小顶堆正好相反,小顶堆:堆对应一棵完全二叉树,且所有非叶结点的值均不大于其子女的值,根结点(堆顶元素)的值是最小的。
举个例子:
(a)大顶堆序列:(96, 83,27,38,11,09)
(b)小顶堆序列:(12,36,24,85,47,30,53,91)
实现堆排序需解决两个问题:
1. 如何将n 个待排序的数建成堆?
2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆?
首先讨论第二个问题:输出堆顶元素后,怎样对剩余n-1元素重新建成堆?
调整小顶堆的方法:
1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。
2)将根结点与左、右子树中较小元素的进行交换。
3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).
4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).
5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。
称这个自根结点到叶子结点的调整过程为筛选。如图:
再讨论第一个问题,如何将n 个待排序元素初始建堆?
建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。
1)n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。
2)筛选从第n/2个结点为根的子树开始,该子树成为堆。
3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)
C#算法实现:
using System;
using System.Collections.Generic;
namespace StructScript
{
/// <summary>
/// 最小堆实现
/// </summary>
/// <typeparam name="T"></typeparam>
public class BinaryHeap<T>
{
//默认容量为6
private const int DEFAULT_CAPACITY = 6;
private int mCount;
private T[] mItems;
private Comparer<T> mComparer;
public BinaryHeap() : this(DEFAULT_CAPACITY) { }
public BinaryHeap(int capacity)
{
if (capacity < 0)
{
throw new IndexOutOfRangeException();
}
mItems = new T[capacity];
mComparer = Comparer<T>.Default;
}
/// <summary>
/// 增加元素到堆,并从后往前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Enqueue(T value)
{
if (mCount == mItems.Length)
{
ResizeItemStore(mItems.Length * 2);
}
mItems[mCount++] = value;
int position = BubbleUp(mCount - 1);
return (position == 0);
}
/// <summary>
/// 取出堆的最小值
/// </summary>
/// <returns></returns>
public T Dequeue()
{
return Dequeue(true);
}
private T Dequeue(bool shrink)
{
if (mCount == 0)
{
throw new InvalidOperationException();
}
T result = mItems[0];
if (mCount == 1)
{
mCount = 0;
mItems[0] = default(T);
}
else
{
--mCount;
//取序列最后的元素放在堆顶
mItems[0] = mItems[mCount];
mItems[mCount] = default(T);
// 维护堆的结构
BubbleDown();
}
if (shrink)
{
ShrinkStore();
}
return result;
}
private void ShrinkStore()
{
// 如果容量不足一半以上,默认容量会下降。
if (mItems.Length > DEFAULT_CAPACITY && mCount < (mItems.Length >> 1))
{
int newSize = Math.Max(
DEFAULT_CAPACITY, (((mCount / DEFAULT_CAPACITY) + 1) * DEFAULT_CAPACITY));
ResizeItemStore(newSize);
}
}
private void ResizeItemStore(int newSize)
{
if (mCount < newSize || DEFAULT_CAPACITY <= newSize)
{
return;
}
T[] temp = new T[newSize];
Array.Copy(mItems, 0, temp, 0, mCount);
mItems = temp;
}
public void Clear()
{
mCount = 0;
mItems = new T[DEFAULT_CAPACITY];
}
/// <summary>
/// 从前往后依次对各结点为根的子树进行筛选,使之成为堆,直到序列最后的节点
/// </summary>
private void BubbleDown()
{
int parent = 0;
int leftChild = (parent * 2) + 1;
while (leftChild < mCount)
{
// 找到子节点中较小的那个
int rightChild = leftChild + 1;
int bestChild = (rightChild < mCount && mComparer.Compare(mItems[rightChild], mItems[leftChild]) < 0) ?
rightChild : leftChild;
if (mComparer.Compare(mItems[bestChild], mItems[parent]) < 0)
{
// 如果子节点小于父节点, 交换子节点和父节点
T temp = mItems[parent];
mItems[parent] = mItems[bestChild];
mItems[bestChild] = temp;
parent = bestChild;
leftChild = (parent * 2) + 1;
}
else
{
break;
}
}
}
/// <summary>
/// 从后往前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点
/// </summary>
/// <param name="startIndex"></param>
/// <returns></returns>
private int BubbleUp(int startIndex)
{
while (startIndex > 0)
{
int parent = (startIndex - 1) / 2;
//如果子节点小于父节点,交换子节点和父节点
if (mComparer.Compare(mItems[startIndex], mItems[parent]) < 0)
{
T temp = mItems[startIndex];
mItems[startIndex] = mItems[parent];
mItems[parent] = temp;
}
else
{
break;
}
startIndex = parent;
}
return startIndex;
}
}
}
附上,测试用例:
using System;
namespace StructScript
{
public class TestBinaryHeap
{
static void Main(string[] args)
{
BinaryHeap<int> heap = new BinaryHeap<int>();
heap.Enqueue(8);
heap.Enqueue(2);
heap.Enqueue(3);
heap.Enqueue(1);
heap.Enqueue(5);
Console.WriteLine(heap.Dequeue());
Console.WriteLine(heap.Dequeue());
Console.ReadLine();
}
}
}
测试用例,执行结果依次输出1,2。
总结
来源:https://blog.csdn.net/qq826364410/article/details/79770791
猜你喜欢
- 启动第二个服务时就会报如下的错误:Server failed to start for port 8080: Address already
- 前天在做批量数据导入新增时,要对数据进行有效性判断,其中还要去除重复,如果没出现linq的话可能会新声明一个临时对象集合,然后遍历原始数据判
- 开发过程中,如果使用mybatis做为ORM框架,经常需要打印出完整的sql语句以及执行的结果做为参考。虽然mybatis结合日志框架可以做
- 会话技术会话:一次会话中包含多次请求和响应。一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止功能:在一次会话的范围内
- 1. 启动入口本系列RocketMQ4.8注释github地址,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈前面我们已经分析完
- 代码如下:using System; using System.Collections.Generic; using S
- 本文实例为大家分享了Java实现聊天机器人完善版的具体代码,供大家参考,具体内容如下Client代码:package GUISocket.c
- Java序列化JSON时long型数值,会出现精度丢失的问题。原因:java中得long能表示的范围比js中number大,也就意味着部分数
- 前言最近在维护一个运营端的系统,和前端联调的过程中,经常需要排查一些交互上的问题,每次都得看前端代码的传参和后端代码的出参,于是打算给HTT
- 本文介绍restTemplate基础用法。Java中get和post的用法请参考:一文带你搞懂Java中Get和Post的使用1 提供get
- 登录接口实现public User queryUser(String UserName, String Password,HttpServl
- 传播inbound事件有关于inbound事件, 在概述中做过简单的介绍, 就是以自己为基准, 流向自己的事件, 比如最常见的channel
- 废话目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以。SpringBoo
- JUnit是Java中最有名的单元测试框架,用于编写和运行可重复的测试,多数Java的开发环境都已经集成了JUnit作为单元测试的工具。好的
- handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段。使用起来很简单,就两个步骤,在主线程重写handler的handl
- 使用flatMap列出子目录前面已经看到如何列出指定目录下的文件了。我们再来看下如何遍历指定目录的直接子目录(深度为1),先实现一个简单的版
- 前言相信很多人选择Spring Boot主要是考虑到它既能兼顾Spring的强大功能,还能实现快速开发的便捷。我们在Spring Boot使
- 最近由于工作原因,没时间更新,开始吧~~关于json的返回需要用到一个工具包来将书转换为json格式,在此用到的jar包为: im
- protobuf对象不能直接使用jsonlib去转,因为protobuf生成的对象的get方法返回的类型有byte[],而只有String类
- 本文实例为大家分享了Spring MVC多文件上传的具体代码,供大家参考,具体内容如下1)创建工程并导入JAR包2)创建多文件选择页面在 W