堆排序
堆排序相对冒泡这些要复杂一些,它需要先初始化堆。.net里List的排序就混合使用了堆排序和另2种排序。
出于学习目的,代码示范里不使用数组结构,数组取索引比较深涩。而使用嵌套类来实现。
1.初始化堆
排序肯定是有升序和降序两种,堆排序也一样,分为大顶堆和小顶堆。初始化堆的目的就是变为大顶堆或者小顶堆
传统的方法是取数组下标[n/2]向下取整,但直接取最后一个元素往前遍历也是可行的
下面用22,44,6,88,5几个数作为示范
step1.默认的树结构
step2.自顶向下,以22开始先比较44和6两个子树,比较方式可以先比较左子树和右子树,取最大的值再和父节点比较。
44和6先进行比较,44比较大。然后44再和22进行比较,发现比父节点大。然后执行交换
step3.然后比较22的两个子树,88比较大,执行交换
step4.由于内部执行了交换,再次进行遍历。发现88比44大,执行交换。
至此,初始化堆完成
2.执行排序
堆排序有一个有序区和无序区的概念,有序区同样在堆中,但是不参与排序。只有无序区参与排序,排序结果转移到有序区。
直到无序区没有之后,排序结束
step1.是初始化好的堆,没有进行任何操作
step2.需要不断的把第一个元素和最后一个元素进行交换,放入有序区,橙色的是有序区
step3.交换完成之后,再重复进行初始化堆,进行调整
step4.由于88是有序区的内容,所以只和左子树22比较后,进行交换。
step5.再次和第一个元素进行交换,加入到有序区。
堆排序完成
下面附上代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Hont { public class HeapSort { public class Node { /// <summary> /// 用了一下空对象模式,省去了判断null /// </summary> public readonly static Node EMPTY_NODE; public Node LeftChild { get; set; } public Node RightChild { get; set; } public Node Parent { get; set; } public int Value { get; set; } public bool IsUsed { get; set; } static Node() { EMPTY_NODE = new Node() { Value = 0, IsUsed = true }; } public Node() { LeftChild = EMPTY_NODE; RightChild = EMPTY_NODE; } public Node(Node parent) : this() { this.Parent = parent; } } /// <summary> /// 执行排序 /// </summary> /// <param name="sourceArray">原始数组</param> /// <param name="maxOrMin">降序还是升序</param> /// <returns>排序后的数组</returns> public int[] Sort(int[] sourceArray, bool maxOrMin = true) { Func<int, int, bool> compare = null; if (maxOrMin) compare = (a, b) => a > b; else compare = (a, b) => a < b; var rootNode = BuildHeap(sourceArray.ToList()); AdjustHeap(rootNode, compare); SortHeap(rootNode, compare); return OutputHeap(rootNode).ToArray(); } /// <summary> /// 构建堆,因为没有用数组去实现,需要额外构建 /// </summary> Node BuildHeap(List<int> sourceList) { Node result = new Node(); List<Node> nextDepthNodeList = new List<Node>(); List<Node> tmpNextDepthNodeList = new List<Node>(); BuildHeap(result, sourceList, nextDepthNodeList); for (int i = 0; i < nextDepthNodeList.Count; i++) { var item = nextDepthNodeList[i]; if (!BuildHeap(item, sourceList, tmpNextDepthNodeList)) { break; } if (i - 1 == nextDepthNodeList.Count) { nextDepthNodeList = tmpNextDepthNodeList; tmpNextDepthNodeList.Clear(); i = 0; } } var lastNode = GetLastSortNode(result); result.Value = lastNode.Value; if(lastNode.Parent.LeftChild == lastNode) { lastNode.Parent.LeftChild = Node.EMPTY_NODE; } else { lastNode.Parent.RightChild = Node.EMPTY_NODE; } return result; } bool BuildHeap(Node currentNode, List<int> sourceList, List<Node> nextDepthNodeList) { if (sourceList.Count < 1) return false; var firstNode = sourceList[0]; currentNode.LeftChild = new Node(currentNode) { Value = firstNode }; nextDepthNodeList.Add(currentNode.LeftChild); sourceList.RemoveAt(0); if (sourceList.Count > 0) { var lastNode = sourceList[sourceList.Count - 1]; currentNode.RightChild = new Node(currentNode) { Value = lastNode }; nextDepthNodeList.Add(currentNode.RightChild); sourceList.RemoveAt(sourceList.Count - 1); } return true; } /// <summary> /// 调整堆,调整至大顶堆或小顶堆,依据第二个参数 /// </summary> void AdjustHeap(Node heap, Func<int, int, bool> compare) { if (heap == Node.EMPTY_NODE) return; if (heap.LeftChild.IsUsed && heap.RightChild.IsUsed) return; var leftChild = heap.LeftChild; var rightChild = heap.RightChild; var leftChildValue = heap.LeftChild.Value; var rightChildValue = heap.RightChild.Value; if (leftChild.IsUsed) leftChildValue = compare(int.MinValue, int.MaxValue) ? int.MaxValue : int.MinValue; if (rightChild.IsUsed) rightChildValue = compare(int.MinValue, int.MaxValue) ? int.MaxValue : int.MinValue; var selectChild = compare(leftChildValue, rightChildValue) ? leftChild : rightChild; var flag = compare(selectChild.Value, heap.Value); if (flag) { var tmp = heap.Value; heap.Value = selectChild.Value; selectChild.Value = tmp; } AdjustHeap(heap.LeftChild, compare); AdjustHeap(heap.RightChild, compare); if (flag) { AdjustHeap(heap, compare); } } void SortHeap(Node root, Func<int, int, bool> compare) { while (!root.IsUsed) { var lastNode = GetLastSortNode(root); var tmpHeapValue = root.Value; root.Value = lastNode.Value; lastNode.Value = tmpHeapValue; lastNode.IsUsed = true; AdjustHeap(root, compare); } } /// <summary> /// 获得最后可用的堆成员,如果是IsUsed说明是有序区就跳过,主要用于排序时,首尾成员交换 /// </summary> Node GetLastSortNode(Node root) { Node result = root; while (true) { if (result.RightChild != Node.EMPTY_NODE && !result.RightChild.IsUsed) { result = result.RightChild; } else if (result.LeftChild != Node.EMPTY_NODE && !result.LeftChild.IsUsed) { result = result.LeftChild; } else { break; } } return result; } /// <summary> /// 遍历节点 /// </summary> void ForeachNode(Node root, Action<Node> foreachContent) { if (root != Node.EMPTY_NODE) { foreachContent(root); } if (root.LeftChild != Node.EMPTY_NODE) { ForeachNode(root.LeftChild, foreachContent); } if (root.RightChild != Node.EMPTY_NODE) { ForeachNode(root.RightChild, foreachContent); } } List<int> OutputHeap(Node root) { List<int> result = new List<int>(); ForeachNode(root, m => m.IsUsed = false); while (!root.IsUsed) { var tmpNode = GetLastSortNode(root); result.Add(tmpNode.Value); tmpNode.IsUsed = true; } return result; } } }
调用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Hont; namespace HeapSortPackage { class Program { static void Main(string[] args) { HeapSort heapSort = new HeapSort(); var result = heapSort.Sort(new int[] { 22, 44, 88, 5, 6 }, true); Console.WriteLine(string.Join(",", result)); Console.Read(); } } }