C# 排序算法之堆排序

 更新时间:2010年09月28日 09:12:54   作者:  
这里是指一种数据结构,而不是我们在C#中提到的用于存储引用类型对象的地方。它可以被当成一棵完全二叉树。
一、基本概念

堆:这里是指一种数据结构,而不是我们在C#中提到的用于存储引用类型对象的地方。它可以被当成一棵完全二叉树。
binaryTree 
为了将堆用数组来存放,这里对每个节点标上顺序。事实上,我们可以用简单的计算公式得出父节点,左孩子,右孩子的索引:

    parent(i) = clip_image002[4]

    left(i) = 2i

    right(i)=2i + 1

最大堆和最小堆: 最大堆是指所有父节点的值都大于其孩子节点的堆,即满足以下公式:

                      A[parent[i]]clip_image002[6]A[i](A是指存放该堆的数组)

    最小堆相反。

    最大堆和最小堆是堆排序的关键,可知最大堆的根节点是堆中最大的节点。因此只要我们构造出最大(小)堆,最大(小)的元素也就得到了,然后再对剩下的元素继续构造最大(小)堆,就可以取出第二大(小)的元素,依此类推,直到排序完成。

二、构造最大(小)堆
我们已经得知构造最大(小)堆是堆排序的关键,下面就来看看如何构造最大堆。
万事开头难,首先来看一种特殊的情形吧:堆的根节点的左子树和右子树都已经是最大堆了,然而根节点却比孩子节点小,当然,这个堆不满足最大堆的定义。为了⑩这个堆成为最大堆,我们可以按如下步骤操作:
(1)将根节点与左右孩子中最大的交换
(2)交换之后可能会面临左或右子树不是最大堆的问题,但由于整个左(右)子树一开始就是最大堆,问题又回到了最开始的状态,因此只要如此反复即可得到最大堆。
对于上面的特殊堆已经找到了解决办法,但对于一般意义上的堆呢?
我们可以选择自底向上来构造:叶子节点是特殊的最大堆,举个例子有叶子节点a,b,它们的父节点是p;a,b肯定已经是最大堆了,这是要保证a,b,p组成的子树是最大堆。这个堆很眼熟是不是?没错,它就是前面提到的特殊的堆。在a,b,p组成的子树变成最大堆后,我们又可以类似的使该子树,该子树的父节点,以及同胞子树(或节点)组成的新子树成为最大堆,如此类推,最终使堆变为最大堆。

对于求解最小堆与此类似。
三、实现
完整代码:
复制代码 代码如下:

namespace HeapSort
{
using System;
class Program
{
static int heapSize =0;
static void Main(string[] args)
{
var heap = new[] { -1, 10, 5, 12, 77, 54, 7, 34, 23, 11 };//为了方便,索引0处不存放元素(或存放无用元素)
heapSize = heap.Length - 1;
BuildMaxHeap(heap);
for (var i = heap.Length - 1; i >= 2; i--)
{
//1.每次在构建好最大堆后,将第一个元素和最后一个元素交换;
//2.第一次以索引1到length-1出的元素组成新的堆,第二次1到length-2,直到剩下最后两个元素组成堆
//3.每次新组成的堆除了根节点其他节点都能保持最大堆的特性,因此只要DoBuildMaxHeap(heap, 1)就可以得到新的最大堆
Swap(heap, 1, i);
heapSize--;
MaxHeapfy(heap, 1);
}
foreach (var i in heap)
Console.Write(i + " ");
}
static void BuildMaxHeap(int[] heap)
{
for (var i = (heap.Length - 1) / 2; i >= 1; i--)
{
MaxHeapfy(heap, i);
}
}
static void MaxHeapfy(int[] heap, int index)
{
var largerItemIndex = index;
var leftChildIndex = index << 1;
var rightChildIndex = (index<<1) + 1;
if (leftChildIndex <= heapSize && heap[leftChildIndex] > heap[index])
{
largerItemIndex = leftChildIndex;
}
if (rightChildIndex <= heapSize && heap[rightChildIndex] > heap[largerItemIndex])
{
largerItemIndex = rightChildIndex;
}
if( index != largerItemIndex)
{
Swap(heap, index, largerItemIndex);
MaxHeapfy(heap, largerItemIndex);
}
}
static void Swap(int[] heap, int index1, int index2)
{
var temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
}
}

1.MaxHeapfy:该方法的前提是index处节点的左右子树已经是最大堆,最终的目的是使以index处节点为根的堆成为最大堆

2.BuildMaxHeap:该方法涉及一个事实:如果一个对含n个元素,那么从clip_image002开始的元素(假设节点下表从1开始)就一定是叶子节点(这一点可以用反证法证明,假设clip_image002[4]处节点不是叶子节点,那么该节点必包含子节点,从而可以得出其左孩子的索引2 *(clip_image002[6]) > n的结论,显然这是错误的)。在这个前提下,该方法至底向上通过MaxHeapfy将堆构建成最大堆。

相关文章

  • C#使用Object类实现栈的方法详解

    C#使用Object类实现栈的方法详解

    这篇文章主要介绍了C#使用Object类实现栈的方法,详细分析了栈的原理及使用Object类实现栈的相关技巧与注意事项,需要的朋友可以参考下
    2016-06-06
  • c#操作sql server2008 的界面实例代码

    c#操作sql server2008 的界面实例代码

    这篇文章主要介绍了c#操作sql server2008 的界面实例代码,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-03-03
  • C#递归算法之分而治之策略

    C#递归算法之分而治之策略

    分而治之的策略主要是将大量复杂的问题分成多个子问题,解决各个子问题,从而解决原问题,下面就让我们看看具体如何实现。
    2016-06-06
  • c# FTP上传文件实例代码(简易版)

    c# FTP上传文件实例代码(简易版)

    下面小编就为大家分享一篇c# FTP上传文件的实例代码,超简单哦~希望对大家有所帮助。一起跟随小编过来看看吧,
    2017-12-12
  • C#以流方式读socket超时设置的实例

    C#以流方式读socket超时设置的实例

    这篇文章主要为大家详细介绍了C#以流方式读socket超时设置的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • C# Oracle数据库操作类实例详解

    C# Oracle数据库操作类实例详解

    这篇文章主要介绍了C# Oracle数据库操作类实例,进行数据库操作时很有实用价值,需要的朋友可以参考下
    2014-07-07
  • 100行C#代码实现经典扫雷游戏

    100行C#代码实现经典扫雷游戏

    这篇文章主要为大家详细介绍了如何用100行C#代码实现经典的扫雷游戏,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-02-02
  • C#中IDispose接口的实现及为何这么实现详解

    C#中IDispose接口的实现及为何这么实现详解

    这篇文章主要给大家介绍了关于C#中IDispose接口的实现及为何这么实现的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • unity3d实现七天签到功能

    unity3d实现七天签到功能

    这篇文章主要为大家详细介绍了unity3d实现七天签到功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • C#实现图形路径变换的方法

    C#实现图形路径变换的方法

    这篇文章主要介绍了C#实现图形路径变换的方法,涉及C#针对图形路径的相关操作技巧,需要的朋友可以参考下
    2015-06-06

最新评论