第九篇博客——数据结构二

二叉树

一般地,二叉树的定义:

struct TreeNode{
    char data;
    TreeNode *lc;
    TreeNode *rc;
    TreeNode(char c):data(c),lc(NULL),rc(NULL){}
};
//构造函数不可以少

深度优先遍历方法有前序、中序和后序之分。其中知道中序遍历次序和前/后序遍历次序是可以还原出二叉树的形状的。一般构建二叉树使用x序和#符号来建立。

例题1:编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

先序序列 ABC##DE#G##F### 对应的二叉树如下:

#include <iostream>
#include<cstdio>

#include<string>
using namespace std;

struct TreeNode{
    char data;
    TreeNode* lc;
    TreeNode* rc;
    TreeNode(char c):data(c),lc(NULL),rc(NULL){}

};
/*
先序建立二叉🌲的思路和先序遍历的差不多,
先new TreeNode,然后build左子树和右子树,
返回的就是当下新建的root节点
*/
TreeNode* Build(int& position,string str){
    char c=str[position++];
    if(c=='#')
        return NULL;
    TreeNode* root=new TreeNode(c);
    root->lc=Build(position, str);
    root->rc=Build(position, str);
    return root;
}

void InOrder(TreeNode* root){
    if(root==NULL)
        return;
    InOrder(root->lc);
    cout<<root->data<<' ';
    InOrder(root->rc);
    return;
}

int main(){
    string str;
    while(cin>>str){
        int position=0;
        TreeNode* root=Build(position,str);
        InOrder(root);
        cout<<endl;

    }
    return 0;
}

例题2:依据前序和中序序列推出后序序列。二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树; 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树; 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。 给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。

输入描述
两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C....最多26个结点。

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;

struct TreeNode{
    char data;
    TreeNode *lc;
    TreeNode *rc;
    TreeNode(char c):data(c),lc(NULL),rc(NULL){}
};
//build函数的第一个参数是前序,第二个参数是中序

TreeNode* Build(string str1,string str2){
  //如果前序序列是0,那么就可以返回null
    if(str1.size()==0)
        return NULL;
  //定义临时参数c=前序序列的第一个字符,也就是当前的root
    char c=str1[0];
    TreeNode *root=new TreeNode(c);
  //寻找中序序列中的根root
    int pos=str2.find(c);
  //左子树的前序序列就是str1的1到pos;中序序列是str2的0到pos-1
  //右子树就是从pos+1到末尾
    root->lc=Build(str1.substr(1,pos), str2.substr(0,pos));
    root->rc=Build(str1.substr(pos+1), str2.substr(pos+1));
  //返回根root
    return root;
}

int PostOrder(TreeNode* root){
    if(root==NULL){
        return 0;
    }
    PostOrder(root->lc);
    PostOrder(root->rc);
    cout<<root->data;
    return 0;
}

int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        TreeNode* root=Build(str1,str2);
        PostOrder(root);
        cout<<endl;
    }
    return 0;
}

二叉排序树

二叉排序树的左子树和右子树都是二叉排序树,左子树一定小于根节点,右子树一定大于根节点。

练习:按顺序插入一个初始为空树的二叉排序树中,每次插入后成功后,求相应的父亲节点的关键字值,如果没有父亲节点,则输出-1。

#include<iostream>
#include<cstdio>

using namespace std;
struct TreeNode{
    int data;
    TreeNode *lc;
    TreeNode* rc;
    TreeNode(int x):data(x),lc(NULL),rc(NULL){}
};

TreeNode *Insert(TreeNode* root,int x,int father){
    if(root==NULL){
        root=new TreeNode(x);
        cout<<father<<endl;
    }
    else if(x<root->data)
        root->lc=Insert(root->lc,  x, root->data);
    else
        root->rc=Insert(root->rc,  x, root->data);
    return root;
}

int main(){
    int n;
    while(cin>>n){
        TreeNode* root=NULL;
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            root=Insert(root, x, -1);
        }
    }
    return 0;
}

练习:输入一系列整数,建立二叉排序树,并进行前序,中序,后序遍历。

#include <iostream>
#include <cstdio>
using namespace std;
struct node
{
    int val;
    node *left, *right;
    node(int v):val(v),left(NULL),right(NULL){}
};

void insert(node *&root, int x)
{
    if (root == NULL)
    {
        root = new node(x);
        return;
    }
    if (root->val == x)
    {
        return;
    }
    if (x < root->val)
    {
        insert(root->left, x);
    }
    else
    {
        insert(root->right, x);
    }
}
void preOrder(node *root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->val);//首先访问当前节点
    preOrder(root->left);//然后访问左右节点
    preOrder(root->right);
}
void inOrder(node *root)
{
    if (root == NULL)
    {
        return;
    }
    inOrder(root->left);
    printf("%d ", root->val);
    inOrder(root->right);
}
void postOrder(node *root)
{
    if (root == NULL)
    {
        return;
    }
    postOrder(root->left);
    postOrder(root->right);
    printf("%d ", root->val);
}
int main()
{
    int n;
    while (cin >> n)
    {
        node *root = NULL;
        while (n--)
        {
            int x;
            cin >> x;
            insert(root, x);
        }
        preOrder(root);
        printf("\n");
        inOrder(root);
        printf("\n");
        postOrder(root);
        printf("\n");
    }
}

练习:二叉搜索树。判断两个序列是否是同一个二叉搜索树序列(浙大上机题)

#include <iostream>
#include <cstdio>
#include <string>

using namespace std;
struct node{
    char val;
    node *lc,*rc;
    node(int c):val(c),lc(NULL),rc(NULL){}
};

bool flag;
node *insert(node *root,int x){
    if(root==NULL){
        root=new node(x);
        return root;
    }
    if(root->val>x)
        root->lc=insert(root->lc,  x);
    else
        root->rc=insert(root->rc,  x);
    return root;
}

void check(node* root,node*judge){
    if(root&&judge){
        if(root->val!=judge->val)
            flag=false;
        check(root->lc, judge->lc);
        check(root->rc, judge->rc);
    }
    else if(root||judge)
        flag=false;
}



int main(){
    int n;
    while(cin>>n){
        node *root=NULL;
        string s;
        cin>>s;
        for(int i=0,len=s.length();i<len;i++)
            root=insert(root, s[i]);
        for(int i=0;i<n;i++){
            flag=true;
            node *judge=NULL;
            cin>>s;
            for(int i=0,len=s.length();i<len;i++)
                judge=insert(judge, s[i]);
            check(root,judge);

            if(flag)
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
    }
}

优先队列

在优先队列中每个元素都被赋予了优先级,每次访问元素的时候只能访问当前队列中优先级最高的元素。

定义一个优先队列的模版

priority_queue<Type, Container, Functional> name;

Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。

当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。

值得注意的地方,无论如何,每次访问元素的时候只能访问当前队列中优先级最高的元素,如果涉及到自定义的大小,比如用时最短的优先级最大,结构体自定义优先级等。这些情况下需要重载运算符。

//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列,大顶堆
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)

优先队列的应用——哈夫曼树

#include<iostream>
#include<cstdio>
#include<queue>

using namespace std;

int main(){
    int n;
    while(cin>>n){
        priority_queue<int,vector<int>,greater<int>> myq;
        while(n--){
            int x;
            cin>>x;
            myq.push(x);
        }

        int ans=0;
        while(1<myq.size()){
            int a=myq.top();
            myq.pop();
            int b=myq.top();
            myq.pop();
            ans+=a+b;
            myq.push(a+b);
        }
        cout<<ans<<endl;
    }
    return 0;
}