庞茜丹

导航

第五章学习小结

1.二叉树的遍历以及哈夫曼树是本章的重点。通过PTA和小组讨论,让我更能想清楚二叉树在存储的时候的模样,也能利用链式和顺序结构操作二叉树,总结如下;哈夫曼树比较清晰,主要是构造(要点是先取权值最小的树作为左右子树构造一棵新的二叉树)与计算WPL=(该结点的权值*对应的结点的路径长度)的求和。

 

2 //存储结构:

//顺序: 3 typedef struct 4 {Elemtype data;//该结点的数据 5 int lch,rch;//该结点左右孩子的下标 6 }TNode; 7 typedef struct 8 {TNode tn[MAX];//TNode *tn 动态申请 9 int root; 10 }Tree; 11 //访问写法:T.tn[i].lch 12 //链式: 13 typedef struct TNode 14 {Elemtype data; 15 struct TNode *lch,*rch; 16 }TNode,*TNodeList;//链表TNodeList可以代表树 17 //访问写法:T->lch 18 19 //建立二叉链表(先序)(递归) 20 void createBTree(BTree &T) 21 { 22 cin>>ch; 23 if(ch=='#') T=NULL://终止条件 24 else 25 { 26 T=new BTNode;//每一棵树(包括子树)的根结点 27 T->data=ch; 28 createBTree(T->lchild);//递归创建左子树 29 createBTree(T->rchild);//递归创建右子树 30 } 31 } 32 33 //各种遍历访问:先序、中序、后序、层次 34 //中序(递归) 35 void InOrder(BTree T) 36 {if(T)//二叉树非空 37 { 38 InOrder(T->lchild);//A 39 operate(T->data);//B 40 InOrder(T->rchild);//C 41 } 42 }//先序:B、A互换 ;后序:B、C互换 43 //中序(非递归,用栈)(链式存储) 44 void InOrder(BTree T) 45 {InitStack(S); 46 BTNode *p=T,*q=new BTNode; 47 while(p||!StackEmpty(S)) 48 { 49 if(p)//如果树非空 50 {push(S,p);//根指针先入栈,接着是所有左子树的所有左孩子入栈 51 p=p->lchild;//遍历此结点的左子树,深度由浅到深直到叶子 52 } 53 else 54 {pop(S,q);//把已经入栈的左子树的左结点 由深度深到浅 出栈,用q记录 55 cout<<q->data;//输出当前子树的根结点q的值 56 p=q->rchild;//让p指向当前结点q的右孩子,如果q没有右孩子,不执行if(p),继续让栈里面的元素出栈;
//如果q不是叶子结点(有右孩子),就可以让q的右孩子在push(S,p)入栈

57 }
58 }  
59
}
60
//先序(非递归,用栈)(链式存储) 61 void PreOrder(BTree T) 62 {stack<struct Tnode *> s;

63 s.push(root); //根结点先入
64
struct Tnode *nd;

65 while (!s.empty())

66 {
67
nd= s.top(); //先取栈顶元素

68 operate(nd);

69 s.pop(); //再出栈顶元素

70 if (nd->right != NULL) //先右孩子进

71 s.push(nd->right);

72 if (nd->left != NULL)//再左孩子进

73 s.push(nd->left);
74
}

75 }

76 //层次(非递归,用队)(顺序存储)

77 void LevelOrder(Tree t)

78 {

79 queue<int>Q;//定义一个辅助的数据结构:队列Q ,用来存储待访问的结点编号即下标(int类型)

80 Q.push(t.root);// 用下标代替根结点,根结点先入队,此时队列里只有根节点下标,(不一定

81 //会按123456…… ,下一个队员将会是这个结点的左右孩子 ,)

82 //这样就保证了结点是从上往下、从左往右进入队列的,再按先进先出的规则输出,就能满足“层次遍历 ”

84 int k;
85
bool flag=false;//为了按格式输出

87 while(!Q.empty())//( 先将根结点入队, 才能先保证Q.empty()非空 )

88 {

89 k=Q.front();//先获取队头元素,用下标代表结点

90 Q.pop();//队头元素出队

91 if(t.data[k].lchild==-1 && t.data[k].rchild==-1)//如果这个队头元素是叶子结点

92 {

93 if(flag==false)//第一个输出

94 {

95 cout<<k;
96
flag=true;

97 }
98
else

99 cout<<" "<<k;//输出正在叶子结点的下标

100 }

101 else//如果 这个队头元素不是叶子结点 ,让它的非空左右孩子依次入队 等待判断输出

102 {
103 if(t.data[k].lchild!=-1) Q.push(t.data[k].lchild) ;//t.data[k].lchild代表的是k号结点的左孩子的下标

104 if(t.data[k].rchild!=-1) Q.push(t.data[k].rchild) ;

105 }

106 } //while

107 }

几点:

  • 二叉树的子树是有左右之分的,其次序不能任意颠倒,如先序遍历的123与132(根结点是1,2和3是它的孩子)是不同的;
  • bool *a; a=new bool[n]();可以实现a数组的初始化,全为false;

2.心得:在List leaves中知道了层次遍历可以利用队列先进先出的特点实现,让我眼前一亮;而在用非递归的方法实现遍历要用到栈,这些都和之前的知识联系在了一起;还在小组讨论中发现,可以根据不同遍历的特性更容易地达到某些操作要求,这次就是利用了层次遍历的最后结点即为叶子结点的特点。

3.目标回顾:这一章刚接触树的时候没能及时反应过来它使用顺序、链式存储时的操作,模模糊糊地被动跟着进度,但是现在已经明朗了许多;

下期目标:目前感觉图也不是一个省油的灯,这章得花更多心思多琢磨,进度也不能拖。

posted on 2020-05-31 20:31  庞茜丹  阅读(134)  评论(0编辑  收藏  举报