数据结构第四周笔记(2)——树(中)(慕课浙大版本--XiaoYu)

113 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

小白专场:是否同一棵二叉搜索树 - C实现

题意理解及搜索树表示

是否是同一棵二叉搜索树

求解思路

两个序列是否对应相同搜索树的判别

  1. 分别建两棵搜索树的判别方法:根据两个序列分别建树,再判别树是否一样

  2. 不建树的判别方式

    1. image-20220702053319839
    2. 先看根结点也就是第一个数一样吗?再把比根结点小的放左边(顺序不动),比根结点大的放右边(顺序不动),然后再进行对比左右树,上图就是一样的,下图为反面例子
    3. image-20220702053522047
  3. 建一颗树,再判别其他序列是否与该树一致

    1. 求解思路
    2. 搜索树表示
    3. 建搜索树T
    4. 判别一序列是否与搜索树T一致
    5. 搜索树表示
      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;
}

搜索树是否一样的判别

image-20220706083408133

方法:在树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:什么是抽象的链表

链表是一个抽象的数据结构

  1. 有块地方存数据
  2. 有块地方存指针——下一个结点的地址(一个抽象的指针指向的就是地址,任何一种形式去存了下一个结点的位置这东西就叫做指针)
  3. image-20220706093854900
  4. image-20220706094019412

线性结构习题2:链表逆转算法

单链表的逆转

image-20220706094116841

第一个加上头结点虽然会浪费一个空间,但是会使后面的操作变为更加简单

逆序之后如下:

image-20220706094241649

接下来是逆序的过程:

  1. 首先需要对图中定义的词汇进行解释

    1. new:指的是新的已经逆转好的链表,他的头结点的位置
    2. old: 指的是旧的还没有逆转好的老链表,他的头结点位置
  2. 我们首先想要把2跟1进行逆转,但是在逆转之前需要先把3这个位置记住,所以设定一个tmp指针去指向3。否则当2的指针一转向,2后面的链表就丢失了

    1. image-20220706095027756
    2. image-20220706095101249
    3. image-20220706095134083
    4. image-20220706095200258
    5. 以下是伪代码实现
      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:测试数据

  1. 有尾巴不反转
  2. 地址取到上下界
  3. 正好全反转
  4. K = N是全反转
  5. K = 1不用反转
  6. 最大(最后剩K-1不反转)、最小N
  7. 有多余结点

2-6是属于边界测试

  1. 边界测试

    1. 意思就是给你一个数值,你就一定要取到那个范围上界和下界