经典算法题每日演练——第十六题 Kruskal算法
2012-12-17 00:28
393 查看
这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的。
一:思想
若存在M={0,1,2,3,4,5}这样6个节点,我们知道Prim算法构建生成树是从”顶点”这个角度来思考的,然后采用“贪心思想”
来一步步扩大化,最后形成整体最优解,而Kruskal算法有点意思,它是站在”边“这个角度在思考的,首先我有两个集合。
1. 顶点集合(vertexs):
比如M集合中的每个元素都可以认为是一个独根树(是不是想到了并查集?)。
2.边集合(edges):
对图中的每条边按照权值大小进行排序。(是不是想到了优先队列?)
好了,下面该如何操作呢?
首先:我们从edges中选出权值最小的一条边来作为生成树的一条边,然后将该边的两个顶点合并为一个新的树。
然后:我们继续从edges中选出次小的边作为生成树的第二条边,但是前提就是边的两个顶点一定是属于两个集合中,如果不是
则剔除该边继续选下一条次小边。
最后:经过反复操作,当我们发现n个顶点的图中生成树已经有n-1边的时候,此时生成树构建完毕。
View Code
一:思想
若存在M={0,1,2,3,4,5}这样6个节点,我们知道Prim算法构建生成树是从”顶点”这个角度来思考的,然后采用“贪心思想”
来一步步扩大化,最后形成整体最优解,而Kruskal算法有点意思,它是站在”边“这个角度在思考的,首先我有两个集合。
1. 顶点集合(vertexs):
比如M集合中的每个元素都可以认为是一个独根树(是不是想到了并查集?)。
2.边集合(edges):
对图中的每条边按照权值大小进行排序。(是不是想到了优先队列?)
好了,下面该如何操作呢?
首先:我们从edges中选出权值最小的一条边来作为生成树的一条边,然后将该边的两个顶点合并为一个新的树。
然后:我们继续从edges中选出次小的边作为生成树的第二条边,但是前提就是边的两个顶点一定是属于两个集合中,如果不是
则剔除该边继续选下一条次小边。
最后:经过反复操作,当我们发现n个顶点的图中生成树已经有n-1边的时候,此时生成树构建完毕。
View Code
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 using System.IO; 8 9 namespace ConsoleApplication2 10 { 11 public class PriorityQueue<T> where T : class 12 { 13 /// <summary> 14 /// 定义一个数组来存放节点 15 /// </summary> 16 private List<HeapNode> nodeList = new List<HeapNode>(); 17 18 #region 堆节点定义 19 /// <summary> 20 /// 堆节点定义 21 /// </summary> 22 public class HeapNode 23 { 24 /// <summary> 25 /// 实体数据 26 /// </summary> 27 public T t { get; set; } 28 29 /// <summary> 30 /// 优先级别 1-10个级别 (优先级别递增) 31 /// </summary> 32 public int level { get; set; } 33 34 public HeapNode(T t, int level) 35 { 36 this.t = t; 37 this.level = level; 38 } 39 40 public HeapNode() { } 41 } 42 #endregion 43 44 #region 添加操作 45 /// <summary> 46 /// 添加操作 47 /// </summary> 48 public void Eequeue(T t, int level = 1) 49 { 50 //将当前节点追加到堆尾 51 nodeList.Add(new HeapNode(t, level)); 52 53 //如果只有一个节点,则不需要进行筛操作 54 if (nodeList.Count == 1) 55 return; 56 57 //获取最后一个非叶子节点 58 int parent = nodeList.Count / 2 - 1; 59 60 //堆调整 61 UpHeapAdjust(nodeList, parent); 62 } 63 #endregion 64 65 #region 对堆进行上滤操作,使得满足堆性质 66 /// <summary> 67 /// 对堆进行上滤操作,使得满足堆性质 68 /// </summary> 69 /// <param name="nodeList"></param> 70 /// <param name="index">非叶子节点的之后指针(这里要注意:我们 71 /// 的筛操作时针对非叶节点的) 72 /// </param> 73 public void UpHeapAdjust(List<HeapNode> nodeList, int parent) 74 { 75 while (parent >= 0) 76 { 77 //当前index节点的左孩子 78 var left = 2 * parent + 1; 79 80 //当前index节点的右孩子 81 var right = left + 1; 82 83 //parent子节点中最大的孩子节点,方便于parent进行比较 84 //默认为left节点 85 var min = left; 86 87 //判断当前节点是否有右孩子 88 if (right < nodeList.Count) 89 { 90 //判断parent要比较的最大子节点 91 min = nodeList[left].level < nodeList[right].level ? left : right; 92 } 93 94 //如果parent节点大于它的某个子节点的话,此时筛操作 95 if (nodeList[parent].level > nodeList[min].level) 96 { 97 //子节点和父节点进行交换操作 98 var temp = nodeList[parent]; 99 nodeList[parent] = nodeList[min]; 100 nodeList[min] = temp; 101 102 //继续进行更上一层的过滤 103 parent = (int)Math.Ceiling(parent / 2d) - 1; 104 } 105 else 106 { 107 break; 108 } 109 } 110 } 111 #endregion 112 113 #region 优先队列的出队操作 114 /// <summary> 115 /// 优先队列的出队操作 116 /// </summary> 117 /// <returns></returns> 118 public HeapNode Dequeue() 119 { 120 if (nodeList.Count == 0) 121 return null; 122 123 //出队列操作,弹出数据头元素 124 var pop = nodeList[0]; 125 126 //用尾元素填充头元素 127 nodeList[0] = nodeList[nodeList.Count - 1]; 128 129 //删除尾节点 130 nodeList.RemoveAt(nodeList.Count - 1); 131 132 //然后从根节点下滤堆 133 DownHeapAdjust(nodeList, 0); 134 135 return pop; 136 } 137 #endregion 138 139 #region 对堆进行下滤操作,使得满足堆性质 140 /// <summary> 141 /// 对堆进行下滤操作,使得满足堆性质 142 /// </summary> 143 /// <param name="nodeList"></param> 144 /// <param name="index">非叶子节点的之后指针(这里要注意:我们 145 /// 的筛操作时针对非叶节点的) 146 /// </param> 147 public void DownHeapAdjust(List<HeapNode> nodeList, int parent) 148 { 149 while (2 * parent + 1 < nodeList.Count) 150 { 151 //当前index节点的左孩子 152 var left = 2 * parent + 1; 153 154 //当前index节点的右孩子 155 var right = left + 1; 156 157 //parent子节点中最大的孩子节点,方便于parent进行比较 158 //默认为left节点 159 var min = left; 160 161 //判断当前节点是否有右孩子 162 if (right < nodeList.Count) 163 { 164 //判断parent要比较的最大子节点 165 min = nodeList[left].level < nodeList[right].level ? left : right; 166 } 167 168 //如果parent节点小于它的某个子节点的话,此时筛操作 169 if (nodeList[parent].level > nodeList[min].level) 170 { 171 //子节点和父节点进行交换操作 172 var temp = nodeList[parent]; 173 nodeList[parent] = nodeList[min]; 174 nodeList[min] = temp; 175 176 //继续进行更下一层的过滤 177 parent = min; 178 } 179 else 180 { 181 break; 182 } 183 } 184 } 185 #endregion 186 187 #region 获取元素并下降到指定的level级别 188 /// <summary> 189 /// 获取元素并下降到指定的level级别 190 /// </summary> 191 /// <returns></returns> 192 public HeapNode GetAndDownPriority(int level) 193 { 194 if (nodeList.Count == 0) 195 return null; 196 197 //获取头元素 198 var pop = nodeList[0]; 199 200 //设置指定优先级(如果为 MinValue 则为 -- 操作) 201 nodeList[0].level = level == int.MinValue ? --nodeList[0].level : level; 202 203 //下滤堆 204 DownHeapAdjust(nodeList, 0); 205 206 return nodeList[0]; 207 } 208 #endregion 209 210 #region 获取元素并下降优先级 211 /// <summary> 212 /// 获取元素并下降优先级 213 /// </summary> 214 /// <returns></returns> 215 public HeapNode GetAndDownPriority() 216 { 217 //下降一个优先级 218 return GetAndDownPriority(int.MinValue); 219 } 220 #endregion 221 222 #region 返回当前优先队列中的元素个数 223 /// <summary> 224 /// 返回当前优先队列中的元素个数 225 /// </summary> 226 /// <returns></returns> 227 public int Count() 228 { 229 return nodeList.Count; 230 } 231 #endregion 232 } 233 }
相关文章推荐
- 经典算法题每日演练——第十六题 Kruskal算法
- 经典算法题每日演练——第十六题 Kruskal算法
- 经典算法题每日演练——第十六题 Kruskal算法
- 经典算法题每日演练——第十二题 线段树
- 经典算法题每日演练——第四题 最长公共子序列
- 经典算法题每日演练——第二十题 三元组
- 经典算法题每日演练——第三题 猴子吃桃
- 经典算法题每日演练——第十七题 Dijkstra算法
- 经典算法题每日演练——第一题 百钱买百鸡
- 经典算法题每日演练——第二十三题 鸡尾酒排序
- 经典算法题每日演练——第十四题 Prim算法
- 经典算法题每日演练——第五题 字符串相似度
- 经典算法题每日演练——第二十一题 十字链表
- 经典算法题每日演练——第三题 猴子吃桃
- 经典算法题每日演练——第一题 百钱买百鸡
- 经典算法题每日演练——第二题 五家共井
- 经典算法题每日演练——第十七题 Dijkstra算法
- 经典算法题每日演练——第六题 协同推荐SlopeOne 算法
- 经典算法题每日演练——第十九题 双端队列
- 经典算法题每日演练——第二题 五家共井