堆排序

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 

 

posted @ 2011-03-16 20:54  qiang.xu  阅读(1411)  评论(0编辑  收藏  举报