前段时间玩小爬虫的时候,我把url都是放在内存队列里面,有时我们在抓取url的时候,通过LCS之类的相似度比较,发现某些url是很重要的,需要后端解析服务器优先处理,针对这种优先级比较大的url,普通的队列还是苦逼的在做FIFO操作,现在我们的需求就是优先级大的优先服务,要做优先队列,非堆莫属。

一:堆结构

1:性质

堆是一种很松散的序结构树,只保存了父节点和孩子节点的大小关系,并不规定左右孩子的大小,不像排序树那样严格,又因为堆是一种完全二叉树,设节点为i,则i/2是i的父节点,2i是i的左孩子,2i+1是i的右孩子,所以在实现方式上可以采用轻量级的数组。

2:用途

如果大家玩过微软的MSMQ的话,就会发现它其实也是一个优先队列,还有刚才说的抓取url,不过很遗憾,为什么.net类库中没有优先队列呢?而java1.5中就已经支持了。

3:实现

<1>堆结构节点定义:

我们在每个节点上定义一个level,表示该节点的优先级,也是构建堆时采取的依据。


/// <summary>/// 定义一个数组来存放节点/// </summary>private List<HeapNode> nodeList = new List<HeapNode>();/// <summary>/// 堆节点定义/// </summary>public class HeapNode{/// <summary>/// 实体数据/// </summary>public T t { get; set; }/// <summary>/// 优先级别 1-10个级别 (优先级别递增)/// </summary>public int level { get; set; }public HeapNode(T t, int level){this.t = t;this.level = level;}public HeapNode() { }}

<2> 入队操作

入队操作时我们要注意几个问题:

①:完全二叉树的构建操作是“从上到下,从左到右”的形式,所以入队的节点是放在数组的最后,也就是树中叶子层的有序最右边空位。

②:当节点插入到最后时,有可能破坏了堆的性质,此时我们要进行“上滤操作”,当然时间复杂度为O(logN)。

当我将节点“20”插入到堆尾的时候,此时破坏了堆的性质,从图中我们可以清楚的看到节点“20”的整个上滤过程,有意思吧,还有一点就是:获取插入节点的父亲节点的算法是:parent=list.count/2-1。这也得益于完全二叉树的特性。


#region  添加操作/// <summary>/// 添加操作/// </summary>public void Eequeue(T t, int level = 1){//将当前节点追加到堆尾nodeList.Add(new HeapNode(t, level));//如果只有一个节点,则不需要进行筛操作if (nodeList.Count == 1)return;//获取最后一个非叶子节点int parent = nodeList.Count / 2 - 1;//堆调整UpHeapAdjust(nodeList, parent);}#endregion#region 对堆进行上滤操作,使得满足堆性质/// <summary>/// 对堆进行上滤操作,使得满足堆性质/// </summary>/// <param name="nodeList"></param>/// <param name="index">非叶子节点的之后指针(这里要注意:我们/// 的筛操作时针对非叶节点的)/// </param>public void UpHeapAdjust(List<HeapNode> nodeList, int parent){while (parent >= 0){//当前index节点的左孩子var left = 2 * parent + 1;//当前index节点的右孩子var right = left + 1;//parent子节点中最大的孩子节点,方便于parent进行比较//默认为left节点var max = left;//判断当前节点是否有右孩子if (right < nodeList.Count){//判断parent要比较的最大子节点max = nodeList[left].level < nodeList[right].level ? right : left;}//如果parent节点小于它的某个子节点的话,此时筛操作if (nodeList[parent].level < nodeList[max].level){//子节点和父节点进行交换操作var temp = nodeList[parent];nodeList[parent] = nodeList[max];nodeList[max] = temp;//继续进行更上一层的过滤parent = (int)Math.Ceiling(parent / 2d) - 1;}else{break;}}}#endregion

<3> 出队操作

从图中我们可以看出,优先级最大的节点会在一阵痉挛后上升到堆顶,出队操作时,我们采取的方案是:弹出堆顶元素,然后将叶子层中的最右子节点赋给堆顶,同样这时也会可能存在破坏堆的性质,最后我们要被迫进行下滤操作。

从图中可以看出:首先将堆顶20弹出,然后将7赋给堆顶,此时堆性质遭到破坏,最后我们清楚的看到节点7的下滤过程,从摊还分析的角度上来说,下滤的层数不超过2-3层,所以整体上来说出队的时间复杂度为一个常量O(1)。


#region 优先队列的出队操作/// <summary>/// 优先队列的出队操作/// </summary>/// <returns></returns>public HeapNode Dequeue(){if (nodeList.Count == 0)return null;//出队列操作,弹出数据头元素var pop = nodeList[0];//用尾元素填充头元素nodeList[0] = nodeList[nodeList.Count - 1];//删除尾节点nodeList.RemoveAt(nodeList.Count - 1);//然后从根节点下滤堆DownHeapAdjust(nodeList, 0);return pop;}#endregion#region  对堆进行下滤操作,使得满足堆性质/// <summary>/// 对堆进行下滤操作,使得满足堆性质/// </summary>/// <param name="nodeList"></param>/// <param name="index">非叶子节点的之后指针(这里要注意:我们/// 的筛操作时针对非叶节点的)/// </param>public void DownHeapAdjust(List<HeapNode> nodeList, int parent){while (2 * parent + 1 < nodeList.Count){//当前index节点的左孩子var left = 2 * parent + 1;//当前index节点的右孩子var right = left + 1;//parent子节点中最大的孩子节点,方便于parent进行比较//默认为left节点var max = left;//判断当前节点是否有右孩子if (right < nodeList.Count){//判断parent要比较的最大子节点max = nodeList[left].level < nodeList[right].level ? right : left;}//如果parent节点小于它的某个子节点的话,此时筛操作if (nodeList[parent].level < nodeList[max].level){//子节点和父节点进行交换操作var temp = nodeList[parent];nodeList[parent] = nodeList[max];nodeList[max] = temp;//继续进行更下一层的过滤parent = max;}else{break;}}}#endregion

最后我还扩展了一个弹出并下降节点优先级的方法,好了,这个方法大家自己琢磨琢磨,很有意思的,实际应用中使用到了,下面是完整代码的网盘链接:

链接:https://pan.baidu.com/s/15clOhAGOzz10HCEW0YqWKQ 提取码:z69b

数据结构与算法专题——第二题 优先队列相关推荐

  1. 数据结构与算法专题——第一题 Bitmap算法

    在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在定位查找场景上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场合下: ①:对10亿个不重复的整数进行排序. ②:找出1 ...

  2. 数据结构与算法专题——第九题 外排序

    说到排序,大家第一反应基本上是内排序,是的,算法嘛,玩的就是内存,然而内存是有限制的,总有装不下的那一天,此时就可以来玩玩外排序,当然在我看来,外排序考验的是一个程序员的架构能力,而不仅仅局限于排序这 ...

  3. 数据结构与算法专题——第九题 鸡尾酒排序

    这篇我们来聊一下鸡尾酒排序,为了知道为啥取名为鸡尾酒,特意看了下百科,见框框的话,也只能勉强这么说了. 要是文艺点的话,可以说是搅拌排序,通俗易懂点的话,就叫"双向冒泡排序",我想 ...

  4. Java数据结构和算法(第二版)

    Java数据结构和算法(第二版) 下载地址 https://pan.baidu.com/s/112D5houIgu0eMs_i5o0Ujw 扫码下面二维码关注公众号回复 100066获取分享码 本书目 ...

  5. 比特数据结构与算法(第二章收尾)带头双向循环链表的实现

    1.链表的分类 链表的分类 ① 单向或者双向 ② 带头或者不带头 ③ 循环或者非循环 常用的链表: 根据上面的分类我们可以细分出8种不同类型的链表,这么多链表我们一个个讲解这并没有意义.我们实际中最常 ...

  6. 横空出世,席卷互联网--评微软等公司数据结构和算法面试100题

    横空出世,席卷互联网                      ---评微软数据结构+算法面试100题 作者:July. 时间:2010年10月-11月.版权所有,侵权必究. 出处:http://bl ...

  7. 横空出世,席卷互联网--评微软等公司数据结构和算法面试100题 .

    入 编程这一行之初,便常听人说,要多动手写代码.可要怎么写列?写些什么列?做些什么列? c语言程序设计100例,太过基础,入门之后,挑战性不够.直接做项目,初学者则需花费大量的时间与精力.且得有一定能 ...

  8. 《数据结构与算法》第二版-陈卫卫-陆军工程大学811数据结构教材 第1-2章 参考答案

    <数据结构与算法>(第二版)陈卫卫-高等教育出版社     陆军工程大学811数据结构教材    第1-2章 参考答案 习题1.1 1.1-1      (1)名称.数量.特征.性质的   ...

  9. 数据结构与算法基础--错题集

    数据结构与算法的定义 数据结构是一门研究非数值计算的程序设计问题中的操作对象以及它们之间的关系和运算的学科. 算法是:解决问题的有限运算序列 算法分析的两个主要方面是:空间复杂度和时间复杂度 算法分析 ...

最新文章

  1. 如何使用Mockito模拟void方法
  2. js中时间戳与日期时间之间的相互转换
  3. tcl中的string trim使用时需要注意substring是一个集合
  4. 前端每日实战:140# 视频演示如何用纯 CSS 创作文本的淡入动画效果
  5. Android中扫描wifi热点
  6. KeeperErrorCode = Unimplemented for /test
  7. php在浏览器输入路径,关于在sublime text 3 中配置一键浏览器打开php文件,并且是在localhost 本地服务器路径下...
  8. Java1.8安装win10_java1.8环境配置+win10系统
  9. JDBC 8.0 和 JDBC 5.0 区别
  10. linux下tomcat8安装详解详解
  11. [转载]软件界面交互和易用性改进总结
  12. 解决Synology群晖VideoStation电影电视信息无法手动搜索
  13. FaceBook 遭遇有史以来全球最大宕机
  14. 苹果免密支付怎么关闭_有人苹果手机被盗刷了!那是设置有问题...
  15. 解决Uncaught TypeError Cannot read properties of undefined (reading ‘props‘)
  16. winform一个小游戏,赛马
  17. 电磁场的能量守恒和动量守恒
  18. Ansoftnbsp;andnbsp;Ansys
  19. C#中窗体绑定键盘按钮
  20. AndroidO Treble架构分析1

热门文章

  1. day10 多媒体(文字 图片 音频 视频)
  2. WebRTC 音频模块单独编译 --【转载】
  3. Linux文件系统基础(1)
  4. 创业95%失败不是因项目本身
  5. Zoom Host可以真正看到您的所有私人消息吗?
  6. 美味奇缘_轻松访问和管理您的美味书签
  7. redis系列3---理解内存
  8. vue实现首屏加载等待动画 避免首次加载白屏尴尬
  9. LinkedHashMap的实现原理
  10. RabbitMQ详解(三)