//二叉树的父节点是k
//那么它的左儿子的编号就是2*k 右儿子是2*k+1 左儿子或右儿子是那么父节点是x/2
//如果一颗完全二叉树有N个节点 那么这个完全二叉树的高度是logn
2.并查集
并查集通过一个维数组来实现,其本质是维护一个森林 刚开始的时候 森林的每个点都是孤立 的,也可以理解为每个点就是一颗只有一个结点的树,之后通过一些条件 逐渐将这些树合并成一颗大树,合并过程中遵守靠左原则 和擒贼先擒王原则 在每次判断两个节点是否已经在同一颗树的时候(也就是同一集合)也要注意必须求其根源 ,中间父亲节点是不能说明问题的 必须找到其祖宗(树的根结点),判断两个节点的祖宗是否是同一个根结点才行。
#include<iostream>
using namespace std;
int f[1000] = { 0 }, n, m, k, sum = 0;
//这里是初始化 非常的重要 数组里面存的是中间数组下标的编号就好了
void init() {
for (int i = 1; i <= n; i++) {
f[i] = i;
}
}
//这里是找爹的递归函数 不停的去找爹 直到找到祖宗为止 其实就是去找犯罪团伙人的最高领导人 擒贼先擒王原则
int getf(int v) {
if (f[v] == v)return v;
else {
//这里是路径压缩 每次在函数返回的时候 顺带把路上遇到的人的boss改为最后找到的祖宗编号 也就是犯罪团伙人的最高领导编号 这样可以提高今后找到犯罪团伙的最高领导人(其实就是树的祖先)的速度
f[v] = getf(f[v]);
return f[v];
}
}
//这里是合并两子集的函数 擒贼先擒王
void merge(int v, int u) {
int t1, t2;
t1 = getf(v);
t2 = getf(u);
if (t1 != t2) {
f[t2] = t1;
}
}
int main(){
int i, x, y;
cin >> n >> m;
init();
for (int i = 1; i <= m; i++) {
//开始合并犯罪团伙
cin >> x >> y;
merge(x, y);
}
//最后扫描有多少个独立的犯罪团伙
for (int i = 1; i <= n; i++) {
if (f[i] == i)
sum++;
}cout << sum;
return 0;
}
最小生成树
库鲁斯卡尔算法+查并集
#include<iostream>
using namespace std;
struct edge {
int u;
int v;
int w;
};
edge e[10];
int f[7],sum;
void quicksort(int left, int right) {
int i = left;
int j = right;
if (left > right)return;
while (i != j) {
while (e[j].w >= e[left].w && i < j)j--;
while (e[i].w <= e[left].w && i < j)i++;
if (i < j)swap(e[j], e[i]);
}
swap(e[left], e[i]);
quicksort(left, i - 1);
quicksort(i + 1, right);
return;
}
int getf(int v) {
if (f[v] == v)return v;
else return f[v] = getf(f[v]);
}
int merge(int u, int v) {
int t1 = getf(u);
int t2 = getf(v);
if (t1 != t2) {
f[t2] = t1;
return 1;
}
return 0;
}
int main() {
int n, m,count=0;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> e[i].u >> e[i].v >> e[i].w;
}
for (int i = 1; i <= n; i++) {
f[i] = i;
}
quicksort(1, m);
for (int i = 1; i <= m; i++) {
if (merge(e[i].u, e[i].v)) {
count++;
sum += e[i].w;
}
if (count == count - 1)break;
}
cout << sum;
return 0;
}