数据结构笔记6: 二叉树和其他树

树的一般定义

  • 线性表、表:不适合描述层次结构数据
  • 树(tree):是一个非空的有限元素的集合,其中有一个特殊的元素称为,余下的元素组成树的若干子树
  • 递归的思想:每棵树=根+若干棵子树,每棵子树=子树的根+若干棵它的子树

相关术语

  • 元素:节点
  • 根节点与子树的根节点的关系:边
  • 边的两端:父母(parent)&孩子(children)
  • 相同父母的节点:兄弟(sibling)
  • 叶节点(终端节点):没有孩子的节点
  • 根节点(root):没有父亲的节点
  • 层(level):根为1,根的孩子为2,……
  • 节点的度(degree):孩子的数目

树的种类

  • 自由树:根不确定,但可以设定一个根,能够确定其层级结构
  • 有根树:节点无顺序
  • 有序树:节点是有顺序的,在左边还是右边不一样

森林和有序森林

  • 森林(forest):树的集合,通常认为是有根树的集合
  • 有序森林(ordered forest):有序树的有序集合
  • 有根(有序)树去掉根节点➡️(有序)森林
    (有序)森林添加辅节点➡️有根(有序)树 

二叉树的定义

二叉树的定义

  • 二叉树(binary tree):有限元素集合,或者为空,或者有一个特殊元素根,余下的元素构成2个二叉树(可以为空),即左子树右子树

二叉树的种类

  • 满二叉树:高度为h,节点数为2^h-1
  • 完全二叉树:高度为h的满二叉树中节点按从上到下,从左到右的顺序从1到2^h-i进行编号,从中删除编号最后的k个节点,编号为2^h-i, 1\leq i\leq k,深度为[{log_{2}}^{n+1}]

二叉树的特性

  • 二叉树的高度为h,h\geq 0,则它至少有h个节点,最多有2^h-1个节点
  • 包含n个节点的二叉树的高度,最大为n,最小为[{log_{2}}^{n+1}]
  • 设完全二叉树中一节点的序号为i, 1\leq i\leq n,则
    (1)i=1时,该元素为二叉树的根;若i> 1,则该元素父节点的编号为[\frac{i}{2}]
    (2)2i> n时,该元素无左孩子,否则,其左孩子的编号为2i
    (3)2i+1>n时,该元素无右孩子,否则,其右孩子编号为2i+1
  • 设二叉树中度为2的节点有n_{2}个,度为1的节点有n_{1}个,度为0的节点有n_{0}个,则n_{0}=n_{2}+1

    n_{0}=(\sum_{i=1}^{n}(i-1)n_{i})+1

二叉树的描述

公式化描述

  • 用一个一维数组当作一个完全二叉树,缺少节点的位置空出来 ➡️浪费空间,且节点的位置之间没有内在约束关系

链表描述

  • 树节点:节点类对象,包含数据域、left、right
  • n-1条边 ➡️ 2n-(n-1)=n+1个空指针
template<class T>
class BinaryTreeNode {
public:
    BinaryTreeNode() {
        LeftChild = RightChild = 0;
    }
    BinaryTreeNode(const T& e) {
        data = e; 
        LeftChild = RightChild = 0;
    }
    BinaryTreeNode(const T& e, BinaryTreeNode *l, BinaryTreeNode *r){
        data = e; 
        LeftChild = l; 
        RightChild = r;
    }
private:
    T data;
    //BinaryTreeNode *parent; 方便寻找父节点
    BinaryTreeNode *leftchild;
    BinaryTreeNode *rightchild;
}

template<class T> 
class BinaryTree { 
public:
    BinaryTree() {
        root = 0;}
    ; 
    ~BinaryTree(){};
    bool IsEmpty() const{ //根据是否有根来判断二叉树是否为空
        return ((root) ? false : true);
    } 
    bool Root(T& x) const; //返回二叉树的根
    void MakeTree(const T& element,
    BinaryTree<T>& left, BinaryTree<T>& right); 
    void BreakTree(T& element, BinaryTree<T>& left,BinaryTree<T>& right);
    void PreOrder(void(*Visit)(BinaryTreeNode<T> *u)){ //先序遍历
        PreOrder(Visit, root);
    }
    void InOrder(void(*Visit)(BinaryTreeNode<T> *u)) { //中序遍历
        InOrder(Visit, root);
    }
    void PostOrder(void(*Visit)(BinaryTreeNode<T> *u)) { //后序遍历
        PostOrder(Visit, root);
    }
    void LevelOrder(void(*Visit)(BinaryTreeNode<T> *u)); //层级遍历
private:
    BinaryTreeNode<T> *root; //根指针
    void PreOrder(void(*Visit)(BinaryTreeNode<T> *u), BinaryTreeNode<T> *t); 
    void InOrder(void(*Visit)(BinaryTreeNode<T> *u), BinaryTreeNode<T> *t); 
    void PostOrder(void(*Visit)(BinaryTreeNode<T> *u), BinaryTreeNode<T> *t); 
};

template<class T>
bool BinaryTree<T>::Root(T& x) const { //把根节点数据存在x里,如果不存在根节点,返回false
    if (root) {
        x = root->data; 
        return true;
    }
    else 
        return false; 
}

template<class T>
void BinaryTree<T>::MakeTree(const T& element,BinaryTree<T>& left, BinaryTree<T>& right){ //合成二叉树
    root = new BinaryTreeNode<T> (element, left.root, right.root);
    left.root = right.root = 0; 
}

template<class T>
void BinaryTree<T>::BreakTree(T& element,BinaryTree<T>& left, BinaryTree<T>& right){ //分裂二叉树
    if (!root) //检查是否为空
        throw BadInput(); 
    element = root->data; 
    left.root = root->LeftChild; 
    right.root = root->RightChild;
    delete root; 
    root = 0;
}

template <class T>
int BinaryTree<T>::Height(BinaryTreeNode<T> *t) const{ //计算高度的递归函数
    if (!t) return 0; 
    int hl = Height(t->LeftChild); 
    int hr = Height(t->RightChild); 
    if (hl > hr) 
        return ++hl;
    else 
        return ++hr;
}

template <class T>
int BinaryTree<T>::Size(BinaryTreeNode<T> *t) const{ //统计节点数目
    if (!t) 
        return 0;
    else 
        return Size(t->LeftChild)+Size(t- >RightChild)+1;
}

二叉树的遍历

先序、中序、后序遍历(深度优先)

  • 按照某种顺序访问树中的每个节点,要求每个节点被访问一次且仅被访问一次
  • 遍历顺序(递归)
    (1)先序遍历:根节点➡️左子树➡️右子树
    (2)中序遍历:左子树➡️根节点➡️右子树   
    (3)后序遍历:左子树➡️右子树➡️根节点
//先序遍历
template<class T>
void PreOrder(BinaryTreeNode<T> *t){ //二叉树的根节点
    if (t) {
        Visit(t);
        PreOrder(t->LeftChild); 
        PreOrder(t->RightChild); 
    }
}

//中序遍历
template <class T>
void InOrder(BinaryTreeNode<T> *t) {
    if (t) {
        InOrder(t->LeftChild);
        Visit(t);
        InOrder(t->RightChild);
    }
}

//后序遍历
template <class T>
void PostOrder(BinaryTreeNode<T> *t) {
    if (t) {
        PostOrder(t->LeftChild); 
        PostOrder(t->RightChild); 
        Visit(t); 
    } 
}
  • 输出完全括号化的中缀表达式
template <class T>
void Infix(BinaryTreeNode<T> *t) {
    if (t) {
        cout << '(';
        Infix(t->LeftChild); 
        cout << t->data; 
        Infix(t->RightChild); 
        cout << ')';
    }
}

逐层遍历(宽度优先)

  • 根➡️叶逐层遍历,同层从左到右
template<class T>
void LevelOrder(BinaryTreeNode<T> *t){ //二叉树的根节点
    LinkedQueue<BinaryTreeNode<T>*> Q; //记录同一层的节点
    while(t){
        Visit(t);
        if(t->LeftChild)
            Q.Add(t->LeftChild);
        if(t->RightChild)
            Q.Add(t->RightChild);
        try{
            Q.Delete(t); //把队首元素取出来,放到t里面
        } catch(OutOfBounds){
            return;
        }
    }
}

由遍历顺序推导二叉树结构

二叉树和森林的互转

  • 一般树
    (1)父指针表示法:无法寻找
    (2)左孩子右兄弟指针表示法 ➡️ 二叉树(一定没有右子树)
  • 森林转换为二叉树
    (1)每棵树单独转换成一棵二叉树
    (2)把每棵树接在第一棵树的右子树上

作业6

用循环数组实现双端队列

template<typename T>
class doubleQueue{
public:
    doubleQueue<T>& create(int m);
    bool isEmpty();
    bool isFull();
    doubleQueue<T>& addLeft(T t);
    doubleQueue<T>& addRight(T t);
    T getLeft();
    T getRight();
    doubleQueue<T>& deleteLeft();
    doubleQueue<T>& deleteRight();
    void print();
private:
    T *dq;
    int maxSize;
    int capacity;
    int left; //最左端元素所在的位置的前一个
    int right; //最右端元素所在的位置的后一个
};

template<typename T>
doubleQueue<T>& doubleQueue<T>::create(int m){
    maxSize=m;
    dq=new T[maxSize];
    left=0;
    right=0;
    capacity=0;
    return *this;
}

template<typename T>
bool doubleQueue<T>::isEmpty(){
    if(capacity==0)
        return true;
    else
        return false;
}

template<typename T>
bool doubleQueue<T>::isFull(){
    if(capacity==maxSize)
        return true;
    else
        return false;
}

template<typename T>
doubleQueue<T>& doubleQueue<T>::addLeft(T t){
    if(isFull()){
        cout<<"FULL!"<<endl;
        return *this;
    }
    if(left==right)
        right=(right+1)%maxSize;
    dq[left]=t;
    capacity++;
    left--;
    if(left<0)
        left=maxSize-1;
    return *this;
}

template<typename T>
doubleQueue<T>& doubleQueue<T>::addRight(T t){
    if(isFull()){
        cout<<"FULL!"<<endl;
        return *this;
    }
    if(left==right){
        left--;
        if(left<0)
            left=maxSize-1;
    }
    dq[right]=t;
    capacity++;
    right=(right+1)%maxSize;
    return *this;
}

template<typename T>
T doubleQueue<T>::getLeft(){
    return dq[(left+1)%maxSize];
}

template<typename T>
T doubleQueue<T>::getRight(){
    int index=right-1;
    if(index<0)
        index=maxSize-1;
    return dq[index];
}

template<typename T>
doubleQueue<T>& doubleQueue<T>::deleteLeft(){
    if(isEmpty())
        cout<<"WRONG!"<<endl;
    left=(left+1)%maxSize;
    dq[left]=-1;
    capacity--;
    return *this;
}

template<typename T>
doubleQueue<T>& doubleQueue<T>::deleteRight(){
    if(isEmpty())
        cout<<"WRONG!"<<endl;
    right--;
    if(right<0)
        right=maxSize-1;
    dq[right]=-1;
    capacity--;
    return *this;
}

template<typename T>
void doubleQueue<T>::print(){
    if(isEmpty()){
        cout<<"EMPTY!"<<endl;
        return;
    }
    for(int i=(left+1)%maxSize;i!=right;i=(i+1)%maxSize)
        cout<<dq[i]<<" ";
    cout<<endl;
}

int main(){
    int t;
    cout<<"Please input double-queue's max size!"<<endl;
    cin>>t;
    doubleQueue<int> DQ;
    DQ.create(t);
    string command;
    cout<<"Please input command!"<<endl;
    cin>>command;
    while(command!="End"){
        if(command=="AddLeft"){
            cin>>t;
            DQ.addLeft(t);
            DQ.print();
        }
        else if(command=="AddRight"){
            cin>>t;
            DQ.addRight(t);
            DQ.print();
        }
        else if(command=="GetLeft")
            DQ.getLeft();
        else if(command=="GetRight")
            DQ.getRight();
        else if(command=="DeleteLeft"){
            DQ.deleteLeft();
            DQ.print();
        }
        else if(command=="DeleteRight"){
            DQ.deleteRight();
            DQ.print();
        }
        else if(command=="IsEmpty"){
            if(DQ.isEmpty())
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
        else if(command=="IsFull"){
            if(DQ.isFull())
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
        cout<<"Please input command!"<<endl;
        cin>>command;
    }
    return 0;
}

构造并输出中缀表达式树

//a+b+c*(d+e)
//缺陷:存储方式不是以完全二叉树形式存储,时间性能较差

void findDelete(string& str){ //删除表达式中的括号
    string temp;
    for(int i=0;i<str.length();i++){
        if(str[i]=='('||str[i]==')'){
            temp=str.substr(0,i)+str.substr(i+1,str.length()-i-1);
            str=temp;
        }
    }
}

template<typename T>
class BinaryTree;

template<typename T>
class node{
    friend BinaryTree<T>; //声明友元
public:
    node(){
        parent=leftchild=rightchild=NULL;
    }
    node(const T& t){
        data=t;
        parent=leftchild=rightchild=NULL;
    }
    node(const T& t,node *l,node*r){
        data=t;
        leftchild=l;
        rightchild=r;
        leftchild->parent=this;
        rightchild->parent=this;
    }
    T data;
    node<T> *parent; //父节点
    node<T> *leftchild;
    node<T> *rightchild;
};

static int indent; //控制缩进的静态量

template<typename T>
class BinaryTree{
public:
    BinaryTree(){
        root=NULL;
    }
    bool isEmpty(){
        return ((root==NULL)? true:false);
    }
    node<T>* getRoot(){ //返回根节点
        return root;
    }
    node<T>* findLast(){ //找到最后一个叶节点并输出
        node<T>* t=root;
        while(t->rightchild){
            t=t->rightchild;
            indent++;
        }
        for(int i=0;i<2*indent;i++)
            cout<<" ";
        cout<<t->data<<endl;
        return t;
    }
    void combine(const T& t,BinaryTree<T>& left,BinaryTree<T>& right){ //合并树
        root=new node<T>(t,left.root,right.root);
        left.root=right.root=NULL;
    }
    void inorder(node<T> *t); //中序遍历输出
    BinaryTree<T>& store(string exp); //存储中缀表达式
private:
    node<T> *root; //根节点
};

template<typename T>
BinaryTree<T>& BinaryTree<T>::store(string exp){
    int i=0;
    for(;i<exp.length();i++){ //找到运算符
        if(exp[i]=='+'||exp[i]=='-'||exp[i]=='*'||exp[i]=='/')
            break;
    }
    if(i==exp.length()){ //如果运算符两边的表达式只有一个字母
        root=new node<T>(exp[0]);
        return *this;
    }
    string tempLeft,tempRight; //把运算符两边分开成两个表达式
    tempLeft=exp.substr(0,i);
    tempRight=exp.substr(i+1,exp.length()-i-1);
    BinaryTree<T> leftTree,rightTree;
    combine(exp[i], leftTree.store(tempLeft), rightTree.store(tempRight)); 
    //递归调用store
    return *this;
}

template <typename T>
void BinaryTree<T>::inorder(node<T> *t) {
    t=t->parent;
    for(int i=0;i<2*(indent-1);i++)
        cout<<" ";
    cout<<t->data<<endl;
    for(int i=0;i<2*indent;i++)
        cout<<" ";
    cout<<t->leftchild->data<<endl;
    indent--;
    if(t->parent){
        inorder(t);
    }
}

int main(){
    string exp;
    cin>>exp;
    BinaryTree<char> expression;
    findDelete(exp);
    expression.store(exp);
    expression.inorder(expression.findLast());
    return 0;
}
//a+b+c*(d+e)
 
template<typename T>
class BinaryTree;
 
template<typename T>
class node{
    friend BinaryTree<T>; //声明友元
public:
    node(){
        leftchild=rightchild=NULL;
    }
    node(const T& t){
        data=t;
        leftchild=rightchild=NULL;
    }
    node(const T& t,node *l,node*r){
        data=t;
        leftchild=l;
        rightchild=r;
    }
    T data;
    node<T> *leftchild;
    node<T> *rightchild;
};
 
template<typename T>
class BinaryTree{
public:
    BinaryTree(){
        root=NULL;
    }
    bool isEmpty(){
        return ((root==NULL)? true:false);
    }
    node<T>* getRoot(){ //返回根节点
        return root;
    }
    void inorder(node<T> *t,int begin); //中序遍历输出
    void makeTree(stack<node<T>*> &s){ //存入二叉树
        node<char> *p=new node<char>(expre.top());
        expre.pop();
        p->rightchild=s.top();
        s.pop();
        p->leftchild=s.top();
        s.pop();
        s.push(p);
        root=p;
    }
    BinaryTree<T>& store(string exp); //存储中缀表达式
private:
    stack<char> expre; //储存表达式字符的栈
    stack<node<T>*> binaryTreeNode; //储存子树的根节点
    node<T> *root; //根节点
};
 
template<typename T>
BinaryTree<T>& BinaryTree<T>::store(string exp){
    for(int i=0;i<exp.length();i++){ //遍历表达式
        if(exp[i]=='(') //如果遇到左括号
            expre.push(exp[i]); //压栈
        else if(exp[i]==')'){ //如果遇到右括号
            while(expre.top()!='(') //把两个括号之间的表达式存入二叉树
                makeTree(binaryTreeNode);
            expre.pop(); //把左括号弹出
        }
        else if(exp[i]=='+'||exp[i]=='-'){ //如果遇到+ -
            if(!expre.empty()){ //当表达式栈不为空
                while(expre.top()=='*'||expre.top()=='/') //如果栈顶是* /则先算
                    makeTree(binaryTreeNode);
            }
            expre.push(exp[i]); //记得把+ -压栈
        }
        else if(exp[i]=='*'||exp[i]=='/') //如果遇到* / 优先级最高,直接压栈
            expre.push(exp[i]);
        else{ //默认除了上述操作符以外就是操作数
            node<char> *p=new node<char>(exp[i]);
            binaryTreeNode.push(p); //放入子树的根节点栈
        }
    } //遍历结束
    while(expre.top()=='*'||expre.top()=='/') //计算* /
        makeTree(binaryTreeNode);
    stack<node<T>*> temp;
    while(!binaryTreeNode.empty()){ //子树的根节点栈不为空时
        temp.push(binaryTreeNode.top()); //入栈
        binaryTreeNode.pop();
    }
    while(expre.size()!=0) //把剩下的表达式字符存入树中
        makeTree(temp);
    return *this;
}
 
template <typename T>
void BinaryTree<T>::inorder(node<T> *t,int indent) { //indent用来记录缩进
    node<char> *p=t;
    if(p){
        inorder(p->leftchild,indent+1); //递归输出左子树
        cout<<setw(3*indent)<<" "<<p->data<<endl; //输出根
        inorder(p->rightchild,indent+1); //递归输出右子树
    }
}
 
int main(){
    string exp;
    cin>>exp;
    BinaryTree<char> expression;
    expression.store(exp);
    expression.inorder(expression.getRoot(),0);
    return 0;
}

二叉树 

// 1 2 3 4 5 6 7 8 9 10
// 1 3 2 7 6 5 4 10 9 8

template<typename T>
class BinaryTree;
 
template<typename T>
class node{
    friend BinaryTree<T>; //声明友元
public:
    node(){
        leftchild=rightchild=NULL;
    }
    node(const T& t){
        data=t;
        leftchild=rightchild=NULL;
    }
    node(const T& t,node *l,node*r){
        data=t;
        leftchild=l;
        rightchild=r;
    }
    T data;
    node<T> *leftchild;
    node<T> *rightchild;
};

template<typename T>
class BinaryTree{
public:
    BinaryTree(){
        root=NULL;
    }
    bool isEmpty(){
        return ((root==NULL)? true:false);
    }
    node<T>* getRoot(){ //返回根节点
        return root;
    }
    int size(node<T> *t){ //返回节点的数目
        if(!t)
            return 0;
        else
            return size(t->leftchild)+size(t->rightchild)+1;
    }
    void combine(const T& t,BinaryTree<T>& left,BinaryTree<T>& right){ //合并树
        root=new node<T>(t,left.root,right.root);
        left.root=right.root=NULL;
    }
    node<T>* store(T *t,int len,int index){ //以完全二叉树的形式存储数据
        if(index>=len)
            return NULL;
        node<T> *temp=new node<T>;
        temp->data=t[index];
        temp->leftchild=store(t,len,index*2+1); //映射关系
        temp->rightchild=store(t,len,index*2+2);
        root=temp;
        return root;
    }
    void swap(node<T> *t){ //交换二叉树里所有的左右子树
        if(t->leftchild||t->rightchild){ //只要左子树或右子树存在
            node<T> *temp=t->leftchild;
            t->leftchild=t->rightchild;
            t->rightchild=temp;
        }
        if(t->leftchild)
            swap(t->leftchild); //在左子树内部实行交换
        if(t->rightchild)
            swap(t->rightchild);
    }
    void levelOrder(node<T> *t){ //以层级遍历输出数据
        queue<node<T>*> q; //存储有左右子树的节点
        while(t){
            cout<<t->data<<" ";
            if(t->leftchild)
                q.push(t->leftchild); //入队
            if(t->rightchild)
                q.push(t->rightchild);
            if(!q.empty()){
                t=q.front();
                q.pop(); //出队
            }
            else //队列为空时退出(仅限于完全二叉树)
                break;
        }
        cout<<endl;
    }
    int width(node<T> *t){ //返回二叉树的宽度,即同一层最多的叶节点数
        queue<node<T>*> q;
        int w=0;
        while(t){
            if(t->leftchild)
                q.push(t->leftchild);
            if(t->rightchild)
                q.push(t->rightchild);
            if(q.size()>w)
                w=q.size()-1;
            if(!q.empty()){
                t=q.front();
                q.pop();
            }
            else
                break;
        }
        return w;
    }
private:
    node<T> *root; //根节点
};

int main(){
    BinaryTree<int> b;
    int n;
    cin>>n;
    int *p=new int[n];
    for(int i=0;i<n;i++)
        cin>>p[i];
    b.store(p, n, 0);
    b.levelOrder(b.getRoot());
    cout<<"There are "<<b.size(b.getRoot())<<" leaf nodes."<<endl;
    cout<<"The with of this binary tree is "<<b.width(b.getRoot())<<endl;
    b.swap(b.getRoot());
    cout<<"Swaped binary tree:"<<endl;
    b.levelOrder(b.getRoot());
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值