最短路径、最小生成树、二叉树实现

最短路径

单源最短路:已知起点,求到达其它点的最短路径

​ 常用:Dijkstra算法、Bellman-Ford算法、SPFA算法

多源最短路:求任意两点之间的最短路径

​ 常用:Floyd算法

上述算法介绍,推荐文章:http://t.csdnimg.cn/IuCsu

Dijkstra(迪杰斯特拉算法)

源码示例:
int n;  // 顶点索引从 1 - n
int g[N][N],  dis[N],  vis[N];
void Dij (int pre) {   // pre 表示源点索引
	memset(dis, 0x3f, sizeof dis);  // dis[i] 表示从源点到 i 点的距离
	memset(vis, 0, sizeof vis);
	dis[pre] = 0;
	for (int i = 0; i < n; i++) {
		int u = -1;
		// 找到 dis 数组中最小距离的索引
		for (int j = 1; j <= n; j++) {
			if ((!vis[j]) && (u == -1 || dis[j] < dis[u])) u = j;
		}
		vis[u] = 1;
		// 更新 dis 数组
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && dis[j] > dis[u] + g[u][j]) {
				dis[j] = dis[u] + g[u][j];
			}
		}
	}	
}

Floyd(弗洛伊德算法)

源码示例:
int n;  // 顶点索引从 1 - n
int g[N][N];
void floyd() {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			g[i][j] = INF;
		}
		g[i][i] = 0;
	}
	/*
	    输入邻接矩阵 g
	*/
	for (int k = 1; k <= n; k++) { // 枚举中间点,即一定经过 k 点
		for (int i = 1; i <= n; i++) {  // 枚举起点
			for (int j = 1; j <= n; j++) {  // 枚举终点
				g[i][j] = min(g[i][j], g[i][k] + g[k][j]);  // 状态转移方程
			}
		}
	}

// if (g[x][y] > INF / 2) cout << "impossible" << endl;
// else cout << g[x][y] << endl;
/*解释为什么不是">INF"而是“>INF/2”:由于题目中可能会有负数边存在,而在方程“dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]); ”中我们可以发现,如果边为负数居然会把INF更新为INF-x,但是这个值实际上并不是我们能到达的距离。根据题目中数据范围,我们可以发现,我们虽然不能写成”>INF",但是“>INF/2”是可以判断是否能到达终点的。*/
}	

最小生成树

Kruskal(克鲁斯卡尔算法)

源码示例:
	int n, m; // n 为顶点个数,m 为边数
	int f[N];
	struct node {
		int x, y, w;
	}a[N];
	int find(int x) {
		return x == f[x] ? x : f[x] = find(x);
	}
	bool cmp(node x, node y) {
		return x.w < y.w;
	}
	sort(a + 1, a + 1 + n, cmp);
	int ans = 0;
	for (int i = 1; i <= m; i++) {
		int xx = find(a[i].x);
		int yy = find(a[i].y);
		if (xx == yy) continue;
		f[xx] = yy;
		ans += a[i].w;
	}

Prime

源码示例:

int n;  // 顶点索引从 1 - n
int g[N][N],  dis[N],  vis[N];
int len = 0;
void Prime () {
	memset(dis, 0x3f, sizeof dis);  // dis[i] 表示从已访问节点到未访问节点 i 的距离
	memset(vis, 0, sizeof vis);
	dis[1] = 0;
	for (int i = 0; i < n; i++) {
		int u = -1, mi = INF;
		// 找到 dis 数组中最小距离的索引
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && dis[j] < mi) {
				u = j;
				mi = dis[j];
			}
		}
		vis[u] = 1;
		len += mi;   // 计算最小生成树的总和
		// 更新 dis 数组
		for (int j = 1; j <= n; j++) {
			if (!vis[j] && g[u][j] < dis[j]) {
				dis[j] = g[u][j];
			}
		}
	}	
}

二叉树

基本性质

  1. 二叉树的第 i 层上最多有 2^(i - 1) 个结点
  2. 在一棵深度为 k 的二叉树中,最多有 (2^k - 1 )个结点,最少有 k 个结点
  3. 在一棵二叉树中,如果叶子结点的个数为 n0,度为 2 的结点个数为 n2,则 n0 = n2 + 1
  4. 具有 n 个结点的完全二叉树的深度为 floor(log2 (n)) + 1 // floor函数用于向下取整
  5. 对一颗具有 n 个结点的完全二叉树中的结点从 1 开始按层序编号,则对于任意编号 i 的结点,它的左孩子的结点编号为 2 * i,右孩子的节点编号为 2 * i + 1

第五点适用于数组和完全二叉树的转换,有些题目可以通过建立数组来代替建树,例如建立小顶堆和大顶堆

struct TreeNode {
	char val;
	TreeNode *l, *r;
};

建树

方法有很多,扩展先序遍历序列,前序+中序,后序+中序……

  1. 扩展先序遍历序列

    TreeNode* Creat(TreeNode *t) {
    	char ch;
    	cin >> ch;
    	if (ch == '#') t = nullptr;
    	else {
    		t = new TreeNode;
    		t->val = ch;
    		t->l = Creat(t->l);
    		t->r = Creat(t->r);
    	}
    }
    
  2. 前序+中序

    int mid[N], pre[N];  // 中序,前序
    //此方法由长度确定终止条件
    TreeNode* Creat(int midL, int preL, int len) {  
    	if (len == 0) return nullptr;
    	TreeNode* p = new TreeNode;
    	p->val = pre[preL];
    	p->l = p->r = nullptr;
    	int k = 0;
    	for (int i = midL; i < midL + len; i++) {
    		if (mid[i] == pre[preL]){
    			k = i - midL;
    			break;
    		}
    	}
    	p->r = creat(midL, preL + 1, k);
    	p->l = creat(midL + k + 1, preL + k + 1, len - k - 1);
    	return p;
    }
    int mian() {
    	TreeNode *p = Creat(1, 1, n);
    }
    
  3. 后序+中序

    int post[N], mid[N]l;  // 后序,中序
    // 此方法由左右子树是否存在确定终止条件
    TreeNode* Creat(int postL, int postR, int midL, int midR) {
    	if (postL > postR) return nullptr;
    	TreeNode* p = new TreeNode;
    	p->val = post[postR];
    	p->l = p->r = nullptr;
    	int i;
    	for (i = midL; i <= midR; i++) {
    		if (mid[i] == post[postR]) break;
    	}
    	p->l = Creat(postL, postL + i - midL - 1, midL, i - 1);
    	p->r = Creat(postL + i - midL, postR - 1, i + 1, midR);
    	return p;
    }
    
    

遍历(除层序遍历外,建议使用递归)

  1. 前序遍历(中左右)

    void PreOrder(TreeNode *t) {
    	if (t == nullptr) return ;
    	else {
    		cout << t->val;
    		PreOrder(t->l);
    		PreOrder(t->r);
    	}
    }
    
  2. 中序遍历(左中右)

    void InOrder(TreeNode *t) {
    	if (t == nullptr) return ;
    	else {
    		InOrder(t->l);
    		cout << t->val;
    		InOrder(t->r);
    	}
    }
    
  3. 后序遍历(左右中)

    void PostOrder(TreeNode *t) {
    	if (t == nullptr) return ;
    	else {
    		PostOrder(t->l);
    		PostOrder(t->r);
    		cout << t->val;
    	}
    }
    
  4. 层序遍历

    void LeverOrder(TreeNode *t) {
    	queue<TreeNode*> qu;
    	if (t == nullptr) return ;
    	qu.push(t);
    	while (!qu.empty()) {
    		TreeNode tmp = qu.top();
    		qu.pop();
    		cout << tmp->val;
    		if (tmp->l != nullptr) qu.push(tmp->l);
    		if (tmp->r != nullptr) qu.push(tmp->r);
    	}
    }
    
posted @ 2024-10-10 19:37  clockleaf  阅读(111)  评论(0)    收藏  举报