中国大学MOOC ZJU-数据结构-树(中)

04-树7 二叉搜索树的操作集 (30分)

本题要求实现给定二叉搜索树的5种常用操作。

函数接口定义:

BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );

其中BinTree结构定义如下:

typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
    ElementType Data;
    BinTree Left;
    BinTree Right;
};

函数Insert将X插入二叉搜索树BST并返回结果树的根结点指针;
函数Delete将X从二叉搜索树BST中删除,并返回结果树的根结点指针;如果X不在树中,则打印一行Not Found并返回原树的根结点指针;
函数Find在二叉搜索树BST中找到X,返回该结点的指针;如果找不到则返回空指针;
函数FindMin返回二叉搜索树BST中最小元结点的指针;
函数FindMax返回二叉搜索树BST中最大元结点的指针。
裁判测试程序样例:

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
    ElementType Data;
    BinTree Left;
    BinTree Right;
};

void PreorderTraversal( BinTree BT ); /* 先序遍历,由裁判实现,细节不表 */
void InorderTraversal( BinTree BT );  /* 中序遍历,由裁判实现,细节不表 */

BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );

int main()
{
    BinTree BST, MinP, MaxP, Tmp;
    ElementType X;
    int N, i;

    BST = NULL;
    scanf("%d", &N);
    for ( i=0; i<N; i++ ) {
        scanf("%d", &X);
        BST = Insert(BST, X);
    }
    printf("Preorder:"); PreorderTraversal(BST); printf("\n");
    MinP = FindMin(BST);
    MaxP = FindMax(BST);
    scanf("%d", &N);
    for( i=0; i<N; i++ ) {
        scanf("%d", &X);
        Tmp = Find(BST, X);
        if (Tmp == NULL) printf("%d is not found\n", X);
        else {
            printf("%d is found\n", Tmp->Data);
            if (Tmp==MinP) printf("%d is the smallest key\n", Tmp->Data);
            if (Tmp==MaxP) printf("%d is the largest key\n", Tmp->Data);
        }
    }
    scanf("%d", &N);
    for( i=0; i<N; i++ ) {
        scanf("%d", &X);
        BST = Delete(BST, X);
    }
    printf("Inorder:"); InorderTraversal(BST); printf("\n");

    return 0;
}
/* 你的代码将被嵌在这里 */

输入样例:
10
5 8 6 2 4 1 0 10 9 7
5
6 3 10 0 5
5
5 7 0 10 3

输出样例:
Preorder: 5 2 1 0 4 8 6 7 10 9
6 is found
3 is not found
10 is found
10 is the largest key
0 is found
0 is the smallest key
5 is found
Not Found
Inorder: 1 2 4 6 8 9

BinTree Insert( BinTree BST, ElementType X )
{
    if(!BST){/*找到地方了,插进去*/
        BinTree node = (BinTree)malloc(sizeof(struct TNode));
        node->Left = node->Right = NULL;
        node->Data = X;
        return node;
    }/*根据BST的性质查找结点*/
    else if(X > BST->Data) 
        BST->Right = Insert(BST->Right,X);
    else if(X < BST->Data)
        BST->Left = Insert(BST->Left,X);

    return BST;
}

BinTree Delete( BinTree BST, ElementType X )
{
    Position temp;
    if(!BST){
        printf("Not Found\n");
        return BST;
    }
    else if(X > BST->Data)
        BST->Right =  Delete(BST->Right,X);
    else if(X < BST->Data)
        BST->Left =  Delete(BST->Left,X);
    else if(X == BST->Data){/*这里其实有两个办法删除,即找该结点右子树的最小值或找该结点左子树的最大值*/
        if(BST->Right && BST->Left){
            temp = FindMin(BST->Right);
            BST->Data = temp->Data;
            BST->Right = Delete(BST->Right, BST->Data);
        }
        else{
            temp = BST;
            if(!BST->Left)
                BST = BST->Right;
            else
                BST = BST->Left;
            
            free(temp);
        }
    }
    return BST;
}


Position Find( BinTree BST, ElementType X )
{
    if(!BST)
        return NULL;
    else if(X > BST->Data)
        return Find(BST->Right,X);
    else if(X < BST->Data)
        return Find(BST->Left,X);
    else
        return BST;
}
//最小值一定最左
Position FindMin( BinTree BST )
{
    if(!BST)
        return NULL;
    while(BST->Left)
        BST = BST->Left;
    return BST;
}

//最大值一定最右
Position FindMax( BinTree BST )
{
    if(!BST)
        return NULL;
    while(BST->Right)
        BST = BST->Right;
    return BST;
}

04-树5 Root of AVL Tree (25分)

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤20) which is the total number of keys to be inserted. Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

Output Specification:
For each test case, print the root of the resulting AVL tree in one line.

Sample Input 1:
5
88 70 61 96 120

Sample Output 1:
70

Sample Input 2:
7
88 70 61 96 120 90 65

Sample Output 2:
88

下面介绍四种旋转
右单旋
RR旋转 右单旋
其实右单旋,左单旋的名字其实是非常有误导性的,至少我第一次记错了。所谓左单旋,并不是树的一部分向左旋转,而是一开始结点插入在某结点(所谓“发现者”)的左子树的左子树上,树是需要向右旋转的。右单旋同理。
左右旋:对于根的左子树先进行右单旋,再返回根的左单旋。
右左旋:先对右子树行左单旋,再返回对根的右单旋。

左单旋
在这里插入图片描述
左右旋
在这里插入图片描述
右左旋
在这里插入图片描述

#include <cstdio>
#include <cstdlib>
#include<algorithm>

using namespace std;
struct node{
    int val;
    node* left;
    node* right;
};
typedef node* AVLTree;
node* NewNode(int val)
{
    node* temp = (node*)malloc(sizeof(node));
    temp->val = val;
    temp->left = temp->right = NULL;
    return temp;
}

node* SingleLeftRotation(node* tree)
{
    node* temp = tree->left;
    tree->left = temp->right;
    temp->right = tree;
    return temp;
}

node* SingleRightRotation(node* tree)
{
    node* temp = tree->right;
    tree->right = temp->left;
    temp->left = tree;
    return temp;
}


node* LeftRightRotation(node* tree)
{
    tree->left = SingleRightRotation(tree->left);
    return SingleLeftRotation(tree);
}

node* RightLeftRotation(node* tree)
{
    tree->right = SingleLeftRotation(tree->right);
    return SingleRightRotation(tree);
}


int GetHeight(node *tree)
{
    if(!tree)
        return 0;
    else
        return max(GetHeight(tree->left),GetHeight(tree->right))+1;
}

node* InsertNode(node* tree,int v)
{
    if(!tree){
        tree = NewNode(v);
    }
    else{
        if(v > tree->val){
            tree->right = InsertNode(tree->right,v);//插入的地方在右边
            if(GetHeight(tree->right) - GetHeight(tree->left) == 2){//失衡了
                if(v > tree->right->val)//插入到右子树的右边,右单旋
                    tree = SingleRightRotation(tree);
                else if(v < tree->right->val)//插入到右子树的左边,右左旋
                    tree = RightLeftRotation(tree);
            }
        }
        else if(v < tree->val){
            tree->left = InsertNode(tree->left,v);//插入到左子树上
            if(GetHeight(tree->left) - GetHeight(tree->right) == 2){//失衡了
           //不建议对子树的高度进行储存,现用现算,否则不易更新
                if(v > tree->left->val)//左子树的右子树上,左右旋
                    tree = LeftRightRotation(tree);
                else if(v < tree->left->val)//左子树的左子树上,左单旋
                    tree = SingleLeftRotation(tree);
            }
        }
    }
    return tree;
}

int main(void)
{
    int n,v;
    node* tree = NULL;
    scanf("%d", &n);
    for(int i = 0;i < n; i++){
        scanf("%d", &v);
        tree = InsertNode(tree,v);
    }
    printf("%d",tree->val);
    return 0;
}

04-树4 是否同一棵二叉搜索树 (25分)

给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。

输入格式:
输入包含若干组测试数据。每组数据的第1行给出两个正整数N (≤10)和L,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出N个以空格分隔的正整数,作为初始插入序列。最后L行,每行给出N个插入的元素,属于L个需要检查的序列。

简单起见,我们保证每个插入序列都是1到N的一个排列。当读到N为0时,标志输入结束,这组数据不要处理。

输出格式:
对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“Yes”,否则输出“No”。

输入样例:
4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0

输出样例:
Yes
No
No

鸣谢青岛大学周强老师补充测试数据!

思路:
先按模板建一棵树,然后再按数据查找,如果:
①查找的路径上的数据标记为false(注意是路径!不是查找的终点),则BST不一样。查找走过的路上的结点之前一定查找过;
②如果查找到叶子结点,查找的值和叶子结点的值不一样,则BST不一样;
③如果查找完了没有返回false,则说明BST一样。

#include<cstdio>
#include<cstdlib>

struct treenode
{
    int val;
    treenode *right,*left;
    bool flag;
};

treenode* newnode(int val)//创建一个新结点
{
    treenode* tree = (treenode*)malloc(sizeof(treenode));
    tree->left = NULL;
    tree->right = NULL;
    tree->flag = 0;
    tree->val = val;
    return tree;
}

treenode* insert(treenode* tree,int v)//插入一个结点
{
    if(tree == NULL)
        tree = newnode(v);
    else{
        if(v > tree->val)
            tree->right = insert(tree->right,v);
        else
            tree->left = insert(tree->left,v);
    }
    return tree;
}

treenode* maketree(int n)//建树
{
    int i,v;
    scanf("%d",&v);
    treenode* tree = newnode(v);
    for(i = 1;i < n; i++){
        scanf("%d",&v);
        tree = insert(tree,v);
    }
    return tree;
}

void reset(treenode* tree)//将BSTflag归零,防止上次测试干扰下次
{
    if(tree->left)
        reset(tree->left);
    if(tree->right)
        reset(tree->right);
    
    tree->flag = false;
}

bool check(treenode* tree,int v)
{
    if(tree->flag){
        if(v > tree->val)
            return check(tree->right,v);
        else if(v < tree->val)
            return check(tree->left,v);
        else//之前查找过了又查找过一次
            return false;
    }
    else{//这个结点没查找过,如果相等,则找到了,不相等,则查找的路径上右未被查找的元素,返回false
        if(v != tree->val)
            return false;
        else{
            tree->flag = true;
            return true;
        }
    }
}

bool judge(treenode* tree,int n)
{
    int v;
    bool flag = false;//标记,如果不相同则为true,相同则为false
    scanf("%d",&v);
    if(tree->val != v)//检查根结点
        flag = true;//不匹配直接不相同
    else
        tree->flag = true;//继续检查子树的结点
    
    for(int i = 1;i < n; i++){
        scanf("%d",&v);//即使因为根判断不相同以后也要把这一行的数据全部读完
        if(!flag && !check(tree,v))//flag为true时直接跳过check
            flag = true;
    }
    if(flag)
        return false;
    else
        return true;
}

void freetree(treenode*tree)//防止内存泄漏,否则容易段错误
{
    if(tree->left)
        freetree(tree->left);
    if(tree->right)
        freetree(tree->right);
    free(tree);
}

int main(void)
{
    int n,l;
    scanf("%d", &n);
    treenode* tree;
    while(n){
        scanf("%d", &l);
        tree = maketree(n);
        for(int i = 0; i < l; i++){
            if(judge(tree,n))
                printf("Yes\n");
            else
                printf("No\n");
            reset(tree);
        }
        freetree(tree);
        scanf("%d",&n);
    }
    return 0;
}

04-树6 Complete Binary Search Tree (30分)

A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than or equal to the node’s key.
Both the left and right subtrees must also be binary search trees.
A Complete Binary Tree (CBT) is a tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right.

Now given a sequence of distinct non-negative integer keys, a unique BST can be constructed if it is required that the tree must also be a CBT. You are supposed to output the level order traversal sequence of this BST.

Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then N distinct non-negative integer keys are given in the next line. All the numbers in a line are separated by a space and are no greater than 2000.

Output Specification:
For each test case, print in one line the level order traversal sequence of the corresponding complete binary search tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.

Sample Input:
10
1 2 3 4 5 6 7 8 9 0

Sample Output:
6 3 8 1 5 7 9 0 2 4

思路:
给出的数据从小到大排序后其实是为CBT的后序遍历。
从小到大排序后,树根结点的位置 = 左子树的结点数 + 1;
由此递归解决问题。

#include<cstdio>
#include<cmath>
#include<algorithm>
int level_order[1010] = {0};//记录层序遍历的顺序
bool cmp(int a,int b)//从小到大排序
{
    return a < b;
}

int getleftlen(int n)//给出树的结点数求出左子树的结点数
{
    int left_height = log2(n);
    int left_len = pow(2,left_height-1)-1;
    int others = n - 2*left_len-1;
    if(others <= pow(2,left_height-1))
        left_len += others;
    else
        left_len += pow(2,left_height-1);
    return left_len;
}

void levelorder(int left,int right,int in[],int pos)
{
    if(left > right)
        return;
    int n = right - left + 1;
    int left_len = getleftlen(n);
    level_order[pos] = in[left+left_len];
    levelorder(left+left_len+1,right,in,2*pos+2);
    levelorder(left,left+left_len-1,in,2*pos+1);
}

int main(void)
{
    int n;
    scanf("%d",&n);
    int in_order[1010] = {0};
    for(int i = 0; i < n; i++)
        scanf("%d",&in_order[i]);
    std::sort(in_order,in_order+n,cmp);
    levelorder(0,n-1,in_order,0);
    for(int i = 0; i < n-1; i++)
        printf("%d ",level_order[i]);
    printf("%d",level_order[n-1]);

    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值