欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-21 16:41:08
...

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.
中国大学MOOC ZJU-数据结构-树(中)
中国大学MOOC ZJU-数据结构-树(中)
中国大学MOOC ZJU-数据结构-树(中)
中国大学MOOC ZJU-数据结构-树(中)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

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

左单旋
中国大学MOOC ZJU-数据结构-树(中)
左右旋
中国大学MOOC ZJU-数据结构-树(中)
右左旋
中国大学MOOC ZJU-数据结构-树(中)

#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;
}