链式前向星

1 链表前插法

头插法,顾名思义,就是在链表的头部插入元素。

  • head:链表的头,指向链表的第一个元素的地址。

以数列1 3 2 4举例模拟头插法的实现过程:

模拟链表说明
head → \to NULL初始状态,head为空(NULL),表示无元素
head → \to 1 → \to NULL插入1,1指向NULL,head指向1
head → \to 3 → \to 1 → \to NULL插入3,3指向1,head指向3
head → \to 2 → \to 3 → \to 1 → \to NULL插入2,2指向3,head指向2
head → \to 4 → \to 2 → \to 3 → \to 1 → \to NULL插入4,4指向2,head指向4

现在用数组来模拟链表:

  • ver[]:保存元素的数值。
  • nxt[]:保存元素的后继地址(下标)。

*在表示时为了方便,把NULL标记为-1。

地址(下标)1234
ver[]1324
nxt[]-1123

head:4

链表头插法的定义、插入函数add()、遍历代码:

int head, ver[N], nxt[N], tot;  //定义

void add(int x) {   //插入
    ver[tot] = x;
    nxt[tot] = head;
    head = tot;
    tot++;
}

for (int i = head; ~i; i = nxt[i]) {  //从头遍历链表
    cout << ver[i] << " ";
}

2 链式前向星

观察图示的有向图和链表:

可以发现:

  1. 一共有4个链表,每一个链表的头部head对应一个节点。
  2. 每个head后面的元素为它的邻接点及其边权。

这种保存图的方法,就是链式前向星

那么此时需要对原有的链表头插法进行改变:

  • head[]:保存每个点的邻接点链表的头部地址(下标)。
  • w[]:保存边权。

用数组模拟链式前向星存边方法的最后结果:

地址(下标)12345
ver[]23323
nxt[]-11-1-14
w[]36278
head[]23-14

链式前向星的定义、存边函数add()、邻接点遍历代码:

  1. 数组式
int head[N], ver[N], nxt[N], w[N], tot;  //定义

void add(int x, int y, int w) {   //存边
    ver[tot] = y;
    w[tot] = w;
    nxt[tot] = head[x];
    head[x] = tot;
    tot++;
}

for (int i = head[x]; ~i; i = nxt[i]) {  //遍历x的邻接点
    cout << ver[i] << "-" << w[i] << " ";
}
  1. struct 结构体式
int head[N], size = 0;  //定义
struct edge{
	int ver, nxt, w;
}e[N];

void add(int x, int y, int w) {  //存边
	e[++size].ver = y;
	e[size].nxt = head[x];
	e[size].w = w;
	head[x] = size;
}

for (int i = head[x]; ~i; i = nxt[i]) {  //遍历x的邻接点
    cout << e[i].ver << "-" << e[i].w << " ";
}
  1. vector 动态数组式
vector<vector<pair<int, int>>> e(N);  //定义

e[x].push_back(make_pair(y, w));  //存边

for (int i = 0; i < e[x].size(); i++) { //遍历x的邻接点
    cout << e[x][i].first << "-" << e[x][i].second << " ";
}

3 例题

3.1 题目描述 Tree Cutting

洛谷 P1670 [USACO04DEC] Tree Cutting S

在树形网络中找到若干个节点,删除它和它的边,使删除后的网络各部分子网的节点数的最大值不超过总节点数的一半。如果没有一个可删除的节点,输出 NONE

3.2 题解

补充树的重心的性质

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻。

由此可知,这道题的答案是寻找树的重心。

定义链式前向星:

vector<vector<int>> e(N);

注意,这道题中由于未知父亲节点和儿子节点,所以需要双向存边,遍历时需要处理非法的反向搜索。

通过dfs()计算出每个点的子节点数,保存到cnt数组中:

void dfs(int x, int fat) { 
	f[x] = fat;
	cnt[x] = 1;
	for (int i = 0; i < e[x].size(); i++) {
		if (e[x][i] == fat) continue;
		dfs(e[x][i], x);
		cnt[x] += cnt[e[x][i]];
	}
}

遍历点,找到符合要求的点:

for (int i = 1; i <= n; i++) {
	int maxn = 0;
	for (int j = 0; j < e[i].size(); j++) {
		if (e[i][j] == f[i]) maxn = max(maxn, n - cnt[i]);
		else maxn = max(maxn, cnt[e[i][j]]);
	}
	if (maxn <= n / 2) {
		flag = 1;
		cout << i << endl;
	}
}
  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值