王锐龙

导航

第五章小结

本章学的是二叉树以及其具体操作。

我们学习了二叉树的几个性质,说实话好像数学公式一样。性质如下

1)在二叉树的第i层上至多有2i-1个结点。

2)深度为k的二叉树上至多含2k - 1个结点(k≥1)。

3)对任何一棵二叉树,若它含有n0个叶子结点、n2个度为2的结点,则必存在关系式:n0 = n2 + 1。

4)具有n个结点的完全二叉树的深度为 (log2n) +1 。

5)对完全二叉树,若从上至下、从左至右编号,则编号为 i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2。

之后学了哈夫曼树,感觉上课时学的很带劲,但是课后左右还是挺吃力的。

上课的时候觉得树的结构不算太难,毕竟有规律可循。但是真正用起来的时候发现并没那么简单。拿下面这两题为例吧

首先是实践的第二题,深入虎穴。代码如下。

#include<iostream>
#include<queue> 
using namespace std;    

typedef struct{
    int doors;//代表门的数量
    int *p; //p指向具体门的编号 ,把p看作整型数组 
}node;

int input(node *&a)
{
    int n,m,i,k,j;
    bool *vi; 
    cin>>n;
    a=new node[n+1];//为a数组申请空间
    vi=new bool[n+1];//同上 
    for(i=1;i<=n;i++)//将vi数组初始化 
    {
        vi[i]=false;
    }
    for(i=1;i<=n;++i)
    {
        cin>>a[i].doors;
        a[i].p=new int[a[i].doors];
        for(k=0;k<a[i].doors;++k)//new的空间下标为0到n-1 
        {
            cin>>a[i].p[k];
            vi[a[i].p[k]]=true;
        }
    }
    for(j=1;j<=n;j++)
    {
        if(!vi[j]) break;
        
    }return j;
}

int find(node*a,int root)
{//从a数组root下标往下搜索 
    queue<int> q;//定义用于存放待访问的门编号的队列
    int x;
    q.push(root);//根编号入队 
    while(!q.empty())//队列非空 
    {
        x=q.front();
        q.pop();
        for(int i=0;i<a[x].doors;++i)
        { 
        q.push(a[x].p[i]);
         }     
    }
    return x;
}

int main()
{
    node *a;//定义一个动态的整型数组
    int root;
    root=input(a);//找根 
    cout<<find(a,root)<<endl;//从根开始寻找 
    //cout<<root;
    return 0;
}

这题是老师上课时带着我们一起做的。回来自己试的时候发现几个难点。首先第一个时动态申请的范围。申请是从第0个开始,所以有的循环要从i=0开始,不然会出错输不出答案。然后是find函数中使用队列,学到了用queue的头文件的一些使用方法,以及层次遍历的这种思想。

印象更深刻的是下面这道叶子节点的查找的题目。

代码如下

#include<iostream>
#include<queue> 
using namespace std;
  
typedef struct
{
    char lchild;//左孩子的下标 
    char rchild;//右孩子的下标 
}node;

void levelOrderTraverse(node a[],int x); 
int createBiTree(node a[]);
 
int main()
{
     node a[10];
     int x;
     x=createBiTree(a);//带回根节点
     levelOrderTraverse(a,x); 
}

int createBiTree(node a[])
{
    int n, i;
    char x,y;
    bool t[10]={false}; //用来查找根节点 
    cin>>n;

    for(i=0;i<n;i++)
	{
		cin>>x>>y;
    if(x!='-')//左节点 
    {
      a[i].lchild=x-'0';//-'0'的作用是减去0的ASCII值,用于转换 
      t[a[i].lchild]=true;
    }
    else a[i].lchild=-1;//无孩子 
        
    if(y!='-')//右节点处
    {
    a[i].rchild=y-'0';//同上 
    t[a[i].rchild]=true;//
    }     
    else a[i].rchild=-1;//无孩子 
    }

    for(i=0;i<n;i++)
	{//找根节点 
    if(!t[i])
    return i;
    }
}
void levelOrderTraverse(node a[],int x)
{//层次遍历 
    queue<int> q;
    int m;
    int leaves=0;//标记是第几个叶子结点
    q.push(x);//入队 
     
    while(!q.empty())
    {
        m=q.front();//取队头 
        q.pop();
        if(a[m].lchild==-1&&a[m].rchild==-1)
        {
            if(leaves)
            cout<<" ";
            cout<<m;
            ++leaves;
        }
        if(a[m].lchild!=-1)
        q.push(a[m].lchild);//左孩子入队
        if(a[m].rchild!=-1)
        q.push(a[m].rchild); //右孩子入队
    }
}

  这道题一开始没什么想法。特别时自上而下自左到右的输出方式,是老师讲了层次遍历后才有了点想法。这道题中,请教了一些同学,发现了一个转换字符数字的方法就是

a[i].lchild=x-'0'

这里的-‘0’实际上是减去了0的ASCII码,这样很容易的实现了转换。这题感觉和上一题很想,不过这题是自己敲出来的,感觉很不一样。特别是用上了新学到的队列处理,成就感还是很大的。

下一章的学习希望自己能跟紧老师的步伐吧,也要时刻提醒自己之前学习的知识可以用,就比如这次的队列一样。

posted on 2019-05-04 16:04  王锐龙  阅读(178)  评论(1编辑  收藏  举报