题目描述:
求如下图所示的二叉树中结点e和结点h之间的最短路径
题目分析:
这题题目描述很简单,但是算法实现一点也不简单,首先需要将这颗二叉树用合适的存储结构存储起来,然后还要求离两个顶点最近的两个顶点的公共祖先,最后将两个顶点到这个公共祖先之间的路径合并起来,才可以得到两个顶点之间的最短路径
算法实现:
- 由于这题需要求两个结点之间的公共祖先,所以将这棵树用三叉链表结构来存储将会使得算法实现容易的多,所以首先需要定义二叉树的节点结构
//定义二叉树的节点结构 typedef struct BitreeNode { char value; //数据域用于存放二叉树结点的值 struct BitreeNode* lchild, * rchild, * parent; //三个指针域分别用于存放该节点的左孩子,右孩子以及双亲结点 }BitreeNode,**BN,*bn; //定义二叉树,用三叉链表来存储此二叉树 typedef struct Bitree { bn root; //用于存放该二叉树的根结点 int NodeNum; //用于存放该二叉树的顶点个数 BitreeNode tn[N]; //用于存放该二叉树的结点 }Bitree,*BT;
定义好二叉树的节点结构之后就是创建此二叉树
-
创建此二叉树:创建此二叉树时用于需要用到递归算法,为了方便,我们可以像先序创建二叉链表那样先初始化此二叉树的结点值,以及左右孩子指针域,最后再遍历此二叉树在遍历的过程中初始化此二叉树的结点的双亲指针域,如果对如何根据先序序列创建二叉树的算法不了解的读者可以去看看我的这篇博客:http://t.csdn.cn/pil3L
//根据先序序列创建此二叉树,即先创建根结点,再创建左子树和右子树,对于左右子树的创建也遵循此规律 void CPartBitree(BN root) { //先创建根结点 (*root) = (BN)malloc(sizeof(BitreeNode)); //输入结点的值,直到输入的值为#为止 printf("请输入结点的值\n"); scanf("%c", &((*root)->value)); getchar(); if ((*root)->value == '#') { (*root) = NULL; return; } //再创建左子树 printf("请输入%c的左子树的根结点的值\n", (*root)->value); CPartBitree(&((*root)->lchild)); //再创建右子树 printf("请输入%c的右子树的根结点的值\n", (*root)->value); CPartBitree(&((*root)->rchild)); } //以下函数用于初始化树的双亲指针域 void init_parent(bn root, BT mytree) { //初始化双亲结点是典型的递归过程 if (root == NULL) { return; } mytree->tn[count] = (*root); count++; //先初始化根结点的左孩子右孩子结点的双亲指针域 if (root->lchild != NULL) { (root->lchild)->parent = root; } if (root->rchild != NULL) { (root->rchild)->parent = root; } //再去初始化根结点的左孩子的左右孩子的双亲指针域 init_parent(root->lchild,mytree); //再去初始化根结点的右孩子的左右孩子的双亲指针域 init_parent(root->rchild,mytree); } //完成树的创建需要刚才定义的两个函数来辅助完成 void Create_Bitree(BT mytree) { printf("请输入该二叉树的节点个数\n"); scanf("%d",&( mytree->NodeNum)); getchar(); CPartBitree(&(mytree->root)); mytree->root->parent = NULL; init_parent(mytree->root, mytree); printf("二叉树创建成功\n"); }
初始化此二叉树的双亲指针域的算法思路是这样的:我们先访问根结点,将根结点的左右孩子的双亲指针域赋值为根结点的值,然后再分别去初始化根结点的左子树的根结点的左右孩子的双亲指针域为根结点的左子树的根结点,初始化根结点的右子树的根结点的左右孩子的双亲指针域为根结点的右子树的根结点,所以递归思路已经出来了,对左右子树根结点的左右孩子双亲指针域的初始化操作和对根结点的左右孩子的双亲指针域的初始化操作一摸一样,所以只需要对根结点的左右子树递归调用此方法即可
-
创建好这个二叉树之后就是求此二叉树中任意两个结点之间的最短路径,二叉树中任意两个节点之间的最短路径这很好理解,就是从一个节点到另外一个节点需要经过哪些结点,所以思路是这样的求出离两个节点最近的两个结点的公共祖先,然后将两个结点到这个公共祖先的路径合并起来即可,要求离两个结点最近的公共祖先,我们可以先将两个结点到根结点的要经过的结点用两个数组存储起来,这就需要借助我们所创建的二叉树的双亲指针域来实现,然后依次遍历其中一个数组,直到被遍历的数组中的某一个元素出现在另一个数组中为止,那么这个公共元素就是离两个结点最近的两个结点的公共祖先
char c1, c2; printf("请输入你所需要求最短路径的两个结点\n"); while (scanf("%c%c", &c1, &c2) != EOF) { getchar(); BitreeNode b1, b2; //用于记录所需要求解的结点 for (int i = 0; i <= mybitree->NodeNum; i++) { if (mybitree->tn[i].value == c1) { b1 = mybitree->tn[i]; } if (mybitree->tn[i].value == c2) { b2 = mybitree->tn[i]; } } //将所要求的两个结点到根结点之间的路径求出来 char first[N]; int count1 = 0; char second[N]; int count2 = 0; while (b1.parent != NULL) { first[count1] = b1.value; b1 = *(b1.parent); count1++; } first[count1] = b1.value; while (b2.parent != NULL) { second[count2] = b2.value; b2 = *(b2.parent); count2++; } second[count2] = b2.value; //求解公共祖先 int flag1, flag2; for (int i = 0; i <= count1; i++) { if (is_exist(first[i], second, count2) != -1) { flag1 = i; flag2 = is_exist(first[i], second, count2); break; } else { continue; } } printf("%c与%c之间的最短公共路径为:\n", c1, c2); for (int i = 0; i < flag1; i++) { printf("%c->", first[i]); } for (int i = flag2; i >= 0; i--) { if (i == 0) { printf("%c", second[i]); } else { printf("%c->", second[i]); } } printf("\n"); printf("请输入你所需要求最短路径的两个结点\n"); }
代码如上
-
完整程序源代码:
//求二叉树的两个结点之间的最短路径 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 20 int count = 0; //定义二叉树的节点结构 typedef struct BitreeNode { char value; //数据域用于存放二叉树结点的值 struct BitreeNode* lchild, * rchild, * parent; //三个指针域分别用于存放该节点的左孩子,右孩子以及双亲结点 }BitreeNode,**BN,*bn; //定义二叉树,用三叉链表来存储此二叉树 typedef struct Bitree { bn root; //用于存放该二叉树的根结点 int NodeNum; //用于存放该二叉树的顶点个数 BitreeNode tn[N]; //用于存放该二叉树的结点 }Bitree,*BT; //先根据先序遍历顺序创建此二叉树,初始化其结点值信息以及左孩子右孩子指针域 void CPartBitree(BN root); //以下函数用于初始化树的双亲指针域 void init_parent(bn root, BT mytree); //完成树创建 void Create_Bitree(BT mytree); //如下函数用于判断一个值是否在数组里面,并返回其在数组中的下标,若不在数组中则返回-1 int is_exist(char c, char* p, int n) { for (int i = 0; i <= n; i++) { if (p[i] == c) { return i; } } return -1; } int main() { Bitree bt; BT mybitree = &bt; Create_Bitree(mybitree); char c1, c2; printf("请输入你所需要求最短路径的两个结点\n"); while (scanf("%c%c", &c1, &c2) != EOF) { getchar(); BitreeNode b1, b2; //用于记录所需要求解的结点 for (int i = 0; i <= mybitree->NodeNum; i++) { if (mybitree->tn[i].value == c1) { b1 = mybitree->tn[i]; } if (mybitree->tn[i].value == c2) { b2 = mybitree->tn[i]; } } //将所要求的两个结点到根结点之间的路径求出来 char first[N]; int count1 = 0; char second[N]; int count2 = 0; while (b1.parent != NULL) { first[count1] = b1.value; b1 = *(b1.parent); count1++; } first[count1] = b1.value; while (b2.parent != NULL) { second[count2] = b2.value; b2 = *(b2.parent); count2++; } second[count2] = b2.value; //求解公共祖先 int flag1, flag2; for (int i = 0; i <= count1; i++) { if (is_exist(first[i], second, count2) != -1) { flag1 = i; flag2 = is_exist(first[i], second, count2); break; } else { continue; } } printf("%c与%c之间的最短公共路径为:\n", c1, c2); for (int i = 0; i < flag1; i++) { printf("%c->", first[i]); } for (int i = flag2; i >= 0; i--) { if (i == 0) { printf("%c", second[i]); } else { printf("%c->", second[i]); } } printf("\n"); printf("请输入你所需要求最短路径的两个结点\n"); } return 0; } //根据先序序列创建此二叉树,即先创建根结点,再创建左子树和右子树,对于左右子树的创建也遵循此规律 void CPartBitree(BN root) { //先创建根结点 (*root) = (BN)malloc(sizeof(BitreeNode)); //输入结点的值,直到输入的值为#为止 printf("请输入结点的值\n"); scanf("%c", &((*root)->value)); getchar(); if ((*root)->value == '#') { (*root) = NULL; return; } //再创建左子树 printf("请输入%c的左子树的根结点的值\n", (*root)->value); CPartBitree(&((*root)->lchild)); //再创建右子树 printf("请输入%c的右子树的根结点的值\n", (*root)->value); CPartBitree(&((*root)->rchild)); } //以下函数用于初始化树的双亲指针域 void init_parent(bn root, BT mytree) { //初始化双亲结点是典型的递归过程 if (root == NULL) { return; } mytree->tn[count] = (*root); count++; //先初始化根结点的左孩子右孩子结点的双亲指针域 if (root->lchild != NULL) { (root->lchild)->parent = root; } if (root->rchild != NULL) { (root->rchild)->parent = root; } //再去初始化根结点的左孩子的左右孩子的双亲指针域 init_parent(root->lchild,mytree); //再去初始化根结点的右孩子的左右孩子的双亲指针域 init_parent(root->rchild,mytree); } //完成树的创建需要刚才定义的两个函数来辅助完成 void Create_Bitree(BT mytree) { printf("请输入该二叉树的节点个数\n"); scanf("%d",&( mytree->NodeNum)); getchar(); CPartBitree(&(mytree->root)); mytree->root->parent = NULL; init_parent(mytree->root, mytree); printf("二叉树创建成功\n"); }
代码如上
-
运行结果截图:
对于我的算法思路不理解的都可以问我,我看到之后都会回复,祝学习进步,生活愉快