堆排序

1. 堆简单介绍,数据存储及堆上定义的操作

2. 堆排序简单实现及算法的时间复杂度

3. 代码下载

 

1. 堆简单介绍,数据存储及堆上的定义的操作

二叉堆在本文中使用数组(.net中的List)来存储,它可以完全被看作是一颗二叉树。

 

除了叶子节点外,其他每层都是满的。二叉堆可以分为最大堆和最小堆。最大堆定义如下:

 

有定义可知,最大堆中中根元素是最大的。最小堆定义与最大堆定义正好相反,最小堆中根元素是最小元素。下面定义堆上的操作。

1. 给定某个节点下标i,该节点的父节点PARENT(i) = floor(i / 2)

2. 给定某个节点下标i,该节点的左孩子LEFT(i) = 2 * i

3. 给定某个节点下标i,该节点的右孩子RIGHT(i) = 2 × i + 1;

4. 定义操作MaxHeapfiy,该函数保持堆栈特性,时间负责度lg(n)

5. 定义堆排序HeapSort,时间复杂度nlg(n)

6. 下面定义有限队列操作。HeapMaxium返回堆栈中最大元素

7. HeapExtractMax函数返回堆中最大元素,同时将最大元素从堆栈中删除

8. HeapInsert向堆栈中插入一个元素

9. HeapIncreaseKeyValue增加堆中某个元素的值 

2. 堆排序的简单实现及算法的时间复杂度

下面是上面定义操作的伪代码实现:

 MaxHeapfiy:

BuildMaxHeap: 

HeapSort:

 

实现代码如下: 

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Alice.DataStructure
{
     public  class Heap
    {
         //  数据存储 
         private List< int> heapData =  new List< int>();
         private  int length;
 
#region 构造函数
         public Heap(List< int> data)
        {
             this.heapData = data;
             this.length = data.Count;
        }
        
         //  默认构造函数
         public Heap()
        {
             this.length =  0;
        }
#endregion

#region 堆上操作
         //  父节点下标
         public  int Parent( int i)
        {
             //  向下取整
             return ( int)(i /  2);
        }

         //  左孩子下标
         public  int Left( int i)
        {
             //  由于list的第一个元素的下标是0
             return ( 2 * i) +  1;
        }

         //  右孩子下标
         public  int Right( int i)
        {
             //  由于list的第一个元素的下标是0
             return ( 2 * i +  2);

        }


         //  建立最大堆,这里假设left和right对应的子树已经
        
//  是最大堆
         public  void MaxHeapify( int i)
        {
             int left =  this.Left(i);
             int right =  this.Right(i);
             //  递归出口条件
            
//  1. left超界,return
            
//  2. left未超界,right超界,继续
            
//  3. left未超界,right未超界,继续
             if ((left > ( this.length -  1)))
                 return;

             //  查找i,left,right中的最大值
             int largest = i;         //  largest最大元素下标
            
//  这里需要加上限制条件来判断是否能够使用left和right
             if ( (left <=  this.length -  1) &&  ( this.heapData[left] >  this.heapData[largest]))
                largest = left;
             if ( (right <=  this.length -  1) && ( this.heapData[right] >  this.heapData[largest]))
                largest = right;

             //  递归调用
             int tmp;
             if (largest != i)
            {
                 //  交换largest和i的值
                tmp =  this.heapData[largest];
                 this.heapData[largest] =  this.heapData[i];
                 this.heapData[i] = tmp;

                 //  递归调用
                
                 this.MaxHeapify(largest);
            }
        }

         //  建立最大堆,调用MaxHeapify
         public  void BuildMaxHeap()
        {
             //  从数组的最后位置开始计算
             int last = ( this.length +  1) /  2 -  1;
             for ( int i = last; i >=  0; --i)
            {
                 this.MaxHeapify(i);
            }
        }


         //  堆排序
         public  void HeapSort()
        {
             this.BuildMaxHeap();
            Console.WriteLine( this.ToString());

             int tmp;
             //  交换第一个元素和最后一个元素
             for ( int i =  0this.length >  0; ++i )
            {
                 //  交换第一个元素和最后一个元素
                tmp =  this.heapData[ 0];
                 this.heapData[ 0] =  this.heapData[ this.length -  1];
                 this.heapData[ this.length -  1] = tmp;

                 //  堆长度减少1
                 this.length--;
                 //  保持堆特性
                 this.MaxHeapify( 0);
            }
        }

         //  输出堆栈中内容
         public  override  string ToString()
        {
            System.Text.StringBuilder builder = 
                 new System.Text.StringBuilder();
             for ( int i =  0; i <  this.length -  1; ++i )
            {
                builder.Append( this.heapData[i] +  " \t ");
            }

             return builder.ToString();
        }
#endregion

#region 优先队列操作
         //  仅仅是返回最大堆中的最大元素
         public  int HeapMaximum()
        {
             return  this.heapData[ 0];
        }

         //  提取最大堆的最大元素,并将该元素删除
         public  int HeapExtractMax()
        {
             //  得到最大值
             int max =  this.heapData[ 0];

             //  交换最后一个和第一个元素
             this.heapData[ 0] =  this.heapData[ this.length -  1];
             this.length--;
            
             //  保持堆特性
             this.MaxHeapify( 0);

             //  返回最大值
             return max;
        }

         //  增加堆中第i个元素的值为val 
         public  void  HeapIncreaseKeyValue( int i,  int val)
        {
             if (val <  this.heapData[i])
                 return;
             this.heapData[i] = val;

             //  保持堆特性
             int tmp;
             //  
             while ( (i >=  0) && 
                ( this.heapData[i] >  this.heapData[ this.Parent(i)]))
            {
                tmp =  this.heapData[i];
                 this.heapData[i] =  this.heapData[ this.Parent(i)];
                 this.heapData[ this.Parent(i)] = tmp;
                
                i =  this.Parent(i);
            }
        }

         //  向堆大队中插入元素val
         public  void HeapInsert( int val)
        {
             this.length++;
             this.heapData.Add( int.MinValue);

             this.HeapIncreaseKeyValue(( this.length -  1), val);
        }
#endregion

         public  int Get( int i)
        {
             if (i >  this.length -  1)
                 throw  new Exception();

             return  this.heapData[i];
        }
    }

}  

复制代码

 其中需要注意的是:

1. 如何对所有情况进行分类讨论。

2. 对于函数传递的参数,不去假设出入参数的可用性,需要进行参数的检查。

3. 递归算法中如何确定递归的终止条件:只能通过简单的实例来验证,然后debug,最终确定递归终止条件。

 

3. 代码下载

/Files/xuqiang/Heap.rar 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值