目录
Data structure advanced training course notes and algorithm exercises 数据结构进阶实训课程笔记和算法练习
Source Code: https://github.com/MysticalGuest/DataStructure/tree/master/AdvancedJuniorTraining
题目1
判断二叉树是否为正则二叉树。
- 正则二叉树的定义:指在二叉树中不存在度为1的分支点。
1.1 算法设计思想
利用二叉树遍历递归的思想;
先判断当前节点是否正则;
然后递归判断该节点的左右子树。
1.2 源代码
// 1代表是正则二叉树,0代表不是
int IsRegular(BiTree T){
if(T==NULL)
return 1;
else if(T->LChild==NULL ^ T->RChild==NULL)
return 0;
else{
IsRegular(T->LChild);
IsRegular(T->RChild);
}
}
1.3 运行情况截图
题目2
判断二叉树是否为完全二叉树?
2.1 算法设计思想
一个树是否为完全二叉树,每个节点有以下4中情况:
情况一: 情况二: 情况三: 情况四:
A A A A
/ \ / \ / \ / \
B C B NULL NULL B NULL NULL
规律是:
(1)如果当前访问的节点的左右孩子是情况三,说明不是完全二叉树,直接返回false;
(2)如果当前访问的节点的左右孩子是情况1,继续访问其他节点;
(3)如果当前访问的节点的左右孩子是情况2或者情况4,那么我们定义一个状态(接下来访问的所有节点必须全部是叶子节点)。只要遇到情况2或者情况4,这个状态就开启了。
算法就是层次遍历所有节点并做判断。
2.2 源代码
BOOL IsCBT(BiTree bt){
if(bt==NULL) // 空树
return TRUE;
BOOL leaf = FALSE;
SeqQueue Q;
BiTree p;
InitQueue(&Q);
EnterQueue(&Q, bt);
while(!IsEmpty(Q)){
DeleteQueue(&Q, &p);
if(p->LChild==NULL && p->RChild!=NULL) // 情况3: 当前节点有右孩子,没有左孩子
return FALSE;
//上述的状态已经发生,但是当前访问到的节点不是叶节点(有左孩子或者右孩子)
if(leaf && (p->LChild!=NULL||p->RChild!=NULL))
return FALSE;
if(p->LChild!=NULL) //左孩子不为空,加入到队列中去
EnterQueue(&Q, p->LChild);
if(p->RChild!=NULL) //右孩子不为空,加入到队列中去
EnterQueue(&Q, p->RChild);
//这个if语句就是判断状态是否要发生
if((p->LChild!=NULL && p->RChild==NULL)||(p->LChild==NULL && p->RChild==NULL))
leaf=TRUE;
}
return TRUE;
}
2.3 运行情况截图
题目3
二叉树二叉链表存储,结点数据域的值为整数,且取值各不相同。
编写代码判断该二叉树是否为二叉排序树。
二叉排序树,又称二叉查找树。它或者是一颗空树,或者是具有下列性质的二叉树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的的左、右子树也分别为二叉排序树。
3.1 算法设计思想
由于二叉排序树的中序遍历得到的是一个单调递增的序列;
所以根据这个想法,我们可以中序遍历这个二叉树,将得到的序列存入temp数组;
通过检验temp数组的单调递增性来判断这个二叉树是否为二叉排序树。
3.2 源代码
#define N 100
int temp[N];
int i = 0;
void inorder(BiTree root){
if(root == NULL)
return;
if(root->LChild != NULL)
inorder(root->LChild);
temp[i++] = root->data;
if(root->RChild != NULL)
inorder(root->RChild);
}
int ISBST(int temp[], int k){
int flag=0;
for(int i=1; i<k; i++)
if(temp[i]<temp[i-1])
return 0;
return 1;
}
3.3 运行情况截图
题目4
二叉树二叉链表存储,结点数据域的值为整数,且取值各不相同。
编写代码判断该二叉排序树是否为平衡二叉排序树。
- 平衡二叉树,是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。
4.1 算法设计思想
通过平衡二叉树的定义可以将判断平衡二叉树的条件分为以下2个:
(1)首先是一颗二叉排序树;
(2)每个节点的左右子树的高度差至多为1;
基于以上两个条件写两个方法;
第一个条件思想和上一题一样,判断二叉排序树;
第二个条件通过递归判断每个节点的左右子树高度;
将两个条件返回值综合,就可以判断出。
4.2 源代码
int Deep(BiTree bt){
int ld=0,rd=0;
if(bt){
ld=Deep(bt->LChild)+1;
rd=Deep(bt->RChild)+1;
}
return ld >= rd?ld:rd;
}
BiTree pre=NULL;
BOOL ISAVL(BiTree root){
int lcd=0,rcd=0;
if(root!=NULL){
int l = ISAVL(root->LChild);
// printf("pre: %d\n", pre->data);
lcd=Deep(root->LChild); // 左子树的深度
rcd=Deep(root->RChild); // 右子树的深度
// printf("Deep(root->LChild): %d\n", Deep(root->LChild));
// printf("Deep(root->RChild): %d\n", Deep(root->RChild));
// printf("root: %d\n", root->data);
if(abs(lcd-rcd)>1){ // 条件1:每一个节点的左子树和右子树的高度差至多等于1
return FALSE;
}
if(pre!=NULL){
if(pre->data > root->data){ // 条件2:中序遍历的前驱节点大于后面节点的值,就不是平衡二叉树
return FALSE;
}
}
pre=root;
int r = ISAVL(root->RChild);
return l && r;
}
return TRUE;
}
4.3 运行情况截图
题目5
编写代码完成:输入一棵二叉树,输出它的镜像。
5.1 算法设计思想
利用二叉树遍历递归的思想;
先交换左右子树;
然后分别镜像左右子树。
5.2 源代码
void swap(BiTree *node1, BiTree *node2){
BiTree temp;
temp = *node1;
*node1=*node2;
*node2=temp;
}
void Mirror(BiTree *bt){
if((*bt)==NULL)
return;
swap(&((*bt)->LChild), &((*bt)->RChild));
Mirror(&((*bt)->LChild));
Mirror(&((*bt)->RChild));
}
5.3 运行情况截图
题目6
输入:一个整数和一棵二叉树(树中结点的数据值为int);
输出:二叉树中结点值的和为输入的的整数的所有路径。
路径的定义:从树的根结点开始往下一直到叶子结点形成的,称为一条路径。
6.1 算法设计思想
用先序遍历的方式访问节点,使用栈数组ResultStack存储满足条件的路径,使用栈SeqStack存储当前路径节点。
遍历二叉树的过程:按先序遍历顺序访问每一个节点,访问每个结点时,将结点添加到SeqStack中。
如果当前结点是叶子结点,则判断当前路径是否是符合条件的路径,符合条件的路径存入到栈数组ResultStack;
如果当前结点不是叶子结点,则递归当前节点的左右子节点。
6.2 源代码
SeqStack ResultStack[10];
int i=0;
void SumPath(BiTree bt, SeqStack *S, int value){
BiTree p;
Push(S, bt);
if(bt){
if(!bt->LChild && !bt->LChild){
if(value == bt->data){
ResultStack[i] = *S;
i++;
}
}
else{
SumPath(bt->LChild, S, value-bt->data);
SumPath(bt->RChild, S, value-bt->data);
}
if(!IsEmpty(*S))
Pop(S, &p);
}
}
6.3 运行情况截图
题目7
输入:一个整数数组,判断该数组是否为某二叉排序树的后序遍历序列;
输出:若是,则返回true,若不是,则返回false;
假设该数组中的任何两个数值都互不相同。
7.1 算法设计思想
后续遍历中,最后一个数字是根结点,将数组中的数字分为两部分:
第一部分是左子树的值,它的值都比根结点小;
另一部分是右子树的值,它的值都比根结点大;
后续遍历(5,7,6,9,11,10,8)的最后一个结点是8,所以在这个数组中,5,7,6都比8小时该数的左子树;而9,11,10都比8大,是该树的右子树。
我们以同样的方法来分析其左子树和右子树5,7,6,其中6将左子树分为5和7两部分;10将右子树9和11分为两部分。所以这个序列就是一个后续遍历序列。但是(7,4,5,6)就不是它的一个后续遍历序列。因为6大于7,所以也就是说7,4,5都是其右子树,但是很不幸还有4比6小,所以不可能是一个后续遍历。
7.2 源代码
BOOL VerifySequenceOfBST(int *array,int length){
if(array==NULL || length<=0)
return FALSE;
int root=array[length-1];
int i=0; //左子树的结点小于根节点;
for(;i<length-1;i++){
if(array[i]>root)
break; //找完了全部的左子树的序列;
}
int j=i;//右子树的结点大于根结点;
for(;j<length-1;j++){
if(array[j]<root)
return FALSE;
}
BOOL left=TRUE;
if(i>0)
left=VerifySequenceOfBST(array,i);
BOOL right=TRUE;
if(j<length-1)
right=VerifySequenceOfBST(array+i,length-i-1);
return left && right;
}
7.3 运行情况截图