c# 实现堆排序
整体思路:利用堆的数据结构,每次都从中取出根节点,完成整体排序.
1堆的数据结构:
特点是,根是一棵完全二叉树,上边根节点的值要大于其左右子树所有节点,
其最重要的特征是:从根到叶的任一条路径都满足单调性,如果一颗完全二叉树满足这个特性,
那它也是一个堆.也正是依照这个特性,可将一颗形式上的完全二叉树调整成为堆,堆的重要操作有删除头结点,插入一个结点:
下面定义了一个最大堆
1 public class MaxHeap 2 { 3 private List<int> m_nums { get; set; } 4 //构造函数初始化内部数据,同时给出哨兵. 5 public MaxHeap() 6 { 7 m_nums = new List<int>(); 8 m_nums.Add(int.MaxValue); 9 } 10 //插入操作 11 public void Insert(int item) 12 { 13 //首先放到最后 14 int i = m_nums.Count(); 15 m_nums.Add(item); 16 //跟父节点进行比较 17 for (; item>m_nums[i/2]; i/=2) 18 { 19 //父节点下移 20 m_nums[i] = m_nums[i / 2]; 21 } 22 m_nums[i] = item; 23 } 24 //删除操作 25 public int Delete() 26 { 27 if (m_nums.Count <= 1) 28 { 29 throw new Exception("heap is zero"); 30 } 31 int result = m_nums[1]; 32 //将最后的元素替换到根节点 33 m_nums[1] = m_nums.Last(); 34 m_nums.RemoveAt(m_nums.Count()-1); 35 //从根节点开始,依次与左右孩子作比较,将较大者浮上来 36 int i = 1; 37 while (true) 38 { 39 //判断返回条件 40 if (IsBiggerThanChild(ref i)) 41 break; 42 } 43 return result; 44 } 45 46 private bool IsBiggerThanChild(ref int i) 47 { 48 //没有孩子的情况 49 if (2 * i >= m_nums.Count()) 50 { 51 return true; 52 } 53 //只有左孩子的情况 54 else if (2 * i + 1 >= m_nums.Count()) 55 { 56 if (m_nums[i] >= m_nums[2 * i]) 57 return true; 58 else 59 { 60 var temp = m_nums[i]; 61 m_nums[i] = m_nums[2 * i]; 62 m_nums[2 * i ] = temp; 63 i = 2 * i; 64 } 65 } 66 //左右孩子都有的情况 67 else 68 { 69 if (m_nums[i] >= Math.Max(m_nums[2 * i], m_nums[2 * i + 1])) 70 return true; 71 int bigChildIndex = (m_nums[2 * i] >= m_nums[2 * i + 1]) ? 2 * i : 2 * i + 1; 72 var temp = m_nums[i]; 73 m_nums[i] = m_nums[bigChildIndex]; 74 m_nums[bigChildIndex] = temp; 75 i = bigChildIndex; 76 } 77 return false; 78 } 79 }
有了 堆这个强大的数据结构,排序就很简单了
public static void Heap_Sort(int[] nums) { int length = nums.Length; MaxHeap maxHeap = new MaxHeap(); for (int i = 0; i < length; i++) { maxHeap.Insert(nums[i]); } for (int i = 0; i < length; i++) { nums[length - 1 - i] = maxHeap.Delete(); } }