开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情
小白专场:是否同一棵二叉搜索树 - C实现
题意理解及搜索树表示
是否是同一棵二叉搜索树
求解思路
两个序列是否对应相同搜索树的判别
-
分别建两棵搜索树的判别方法:根据两个序列分别建树,再判别树是否一样
-
不建树的判别方式
- 先看根结点也就是第一个数一样吗?再把比根结点小的放左边(顺序不动),比根结点大的放右边(顺序不动),然后再进行对比左右树,上图就是一样的,下图为反面例子
-
建一颗树,再判别其他序列是否与该树一致
- 求解思路
- 搜索树表示
- 建搜索树T
- 判别一序列是否与搜索树T一致
-
搜索树表示 typedef struct TreeNode*Tree; struct TreeNode{ int v;//v来表示基本信息 Tree Left,Right; int flag;//flag是一个阈(用来判别一个序列是不是跟树一致,实际效果就是如果这个结点没有被访问过flag设为0,被访问过了就设为1) };
程序框架及建树
程序框架搭建
int main()
{
对每组数据
1.读入N和L
2.根据第一行序列建树T
3.依据树T分别判别后面的L个序列是否能与T形成同一搜索树并输出结果//分别读入L个序列来跟T做比较,看是不是一致的,如果说一致的,他所对应的搜索树是一样的,一样输出yes,不一样输出no
return 0;
}
根据上方框架,我们需要设计的主要函数:
1.读数据建搜索树T//读入N个数据来建我们的搜索树,将来所有序列都会跟这个T去比较,所以以T基准
2.判别一序列是否与T构成一样的搜索树
int main()
{
int N,L,i;
Tree T;
scanf("%d",&N);
while(N){//N如果为0输出就结束了
scanf("%d",&L);
T = Make Tree(N);//读入后面N个数来建我们的树T
for(i = 0; i < L;i++ ){//比较是不是跟T一致的这样的序列
if(Judge(T,N))printf("Yes\n");//用函数Judge读入后面的N个数然后跟T去做比较,所以这样Judge有两个参数,一个是树T,一个是序列的个数。通过Judge来判别,如果一致就print yes 反之print no
else printf("No\n");
ResetT(T);//清除T中的标记flag
}
Free Tree(T);//释放掉树占有的空间,因为已经处理完了准备处理下一组数据了,下一组数据需要的空间可能不需要这么多也可能远远不够
scanf("%d",&N);
}
return 0;
}
如何建搜索树
Tree Make Tree(int N)
{
Tree T;
int i,V;
scanf("%d",&V);//读入第一个数,然后把数放到V里面去
T = NewNode(V);//为V构造一个对应的节点,然后赋给T,T只含一个节点
for(i=1;i<N;i++){//这里是循环N-1次噢
scanf("%d",&V);
T = Insert(T,V);//将V一个个插到T里面去
}
return T;
}
Tree NewNode(int V)
{
Tree T = (Tree)malloc(sizeof(struct TreeNode));
T->v = V;
T->Left = T->Right = NULL;
T->flag = 0;
return T;
}
怎么把树插到T里面去呢?引用insert这个函数
Tree Insert(Tree T, int V)
{
if(!T)T = NewNode(V);//如果T空了,那就调用NewNode来产生这样一个节点
else{
if(V > T->v)//如果不空就做比较
T->Right = Insert(T->Right,V);
else
T->Left = Insert(T->Left,V);
}
return T;
}
搜索树是否一样的判别
方法:在树T中按顺序搜索序列
1.如果每次搜索所经过的结点在前面均出现过,则一致
2.否则(某次搜索中遇到前面未出现的结点),则不一致
去在T中搜索我们序列中的每一个整数,实际上就是个查找过程
int check(Tree T,int V)
{
if(T->flag){
if(V<T->v)return check(T->Left,V);
else if(V->T->v)return check(T->Right,V);
else return 0;
}
else{
if(V == T->v ){
T->flag = 1;
return 1;
}
else return 0;
}
}
如何辨别
//有bug版本
int Judge(Tree T,int N)
{
int i,V;
scanf("%d",&V);
if(V!=T->v)return 0;
else T->flag = 1;
for(i=1;i<N;i++){
scanf("%d",&V);
if(!check(T,V))return 0;
}
return 1;
}
//当发现序列中的某个数与T不一致的时候,必须把序列后面的数都读完。这样才能保证我们下一个要求的序列是我们真正想要的序列 ,这个有bug的程序就是不能把后面的数都读完
以下是修改版本,对整个程序的一个标记而不是对结点的标记
int Judge(Tree T,int N)
{
int i,V,flag = 0;//flag:0代表目前还一致,1代表已经不一致
scanf("%d",&V);
if(V!=T->v)flag = 1;
else T->flag = 1;//T->flag是结点上的标记,flag是整个的标记,是不一样的
for(i = 1;i < N;i++){
scanf("%d",&V);
if((!flag)&&(!check(T,V)))flag = 1;
}
if(flag)return 0;
else return 1;
}
void ResertT(Tree T)//清除T中各结点的flag标记
{
if(T->Left) ResetT(T->Left);
if(T->Right) ResetT(T->Right);
T->flag = 0;
}
void FreeTree(Tree T)//释放T的空间
{
if(T->Left) FreeTree(T->Left);
if(T->Right)FreeTree(T->Right);
free(T);//递归方法
}
线性结构之习题讲选[陈越]:Reversing Linked List
线性结构习题1:什么是抽象的链表
链表是一个抽象的数据结构
- 有块地方存数据
- 有块地方存指针——下一个结点的地址(一个抽象的指针指向的就是地址,任何一种形式去存了下一个结点的位置这东西就叫做指针)
线性结构习题2:链表逆转算法
单链表的逆转
第一个加上头结点虽然会浪费一个空间,但是会使后面的操作变为更加简单
逆序之后如下:
接下来是逆序的过程:
-
首先需要对图中定义的词汇进行解释
- new:指的是新的已经逆转好的链表,他的头结点的位置
- old: 指的是旧的还没有逆转好的老链表,他的头结点位置
-
我们首先想要把2跟1进行逆转,但是在逆转之前需要先把3这个位置记住,所以设定一个tmp指针去指向3。否则当2的指针一转向,2后面的链表就丢失了
-
以下是伪代码实现 Ptr Reverse(Ptr head,)//head是一个指针 { cnt = 1 new = head->next;//刚开始指向1的 old = new->next;//还没有逆转的那个头结点 while(cnt < K){ tmp = old->next;//记住下一个的位置 old->next = new;//指针逆转指向新的结点 new = old;old = tmp;//往前位移一段 cnt++; } head->next->next = old;//把开头那个空结点指向结尾的(因为逆转了,最开头的现在变为最末尾的了) return new; } //取巧:用顺序表存储,先排序,再直接逆序输出
线性结构习题3:测试数据
- 有尾巴不反转
- 地址取到上下界
- 正好全反转
- K = N是全反转
- K = 1不用反转
- 最大(最后剩K-1不反转)、最小N
- 有多余结点
2-6是属于边界测试
-
边界测试
- 意思就是给你一个数值,你就一定要取到那个范围上界和下界