《数据结构》003 树——00A

静态查找

1、顺序查找 

/* 哨兵技巧:哨兵放在Element[0], 其他元素从1开始放置 */
int SequentialSearch (List Tbl, ElementType K)
{   /* 在Element[1] ~ Element[n] 中查找关键字为K的数据元素 */
    int i;
    Tbl->Element[0] = K;         // 建立哨兵
    for(i = Tbl->Length; Tbl->Element[i] != K; i--);
    return i;                    // 超找成功后返回所在单元下标,不成功后返回0
}

typedef struct LNode *List;

struct LNode
{
    ElementType Element[MAXSIZE];
    int Length;
};

二分查找    前提:1、n个元素必须有序查找(比如从小到大等) 2、必须是连续存放的数组

例1: 假设有13个数据元素,按关键字由小到大顺序存放,二分查找关键字为444的数据元素

例2:查找关键字为43 的元素

        解: 1. letft=1, right=13, mid=(1+13)/2=7; 100>43, 要查找的元素在右半段,左移right指针

                 2.left=1, right=mid-1=6, mid=(1+6)/2=3; 39<43, 要查的元素在本次分格的右半段,left 指针右移至mid+1位置

                 3.left=4,right=6, mid=(4+6)/2=5; 51>43, 要查的元素在该段左半段,right指针左移至mid-1

                 4.left=4,  right=4,    mid=4; 45>43,要查的元素在该断的左半段,right指针丛至mind-1

                 5.left=4, right=3, left>right---> 错误!退出,未找到43 

例3:(判断)在二分查找的程序实现中,如果left和right的更新不是取mid+1和mid-1而是都取mid,程序也是正确的。             (X)

例4: 11个元素的二分查找判定树 (注意:每个节点代表mid值)

         1.每个节点需要查找的次数恰好为其层数    2. 查找成功时,查找次数不会超过树的深度  3. n个结点的判定树的的深度

为 [log n]+1

           4.ASL平均成功查找次数=(查找W次*共有几个数+查找W-1次*共几个数+查找W-2次*共几个数+.....+查找1次*共几个数)/总数

 树

一、定义

1、字数不相交

2、除根结点外,每个结点仅有一个父结点

3、一棵N个结点的树有N-1条边

例1:有一棵m棵树的集合(也叫森林)公有k条边,问这m棵树公有多少个结点? 

        (k+m)

4、结点的度:结点子树个数;   树的度:最大的结点的度

5、叶结点:度=0

6、父结点、子结点、兄弟结点

7、路径:从哪到哪的方法    路径长度:路径所包含边的个数

8、祖先结点:某结点--->根节点经过的所有的结点是他的;      子孙结点:某结点以后的所有可以经过的结点是他的

9、结点的层次:规定根结点在1层

10、树的深度:所有结点中的最大层次

树的表示

例2:在“儿子-兄弟”法的树中,如果从根节点开始访问其次子的次子,怎样全都访问长子,才能使他们所经过的结点数相等?

        答:长子-长子-长子-长子

例3: (1)若每个结点有3个指针域,有n个结点-------> 一共就有3n个指针域,但是整个树仅有n-1条边,意味着有2n+1个指针域被浪费

        (2)当用“儿子-兄弟”表示法表示二叉树,n个结点-------> 一共有2n个指针域, 有n-1个指针域被利用,有n+1个指针域被浪费,比第一种利用率高

二叉树:

1、分类:斜二叉树、完美(满)二叉树、完全二叉树(允许缺掉后面的结点,其他与满二叉树的编号相同)

2、性质: 1)第i层最大节点数为 2^{i -1}   2)若有k层,总结点数为 2^{k}-1   3)结点有三类:叶结点n0(无儿子)、有1个儿子n1 、有2个儿子n2   存在公式:  总结点数=n0 + n1 + n2        有边数公式   总结点数-1 =   \sum_{i=0}^{n} ( 结点有i个儿子 * i )    得出: n0 = n2 + 1

二叉树存储结构:

1、顺序存储

2、链表存储

二叉树的遍历    重点看printf() 在什么位置

1、先序遍历

void PreOrderTraversal( BinTree BT )
{
    if( BT )
    {   printf("%d", BT->Data;
        PreOrderTraversal( BT->Left );
        PreOrderTraversal( BT->Right );
    }
}

2、中序遍历

void InOrderTraversal( BinTree BT )
{
    if( BT )
    {   InOrderTraversal( BT->Left );
        printf("%d", BT->Data);
        InOrderTraversal( BT->Right );
    }
}

3、后序遍历

void PostOrderTraversal( BinTree BT )
{
    if( BT )
    {   PostOrderTraversal( BT->Left );
        PostOrderTraversal( BT->Right );
        printf("%d", BT->Data);
    }
}

二叉树的非递归算法实现: 使用堆栈

中序遍历非递归算法

void InOrderTraversal( BinTree BT )
{
    BinTree T=BT;
    Stack S = CreateStack( MaxSize );   // 创建并初始化堆栈S
    while( T || !IsEmpty(S) ){          //外层循环:判断T->Right是否为空,若不空,从第二次循环开始就开始遍历左子树的右子树(然后再遍历右子树的左子树)
        while(T){                       //内循环:当T不为空,将T压栈,并遍历T的左子树
            Push(S, T);
            T = T->Left;
        }
        if( !Empty(S) ){                //内循环结束后,即T->Left==NULL,若S不为空,首先弹T出栈,T为出栈元素;再打印T的值;并指向T的右子树
            T = Pop(S);                 
            printf("%5d", T->Data);
            T = T->Right;
        }
    }
}

先序遍历非递归算法

void InOrderTraversal( BinTree BT )
{
    BinTree T BT;
    Stack S = CreateStack( MaxSize );
    while( T || !IsEmpty(S) ){       // 外循环:
        while(T){                    // 内循环:先打印根节点,在往左下遍历 
            printf("%5d", T->Data);
            Push(S, T);              // 将此根节点压入栈中,再向左下遍历
            T = T->Left;
        }
        if( !IsEmpty(S) ){           // 判断栈是否为空,若不是,则弹出T——栈顶元素,并遍历该T的右子树
// 疑问:假如右子树也为空,怎样回到根节点?
            T = Pop(S);
            T = T->Right;
        }
    }
}

层序遍历

要点:1.一层一层看  2. 出那个,放哪个的左右儿子

void LevelOrderTraversal( BinTree BT )
{
    Queue Q;
    BinTree T;
    if ( !BT ) return;
    Q = CreateQueue( MaxSize );
    AddQ( Q, BT );
    while ( !IsEmpty(Q) ){
        T = Delete( Q );
        printf("%d\n", T->Data);
        if( T-Left )   AddQ( Q, T->Left );
        if( T->Right ) AddQ( Q, T->Right );
    }
}

例1: 求二叉树的叶子结点

(方法:二叉树的遍历算法中增加检测结点的“左右子树是否都为空”)

void PreOrderPrintLeaves( BinTree BT )
{
    if( BT ){
        if ( !BT->Left && !BT->Right )
            printf("%d", BT->Data);
        PreOrderPrintLeaves( BT->Left );
        PreOrderPrintLeaves( BT->Right );
    }
}

例2:求二叉树的高度

(方法:分段,分别求左右子树的高度--》比较--》 +1)

void PostOrderGetHeight( BinTree BT )
{   int HL, HR, MaxH;
    if( BT ){
        HL = PostOrderGetHeight( BT->left );  // 先遍历左边子树的深度
        HR = PostOrderGetHeight( BT->Right);
        MaxH = ( HL > HR ) ? HL : HR ;
        return (MaxH + 1);
    }
}
else return 0;

例3: 二元运算表达式树及其遍历

例4: 两种遍历序列确定二叉树:    中序 + 前/后

前序: 根 左 右   中序: 左 根 右      按照这个规律,可以依次确定每一个小左子树的跟 的左子树 的右子树 嘻嘻嘻...

已知: 前序序列为  a b c d e f g h i j     中序序列为: c b e d a h g i j f

 

二叉树表示 (用结构数组)

#define MaxTree 10
#define ElementType char
#define Tree int
#define Null -1

struct TreeNode
{
    ElementType Element;
    Tree Left;
    Tree Right;
} T1[MaxTree], T2[MaxTree];  

   注意:可以通过观察哪个数组没有被任何指针指向,判断根节点

 

  • 程序——判断两棵树是否同构

1. main主函数

int main()
{
    Tree R1, R2;
    
    R1 = BuiltTree(T1);
    R2 = BuiltTree(T2);
    if(Isomorphic(R1, R2))    printf("Yes\n");
    else printf("No\n");

    return 0;
}

2. 创建BuildTree

// 将T[i]数组遍历一遍,看有没有数组指向它
Tree BuildTree(struct TreeNode T[])
{
    scanf("%d \n", &N);                   // 输入有几个结点
    if(N){                                // 若输入的结点不为0
        for(i=0;i<N;i++) check[i]=0;      // 将每一个结点的check值设为0
        for(i=0;i<N;i++){                 // 依次输入每个节点的基本信息
            scanf("%c%c%c\n", &T[i].Element, &cl, &cr); //cl、cr分贝表示左、右结点的值
你的出路在哪   
            if(cl != '-'){
                    T[i].Left = cl-'0';   // 原来输入的是字符型的,减去字符0,得到数值0
                    check[T[i].Left] = 1; 
            //若一个节点指向了某一个位置,则把那个位置结点check设为1
            }
            else T[i].Left = Null;
            if( 
        }
        for(i=0; i<N; i++)
            if( !check(T[i]) )   break;   // 依次判断根节点是否为0
        Root = i;
    }
    return Root;            // 返回根节点
}

 3. 判断同构

int Isomorphic( Tree R1, Tree R2)
{
    if( (R1==Null) && (R2==Null))    return 1;    // 两棵树的根节点都为空
    if( ((R1==Null)&&(R2!=Null)) || ((R1!=Null)&&(R2==Null)) )    return 0;    //一棵空一颗不空的情况,非同构
    if( T1[R1].Element != T2[R2].Element )    return 0;    // 根节点的数值不等,直接舍弃
    if( (T1[R1].Left == Null) && (T2[R2].Left == Null) )    return Isomorphic(T1[R1].Right, T2[R2].Right);   // 两棵树的左子树为空,沿右子树向下迭代
    if( (T1[R1].Left != Null)&&(T2[R2].Left != Null)&&((T1[T1[R1].Left].Element)==(T2[T2[R2].Left].Element)) )     // 两棵树左子树都不全为空,且根节点的值相等,值得迭代,先迭代树1树2的左子树,再迭代树1树2的右子树
       return( (Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right) );
    else                   // 若某一情况不符合上面的假设,分别交叉迭代树1左子树和树2右子树,以及树1右子树和树2左子树,看是否交叉相等
        return( Isomorphic( T1[R1].Left, T2[R2].Right) && Isomorphic( T1[R1].Right, T2[R2].Left));
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值