1.本周学习总结

1.1.思维导图


1.2.谈谈你对树结构的认识及学习体会。

   对于树结构的认识,它既可以顺序结构存储,也可以用链式结构存储,在刚刚预习树结构中的链式存储时,我感觉它的操作跟链表一样,结构体中的指针移来移去,没想到使用递归,看起来方便许多,但是,由于上学期没好好学递归,只是粗略的看了一遍,到了树这里开始还债了,在学习树的过程中又要把递归好好理解一边,勉强读懂了一些关于树的操作的代码,但是如果要求写,可能有些费劲, 但是再敲pta的表达式树时,发现树的结点还能用栈和队列存储,在我以前敲的pta题目中,只有数字和字符使用过栈和队列,没想到自己建的结构体也能用栈和队列来操作,知识果然是一直叠加的。课本在介绍树形结构时,写了许多关于树的专属名词,什么孩子结点、双亲结点、兄弟结点等等,概念非常多,都需要我们理解。二叉树是特殊的一种树,而哈夫曼树又是一种特殊的二叉树,对于哈夫曼树,一位哈夫曼树只有下图这一种,根节点的一个结点为叶子结点,没想到做课堂派题目还有多种的哈夫曼树结构,不局限于下图那种。上周末参加了acm志愿者活动,跟一个参赛的学长交流时,他谈到树的这一章挺重要的,他们考的一题中有需要建立哈夫曼树,可以看出数据结构有多重要。

2.PTA实验作业

2.1.题目1:6-4 jmu-ds-表达式树

2.1.1设计思路

void InitExpTree(BTree &T, string str) 
建立字符栈opchar 
建立树结点栈node
创建树结点p等于NULL,a,b 
while  str[i]!='\0'  
     if str[i]是数字0到9 then 
        将结点存入树栈node中      
     else if str[i]是运算符 then
         if opchar栈为空 then 
             将字符存入opchar栈中 
         else
             f = Precede(op.top(), str[i])   //栈顶元素与字符的优先级 
                switch f                       
                    case:'>':                  //优先级比栈顶元素低 
                        取node栈顶两个元素并赋值给a,b
						利用CreateExpTree建树
						opchar出栈 
						再将新建的树入栈 
                    case '<':
					    字符入栈               //优先级比栈顶元素高
                    case '=':
                    	opchar栈出栈           //优先级与栈顶元素相同 
end while    

while   opchar不空且node栈也不空   
     取node栈顶两个元素并赋值给a,b
	 利用CreateExpTree建树
	 opchar出栈 
	 再将新建的树入栈           
end while



double EvaluateExTree(BTree T)
定义浮点数sum,b,m
建立浮点型栈num
建立字符栈s
建立树类型的栈node
while T!=NULL 
     将每个结点存入s,以及将结点T存入node中
end while
while s!=NULL
    if str[i]是数字0到9 then
       将字符转化浮点型数并存入num中
    else 
        取栈顶的两个元素赋值给sum,b
		出栈 
        switch s的栈顶元素
            case '+':sum += b; 
            case '-':sum -= b; 
            case '*':sum *= b;
            case '/':
                if b==0 then  
                    输出divide 0 error!\n
                    退出程序
            else sum /= b;
            end if 
             
    s栈出栈 
    将sum入到num栈     
返回sum

2.1.2代码截图



2.1.3本题PTA提交列表说明。



1.在用vs运行程序时,老是会跳出下图的问题,然后将代码提交到pta上,发现是段错误,然后问了大佬,可能出现了野指针或者是没有出栈,后面发现自己忘记出栈了,导致程序一直循环

2.在计算表达式时,忘记设置返回值了。

3.由于EvaluateExTree返回值为浮点型,但是当除数等于0时,必须返回一个值,刚开始不知道怎么做,后面舍友告诉我可以用exit(0)直接退出程序。

2.2 题目2:7-2 根据后序和中序遍历输出先序遍历

2.2.1设计思路

int main()

    定义 n, i
    定义一个树节点T;
    输入 n
    for i=0 to n do 
        输入后序排列的元素 
    end for
    for i=0 to n do 
        输入中序排列的元素 
    end for
    利用Build函数建树 
    输出"Preorder:"
    利用PreOrder函数输出前序序列 
    return 0

BiTree Build(int *in, int *post, int n)
    定义整型变量len, 整型指针*p
    if n <= 0 then
        return NULL
    end if 
    for p=in to in+n  
        if  *p == *(post + n - 1) then 
		    break;
		end if        
    enf for
    建立新结点T
    T->data = *p
    len = p - in;
    T->lchild = Build(in, post, len);
    T->rchild = Build(p + 1, post + len, n - len - 1);
    返回T

void PreOrder(BiTree T)
    if T!=NULL then 
        输出 T->data;
        PreOrder(T->lchild); // 利用递归前序排列 
        PreOrder(T->rchild);
    end if 

2.2.2代码截图



2.2.3本题PTA提交列表说明。


1.在头两次次的提交中,弄错了循环条件,程序一直退不出来。
2.刚开始还不知道怎么写,后来看了书,勉强理解了,然后自己敲,错误百出。然后再次百度代码,发现自己没有把递归口弄好。才导致了这么多次的答案错误。

2.3 题目3:7-4 jmu-ds-二叉树叶子结点带权路径长度和

2.3.1设计思路

int main() 
   定义str数组
   输入str
   定义wpl为带权路径长度
   利用creat函数建树
   利用GetWPL函数计算带权路径长度和 
 
BiTree create(string str, int n) 
   建立新的树节点 BT
   if 字符为# then 
      返回NULL 
   end if
   if n的值大于数组长度 then 
      返回NULL 
   end if
   BT->data = str[n];                                                                                       
   BT->lchild = create(str, 2 * n); //使用递归建树
   BT->rchild = create(str, 2 * n+1);
   返回 BT;

void GetWPL(BiTree bt,int h,int&wpl)
   if bt==NULL then 
      结束函数 
   end if 
   if bt左右孩子都不空 then  
      wpl=wpl+(bt->data-'0')*h;
   end if 
      GetWPL(bt->lchild,h+1,wpl);  //使用递归计算带权路径长度和 
      GetWPL(bt->rchild,h+1,wpl);

2.3.2代码截图


2.3.3本题PTA提交列表说明。




1.多次发生段错误是因为忘记设置递归口了,程序跳不出来。
2.在建树和计算表达式利用自己定义的变量会发生错误,所以直接用数字传参。
3.再写建树的函数时,只考虑到str[i]=‘#’这种情况,没有想到如果传进去的n如果大于字符串的长度会怎么样。

3、阅读代码

3.1 题目

3.2 解题思路

根据前序、中序序列还原建树,然后镜面反转,就是将非叶子节点的左右孩子互换,最后层序遍历输出这棵树。

3.3 代码截图

#include<iostream>
#include<queue>
#define maxn 1000

using namespace std;

int n;
int qian[1000],zh[1000];
struct node
{
    int l,r;
}pp[1000];

int build(int la,int ra,int lb,int rb)
{
    if(la>ra)
        return 0;
    int root,p1,p2;
    root=qian[lb];
    p1=la;
    while(zh[p1]!=root)
        p1++;
    p2=p1-la;
    pp[root].l=build(la,p1-1,lb+1,lb+p2-1);
    pp[root].r=build(p1+1,ra,lb+p2+1,rb);
    return root;
}
void fan(int root)
{
    if(pp[root].l || pp[root].r)
    {
        int temp = pp[root].l;
        pp[root].l = pp[root].r;
        pp[root].r = temp;
        if(pp[root].l)
            fan(pp[root].l);
        if(pp[root].r)
            fan(pp[root].r);
    }
}
void level()
{
    queue<int>q;
    q.push(qian[0]);
    cout<<qian[0];
    while(!q.empty())
    {
        int temp = q.front();
        q.pop();
        if(temp!=qian[0])
            cout<<' '<<temp;
        if(pp[temp].l)
            q.push(pp[temp].l);
        if(pp[temp].r)
            q.push(pp[temp].r);
    }
    cout<<endl;
}
int main()
{
    scanf("%d",&n);
    for(int i = 0; i < n; i++)
        cin>>zh[i];
    for(int i = 0; i < n; i++)
        cin>>qian[i];
    build(0,n-1,0,n-1);
    fan(qian[0]);
    level();
    return 0;
}

3.4 学习体会

初看这道题·,思路并不难,我以为直接按书本上的代码建树,然后在交换非叶子节点的左右孩子就行了,但是看到大佬写的代码,真的给跪了,他的结构体中只有整型的r与l代表着左右孩子,方便了下面编写交换非叶子结点的左右孩子,如果要是我来写的话,我肯定又要建栈,入栈,出栈,这样即增加了程序的空间复杂度,写出来的代码又难懂,而大佬直接用上学期交换数字来写,简单易懂,下面的输出则是用队列来写,而最主要的建树函数,他把里面的递归条件拿捏的特别好,和树上用链式存储结构差不多,用数组来存储二叉树看起来要比链式存储更容易理解,真的为那些acm大佬的脑回路给惊到了,听说这只是签到题,对于我来说,看起来简单,写起来难,看来还是和大佬有着巨大的差距。