凉城c

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、本章内容小结

  本章节的内容主要是树和二叉树,其中包含了它们的定义、性质、存储结构等。通过对本单元的学习,我们学会了如何构造树和二叉树这样的数据结构,并且我们可以通过作业、实践以及书本上的案例去知道这样的数据结构在实际应用中可以解决的问题。

  树是n个节点的有限集,当n=0时,它就代表的是一棵空树;当n>0时,它就是一颗非空树。树的存储结构有两种,第一种是双亲表示法,第二种是孩子表示法。这里以双亲表示法为例:

struct NODE
{
  char data;
  NODE* father;
};

对于树中的每个节点,我们定义两个成员变量:节点的数据和指向该节点双亲的指针。在这样的存储结构下,我们求节点的双亲非常便捷,也很容易求出树的根。但是我们在实际操作的过程中,有一个特别需要注意的地方就是要把根节点的双亲指针设为NULL,否则根节点的双亲指针就会是一个未初始化的野指针,这样的后果就是当我们写的代码涉及到根节点的双亲指针时,就会出现指针非法访问的情况,程序立刻崩溃。

  双亲表示法,顾名思义,就是通过结点的双亲指针将该节点的双亲相连,假如我们需要创建一颗包含n个节点的树,并且下标为i的节点是下标为j的节点的双亲,那么我们可以:

 

NODE* tree = new NODE[n];

tree[j].father = &tree[i];

通过这样的方法,我们就可以将一个个节点连接起来,从而形成一棵完整的树。

 

 

  而二叉树是树的一种,但其特殊的地方在于每个节点包含两个子树:左子树和右子树。这些子树也可以为空树,我们定义节点的时候就可以这么定义:

struct NODE
{
  char data;
  NODE* lchild;
  NODE* rchild;
};

如果有子树为空,那我们需将该子树的指针设为NULL,实际操作的方法和双亲表示法如出一辙。

 

二、完成作业实践时的心得体会

(1)关于指针初始化的问题

  在编程的过程中,虽然我们会有机会对绝大部分的变量重新赋值,但是总是会有一些我们漏掉的变量。我们知道如果一个int型变量没有被初始化,那么它输出的时候就会是一个很奇怪的数字,指针没初始化则会导致指针非法访问。在完成第五章实践第一题的时候,我们就很容易漏掉那些本应该为空的指针,所以我们一定要记得将这些指针设为NULL。或者我们一开始的时候就将所有的指针通过一个for循环设为NULL,这样做虽然运行效率低了一些,但是这样处理可以更加保险,避免不必要的麻烦。

for (i = 0; i < N1; i++)
{
tree1[i].c = '-';
tree1[i].F = NULL;
tree1[i].L = NULL;
tree1[i].R = NULL;
}

 

(2)通过一棵树的节点的数据元素,找到另一棵树中包含该数据元素的节点

  在完成第五章实践第一题时,我发现找另一棵树的对应节点比较复杂,最后我发现我们可以通过下标累加得到该节点:

j = 0;

while (tree1[i].c != tree2[j].c)
++j;

大致思路就是当tree1中下标为i的节点的数据元素不和tree2中下标为j的节点的数据元素相同时,我们就让tree2的下标值+1,再重新比较,直至两个数据元素相同时为止。最开始比较时,我们需让j=0,也就是从tree2的第一个节点开始比较。

 

(3)如果指针为空,那么通过该指针来操作结构体变量时就会出现异常

  在完成第五章实践第一题时,我最开始是通过以下方法进行比较的:

if ((tree1[i].L->c != tree2[j].L->c || tree1[i].R->c != tree2[j].R->c) && (tree1[i].L->c != tree2[j].R->c || tree1[i].R->c != tree2[j].L->c))
{
  cout << "No";
  delete tree1;
  delete tree2;
  return 0;
}

但提交后发现它有很严重的段错误,原因是如果tree[i].L或tree[i].R为空,那么我们对tree[i].L->c或tree[i].R->c操作是错误的,最后我通过设置四个char型变量,避免了访问这样的问题:

if (tree1[i].L == NULL)
cL1 = '-';
else
cL1 = tree1[i].L->c;

if (tree1[i].R == NULL)
cR1 = '-';
else
cR1 = tree1[i].R->c;

if (tree2[j].L == NULL)
cL2 = '-';
else
cL2 = tree2[j].L->c;

if (tree2[j].R == NULL)
cR2 = '-';
else
cR2 = tree2[j].R->c;

if (tree1[i].L == NULL&&tree1[i].R==NULL)
{
if (tree2[j].L != NULL || tree2[j].R != NULL)
{
cout << "No";
delete tree1;
delete tree2;
return 0;
}
}

if (tree1[i].L != NULL&&tree1[i].R == NULL)
{
if (tree2[j].L != NULL||tree2[j].R != NULL)
{
cout << "No";
delete tree1;
delete tree2;
return 0;
}
}

if ((tree1[i].L->c != tree2[j].L->c || tree1[i].R->c != tree2[j].R->c) && (tree1[i].L->c != tree2[j].R->c || tree1[i].R->c != tree2[j].L->c))
{
cout << "No";
delete tree1;
delete tree2;
return 0;
}

 

三、参考的资料

  本章的学习主要参考书本上的知识,书本上提供了一些基本功能的实现,但描述得太抽象,不好理解,所以我通常参考的另一个途径是百度。

  https://www.jianshu.com/p/bf73c8d50dc2

 

四、待改进的问题

  理解能力需要加强,抽象的东西需要通过更多的实践去搞明白。

 

五、接下来的目标

  总结我们以前学过的解题方法,巩固以前所学知识。其实章与章的知识都是有不少相关联的部分的,如果将学过的东西研究的再透彻一些,我想学习新的知识就可以更加简单。

posted on 2019-05-04 15:04  凉城c  阅读(167)  评论(2编辑  收藏  举报