当前位置: 首页 > news >正文

扬州润扬交通建设集团网站微信营销管理软件

扬州润扬交通建设集团网站,微信营销管理软件,58招聘求职找工作,网站建设html5源码目录 前言 故事 🌼思路 🌼总结 🌼代码 👊观察过程代码 👊正确代码 👊细节代码 来自《啊哈算法》 前言 刚学了树在优先队列中的应用--堆的实现 那么树还有哪些神奇的用法呢?我们从一…

目录

前言

故事

🌼思路

🌼总结

🌼代码

👊观察过程代码

👊正确代码 

👊细节代码


来自《啊哈算法》

前言

刚学了树在优先队列中的应用--堆的实现

那么树还有哪些神奇的用法呢?我们从一个故事说起----揭秘犯罪团伙

故事

快过年了,犯罪分子们也开始为年终奖“奋斗”了,小哼的家乡出现多次抢劫事件。

由于强盗人数过于庞大,作案频繁,警察想调查清楚到底有几个犯罪团伙实在不容易。

不过警察叔叔还是搜集到了几条线索

现在有10个强盗,9条线索

1--2(表示1号强盗与2号强盗是同伙

3--4,5--2,4--6,2--6,8--7,9--7,1--6,2--4

规定,强盗同伙的同伙也是同伙,你能帮警察叔叔查出有多少个独立的犯罪团伙吗?

🌼思路

要解决这个问题,先假设10个强盗相互不认识,他们各自为政,每个人都是自己的头,只听自己的

之后,我们通过警察的线索,一步步“合并同伙”

1,声明一维数组

申请一个一维数组dad,dad[1] == 1表示1号强盗的头是自己,10号强盗的头是10号自己用dad[10] == 10表示

2,初始化

初始化dad数组(当然,书里是f数组) ,令 dad[i] = i; 即可

3,合并同伙

如果发现两个强盗是同伙,那么他俩是同一犯罪团伙,但是。。。谁做老大呢?

我们假定左边的强盗更厉害些,规定一个“靠左法则”,比如警察得到的第一条线索是,1号和2号同伙

(1)

所以1号在左边,更厉害,那么2号归顺1号,也就是1号是2号的头头,所以dad[2] = 1;表示2号强盗的头是1号强盗

(2)

第二条线索,3--4(3号强盗和4号是同伙),根据“左边的更厉害”,4号归顺3号,所以 dad[4] = 3;

(3)

第3条线索,5--2,5号和2号强盗是同伙,dad[5] == 5,说明5号强盗的头是自己

dad[2] == 1,说明2号强盗的头是1号强盗

根据“左边的更厉害”,此时应该让2号归顺5号,那么“1号强盗”就不干了,“你凭什么抢我的人??

于是这俩强盗差点打起来了,这让2号为难了,2号归顺5号还是继续跟着原来的头1号呢?

这里我们还是按左边的更厉害的原则,5号强盗直接找2号的头1号谈,让1号这个老大也归顺他(递归实现)

操作:dad[1] = 5; dad[2] = 5;

为什么在1号这个老大归顺5号后,还要再让2号归顺一次呢?

这一步不是必须的,但会提高后面找强盗最高领导人的速度(也就是找树的祖先的速度)

否则,很容易形成单支树结构(一长条。。。),极大浪费空间和时间

dad[2] = 5; 这看似多余的一步,也叫路径压缩(不要觉得名字很高大上,其实就是一行代码的事)

第四条线索

4--6(这俩同伙)

此时dad[4] == 3, dad[6] == 6,我们让6号强盗加入“3号犯罪团伙”,即dad[6] = 3;

第5条线索

2--6

此时dad[2] == 5, dad[6] == 3

我们令6号和他的老大都归顺“5号犯罪团伙”(递归实现),dad[6] = 5; 以及 dad[3] = 5;

第6条线索

8--7

此时dad[8] == 8, dad[7] == 7, 让7号归顺8号好了,dad[7] = 8;

第7条线索

9--7

此时dad[9] == 9, dad[7] == 8,所以8号和7号都归顺9号强盗(路径压缩)(递归实现)

dad[8] = 9;  dad[7] = 9;

除了让7号的老大归顺9号,我们还让7号也直接归顺9号,这一步叫路径压缩,这一步通过递归返回时实现,不会增加时间复杂度

第8条线索

1--6

此时dad[1] == 5, dad[6] == 5,已经是同伙了,不需要操作 

第9条线索

2--4

此时dad[2] == 5, dad[4] == 3 

4号归顺3号,3号归顺5号,5号归顺自己,所以5号是最高领导人

从4号顺藤摸瓜到5号最高领导人的过程,就是递归 

递归完后,dad[4] == 5; 

至此,所有线索分析完毕!那么有多少个犯罪团伙呢?

由上图可知,3个团伙

5号犯罪集团:5,2,1,3,4,6组成

9号犯罪集团:9,8,7组成

10号犯罪集团:10组成

容易知道,如果 dad[i] == i,表示 i 号强盗是一个团伙的最高领导人,最后数组dad中:

dad[5] == 5, dad[9] == 9, dad[10] == 10,所以共3个犯罪团伙

🌼总结

我们刚才模拟的是并查集算法,并查集通过一个一维数组实现,本质是维护一个森林。

初始,森林每个点都是孤立的,也就是每个强盗的老大都是自己,也可以理解为每个点就是一棵只有一个节点的树

之后,将这些孤立的点合并成一颗大树,合并的过程就是叫爸爸的过程

叫爸爸的过程,遵循“左边更厉害”和“擒贼先擒王”的原则 

“擒贼先擒王”也就是,先让老大归顺左边的强盗,自己再归顺(自己再归顺就是路径压缩

补充: 并查集也称为为不相交集数据结构

🌼代码

👊观察过程代码

#include<iostream>
using namespace std;
int dad[100];int Find(int x) //不停认老大, 直到顶头上司
{if(dad[x] == x) return x;else {//路径压缩, 将路径上所有点的上级都设置为根节点dad[x] = Find(dad[x]);return dad[x];}
}void join(int x, int y) //合并两个团伙
{//m为x的最高领导人, n为y的最高领导人int m = Find(dad[x]), n = Find(dad[y]);if(m != n) //路径压缩dad[n] = m; //第2个最高领导人认第1个当老大
}int main()
{for(int i = 1; i <= 10; ++i)dad[i] = i; //初始化自己为老大int a, b;for(int i = 1; i <= 9; ++i) {cout<<"第"<<i<<"条线索:";cin>>a>>b;join(a, b); //合并团伙cout<<dad[a]<<" "<<dad[b]<<endl;}int ans = 0;for(int i = 1; i <= 10; ++i)if(dad[i] == i)ans += 1;return 0;
}
第1条线索:1 2
1 1
第2条线索:3 4
3 3
第3条线索:5 2
5 1
第4条线索:4 6
3 3
第5条线索:2 6
1 3
第6条线索:8 7
8 8
第7条线索:9 7
9 8
第8条线索:1 6
5 3
第9条线索:2 4
1 3

为什么第一次没实现路径压缩呢?(就是,虽然团伙总数确定了,但是每个人依然跟着自己原来的老大,没有跟着顶头上司)

原来是代码第18行

m = Find(dad[x]), n = Find(dad[y]);

应该改成

m = Find(x), n = Find(y);

否则只是令右老大归顺了左老大,但是右小弟没有直接归顺左老大,右小弟依然跟着右老大

这样就没起到路径压缩的效果

👊正确代码 

即使是正确代码,《啊哈算法》里也存在BUG,不过无关痛痒而已(对时间复杂度几乎没影响)

#include<iostream>
using namespace std;
int dad[100];int Find(int x) //不停认老大, 直到顶头上司
{if(dad[x] == x) return x;else {//路径压缩, 将路径上所有点的上级都设置为根节点dad[x] = Find(dad[x]); //递归返回return dad[x];}
}void join(int x, int y) //合并两个团伙
{//m为x的最高领导人, n为y的最高领导人int m = Find(x), n = Find(y);if(m != n) //路径压缩dad[n] = m; //第2个最高领导人认第1个当老大
}int main()
{for(int i = 1; i <= 10; ++i)dad[i] = i; //初始化自己为老大int a, b;for(int i = 1; i <= 9; ++i) {cin>>a>>b;join(a, b); //合并团伙}int ans = 0;for(int i = 1; i <= 10; ++i)if(dad[i] == i)ans += 1;for(int i = 1; i <= 10; ++i)cout<<"第"<<i<<"个强盗的老大是"<<dad[i]<<endl;cout<<"共有"<<ans<<"个犯罪团伙";return 0;
}
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
第1个强盗的老大是5
第2个强盗的老大是5
第3个强盗的老大是5
第4个强盗的老大是5
第5个强盗的老大是5
第6个强盗的老大是5
第7个强盗的老大是8
第8个强盗的老大是9
第9个强盗的老大是9
第10个强盗的老大是10
共有3个犯罪团伙

上述代码实现了路径压缩,但是第7个强盗的老大怎么还是8呢,不应该是9吗

因为这种较为简单的路径压缩,有一个小小的缺陷

需要第一次先找到祖宗,第二次才能对路径上的节点进行压缩,而输入9  7前,7原来的老大是8,8的原来的老大也是8;当输入9  7后,进行join()函数

此时7的老大8号就归顺了9号,但7的老大依然是8号,没有变成9号,需要在下一次递归查找老大时,7的老大才变成9号,但此时已经没有下次了

当然,我们也可以在join()函数的最后重写一次 n = Find(y);

就可以实现书里的效果,但是不知道这样是否会影响时间复杂度 

👊细节代码

#include<iostream>
using namespace std;
int dad[100];int Find(int x) //不停认老大, 直到顶头上司
{if(dad[x] == x) return x;else {//路径压缩, 将路径上所有点的上级都设置为根节点dad[x] = Find(dad[x]); //递归返回return dad[x];}
}void join(int x, int y) //合并两个团伙
{//m为x的最高领导人, n为y的最高领导人int m = Find(x), n = Find(y);if(m != n) //路径压缩dad[n] = m; //第2个最高领导人认第1个当老大n = Find(y); //重写一次
}int main()
{for(int i = 1; i <= 10; ++i)dad[i] = i; //初始化自己为老大int a, b;for(int i = 1; i <= 9; ++i) {cin>>a>>b;join(a, b); //合并团伙}int ans = 0;for(int i = 1; i <= 10; ++i)if(dad[i] == i)ans += 1;for(int i = 1; i <= 10; ++i)cout<<"第"<<i<<"个强盗的老大是"<<dad[i]<<endl;cout<<"共有"<<ans<<"个犯罪团伙";return 0;
}
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
第1个强盗的老大是5
第2个强盗的老大是5
第3个强盗的老大是5
第4个强盗的老大是5
第5个强盗的老大是5
第6个强盗的老大是5
第7个强盗的老大是9
第8个强盗的老大是9
第9个强盗的老大是9
第10个强盗的老大是10
共有3个犯罪团伙
http://www.dinnco.com/news/5497.html

相关文章:

  • 徐州免费建站模板站外seo推广
  • 南京领动做网站怎么样免费友情链接
  • 怎样做单页销售网站seo需要懂代码吗
  • 网站建设中期目标seo 培训教程
  • 网站每年维护费用公司网站建设服务机构
  • 大连网站建设怎么做微信营销神器
  • 网站logo怎么做动态百度竞价个人开户
  • 博主怎么赚钱优化大师apk
  • 民宿设计公司页面优化
  • 网站做百度推广域名权重查询工具
  • 昆明网页制作开发优化营商环境工作开展情况汇报
  • 焦作专业做网站公司网络服务平台
  • 房产网站建设批发西安百度关键词推广
  • 什么网站可以做海报赚钱百度搜索页
  • 重庆免费建站公司地址常用的搜索引擎有哪些
  • 衡水做淘宝网站建设百度快照投诉中心官网
  • 中国建设网官方网站下载e路最新版外贸营销网站怎么建站
  • js实现网站浮动窗口国家职业技能培训平台
  • 网站二次开发的模板种类全国各城市感染高峰进度查询
  • 测试网站免费空间网上推广企业
  • 优化seo是什么广州网站优化多少钱
  • qq企业邮箱注册seo诊断站长
  • html5网页模板代码官网排名优化方案
  • 网站建设公司广告标题语nba最新新闻
  • 济南网站开发培训班推广营销是什么
  • 网站将导航条不滚动怎么做软文优化
  • 视频直播类网站开发难度百度产品大全入口
  • 梅州哪里做网站口红的推广软文
  • 做招聘网站需要资质吗友点企业网站管理系统
  • java视频面谈网站开发整站优化深圳