服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#数据结构之最小堆的实现方法

C#数据结构之最小堆的实现方法

2022-11-01 14:43鹅厂程序小哥 C#

这篇文章主要给大家介绍了关于C#数据结构之最小堆的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

最小堆

基本思想:堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最小(大)堆,依次类推,最终得到排序的序列。

堆排序分为大顶堆和小顶堆排序。大顶堆:堆对应一棵完全二叉树,且所有非叶结点的值均不小于其子女的值,根结点(堆顶元素)的值是最大的。而小顶堆正好相反,小顶堆:堆对应一棵完全二叉树,且所有非叶结点的值均不大于其子女的值,根结点(堆顶元素)的值是最小的。

举个例子:

(a)大顶堆序列:(96, 83,27,38,11,09)

(b)小顶堆序列:(12,36,24,85,47,30,53,91)

C#数据结构之最小堆的实现方法

实现堆排序需解决两个问题:

  1. 如何将n 个待排序的数建成堆?

  2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆?

首先讨论第二个问题:输出堆顶元素后,怎样对剩余n-1元素重新建成堆?

调整小顶堆的方法:

  1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。

  2)将根结点与左、右子树中较小元素的进行交换。

  3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).

  4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).

  5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。

  称这个自根结点到叶子结点的调整过程为筛选。如图:

C#数据结构之最小堆的实现方法

再讨论第一个问题,如何将n 个待排序元素初始建堆?

  建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。

  1)n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。

  2)筛选从第n/2个结点为根的子树开始,该子树成为堆。

  3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

  如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)

C#数据结构之最小堆的实现方法

C#数据结构之最小堆的实现方法

C#算法实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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;
  }
 }
}

附上,测试用例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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。

总结

到此这篇关于C#数据结构之最小堆实现的文章就介绍到这了,更多相关C#数据结构最小堆实现内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq826364410/article/details/79770791

延伸 · 阅读

精彩推荐
  • C#c#基础系列之ref和out的深入理解

    c#基础系列之ref和out的深入理解

    有过C#基础知识的都应该清楚Ref和Out的使用方法,所以下面这篇文章主要给大家介绍了关于c#基础系列之ref和out的相关资料,文中通过示例代码介绍的非常详...

    大菜5392022-03-01
  • C#深入分析C# 线程同步

    深入分析C# 线程同步

    这篇文章主要介绍了C# 线程同步的的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    JoeSnail3402022-09-15
  • C#C#中读写INI配置文件的方法

    C#中读写INI配置文件的方法

    这篇文章主要介绍了C#中读写INI配置文件的方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下...

    郝光明7032022-02-25
  • C#VisualStudio2019安装C#环境的实现方法

    VisualStudio2019安装C#环境的实现方法

    这篇文章主要介绍了VisualStudio2019安装C#环境的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    Cauhele浅能7922022-10-25
  • C#C#实现char字符数组与字符串相互转换的方法

    C#实现char字符数组与字符串相互转换的方法

    这篇文章主要介绍了C#实现char字符数组与字符串相互转换的方法,结合实例形式简单分析了C#字符数组转字符串及字符串转字符数组的具体实现技巧,需要的朋...

    Mr-Robot11472021-12-23
  • C#C#如何给枚举类型增加一个描述特性详解

    C#如何给枚举类型增加一个描述特性详解

    这篇文章主要给大家介绍了关于C#如何给枚举类型增加一个描述特性的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    潇十一郎4362022-03-09
  • C#浅析C# 使用Process调用外部程序中所遇到的参数问题

    浅析C# 使用Process调用外部程序中所遇到的参数问题

    这篇文章主要介绍了C# 使用Process调用外部程序中所遇到的参数问题,需要的朋友可以参考下...

    暗暗大人12282021-12-29
  • C#详解c# 多态

    详解c# 多态

    这篇文章主要介绍了c# 多态的相关资料,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    菜鸟教程9512022-09-27