二叉树

2017-09-02  本文已影响0人  安安zoe

0. 什么是树

数据的基本单位是数据元素,在涉及到数据处理时数据元素之间的关系称之为结构,我们依据数据元素之间关系的不同,将数据结构分为线性结构非线性结构 两种。

这上面所划分的是数据结构的逻辑结构,而数据是如何在计算机中存储的,这涉及到数据的物理结构,逻辑结构是从解决问题出发,涉及到功能的简历,物理结构设计到响应速度,处理、修改、存储时间等问题。

树:N个结点的有限集合.png

1. 基本性质

  1. 二叉树不存在度大于2的节点,二叉树是有序的树,需要区分左节点和右节点
  2. 设度为0 , 1, 2 的结点的个数为N0,N1,N2,则结点总数为N = N0 + N1 + N2
  3. 设B为二叉树的分支总数,(除了根节点都有一个接入的分支),N = B+1,而分支总数由度为1或2的结点练出,则有 B= N1 + 2N2;
  4. 叶子结点的个数 N0 = N2 +1;
  5. 非空二叉树的第K层最多有 2 ^(k-1) 个节点;
  6. 高度为H的二叉树最多有 2^H -1 个节点;
  7. 结点为 i 所在的层次为 log2(i) +1;
  8. 两种基本二叉树
二叉树.png

2. 二叉树存储结构

3. 二叉树的遍历

二叉树遍历是最基本的操作,分为先序遍历,中序遍历、后序遍历,以及层次遍历。

3.1 递归遍历方式

void pre_order(BTree *root){
    if (root != NULL){
        visit(root); //cout<<root->value
        pre_order(root->leftchild);
        pre_order(root->rightchild);
    }
}
void in_order(BTree* root){
    if (root != NULL){
        in_order(root->leftchild);
        visit(root);
        in_order(root->rightchild);
    }
}
void post_order(BTree * root){
    if (root != NULL){
        post_order(root->leftchild);
        post_order(root->rightchild);
        visit(root);
    }
}

3.2 非递归的遍历方式

#include<iostream>
#include<string>
#include<stack>
using namesapce std;

typedef struct node{
    int data;
    struct node *leftchild, *rightchild;
}BTree;

void pre_order(BTree *root){
    stack<BTree*> s;
    BTree *p = root;

    //当栈非空或者p指针非空时执行
    while (p != NULL || !s.empty()){
        while (p != NULL){
            cout << p->data;
            s.push(p);
            p = p->leftchild;
        }
        if (!s.empty()){
            p = s.top();
            s.pop();
            p->p->rightchild;
        }
    }
}
void in_order(BTree * root){
    stack<BTree *> s;
    BTree * p = root;

    while (p != NULL || !s.empty()){
        while (P != NULL){
            s.push(p);
            p = p->leftchild;
        }
        if (!s.empty()){
            p = s.top;
            cout << p->data;
            s.pop();
            p = p->rightchild;
        }
    }
}
void post_order(BTree *root){
    stack<BTree *> s;
    BTree *p = root, *r = NULL;

    while (p != NULL || !s.empty()){
        if (p != NULL){
            s.push(p);
            p = p->leftchild;  // 最左边
        }
        else{
            p = s.top();
            //右子树存在且没有被访问过
            if (p->rightchild != NULL && p->rightchild != r){
                p = p->rightchild;
                s.push(p);
                p = p->leftchild;
            }
            else{
                p = s.top();
                //弹出结点并访问
                s.pop();
                cout << p->data;
                r = p;
                p = NULL;

            }
        }
    }
}

方法二:
对于任一结点,线将其入栈,如果结点P不存在左孩子和右孩子,则可以直接访问它,如果P上存在左孩子或者右孩子,但是左孩子或者右孩子已经访问过了,则直接访问该结点,否则,将P的右孩子和左孩子依次入栈。
这种方法要简单一些。

void post_order(BTree *root){
    stack<BTree *> s;
    BTree *cur = NULL; //当前结点
    BTree *pre = NULL; //前一次访问的结点

    s.push(root);
    while (!s.empty()){
        cur = s.top();
        if ((cur->leftchild == NULL && cur->rightchild == NULL) ||
            (pre != NULL && (pre == cur->leftchild || pre == cur->rightchild))){
            //如果当前结点没有孩子结点或者孩子结点已经被访问过
            cout << cur->data;
            s.pop();
            pre = cur;
        }
        else{
            if (cur->rightchild != NULL)
                s.push(cur->rightchild);
            if (cur ->rightchild != NULL)
                s.push(cur->leftchild);
        }
    }
}

3.3 层次遍历

《编程之美:分层遍历二叉树》的另外两个实现

// 输出以root为根结点的第level层中的所有结点(从左向右)
// 成功返回 1
// 递归实现
int printNodeAtLevel(BinaryNode *root, int level){
    if (!root || level < 0)
        return 0;
    if (level == 0){  // 根结点为0深度
        cout << root->data << " ";
        return 1;
    }
    return printNodeAtLevel(root->leftchild,level -1) + 
        printNodeAtLevel(root->rightchild,level-1);

}
void layer_order(BTree *tree){
    if (tree == NULL)
        return;

    queue<BTree *> que;
    que.push(tree);

    BTree * p = NULL; 
    while (!que.empty()){
        p = que.front();
        visit(p);
        que.pop();
        if (p->leftchild != NULL)
            que.push(p->leftchild);
        if (p->rightchild != NULL)
            que.push(p->rightchild);
    }
}

// 自己实现队列
# define max 1000;
typedef struct sequeue{
    BTree data[max];
    int front;
    int rear;
}sequeue;

void enter(sequeue *q, BTree t){
    if (q->rear == max){
        cout << "the queue is full" << endl;
    }
    else{
        q->data[q->rear] = t;
        q->rear++;
    }
}

BTree del(sequeue *q, BTree t){
    if (q->front == q->rear){
        return NULL;
    }
    else{
        q->frot++;
        return q->data[q->front -1]
    }
}

void level_tree(bintree t){
    sequeue q;
    Btree temp;
    q.front = q.rear = 0;
    if (!t){
        printf("the tree is empty\n");
        return;
    }
    enter(&q, t);
    while (q.front != q.rear){
        t = del(&q);
        printf("%c ", t->data);
        if (t->lchild){
            enter(&q, t->lchild);
        }
        if (t->rchild){
            enter(&q, t->rchild);
        }
    }
}

4. 二叉树基本使用

二叉树中的那些常见的面试题

4.1 判断两棵二叉树是否相等

比较两棵二叉树是否相等,注意是否可以旋转的问题

bool isequal(BinaryNode *root1, BinaryNode *root2){
    if (root1 == NULL && root2 == NULL)
        return true;
    if (!root1 || !root2)
        return false;
    if (root1->data == root2->data)
        return isequal(root1->leftchild, root2->leftchild) &&
            isequal(root1->rightchild, root2->rightchild);
    else
        return false;
}

//如果左右节点可以旋转
return (isequal(root1->leftchild, root2->leftchild) && isequal(root1->rightchild, 
    root2->rightchild)) || isequal(root1->leftchild, root2->rightchild) &&
    isequal(root1->rightchild, root2->leftchild);

4.2 求二叉树的深度

本质上是二叉树的遍历,先求出左右子树的深度,才能求出根结点的深度

int tree_height(BinaryNode *root){
    int left, right;
    if (root == NULL) return -1;
    
    left = tree_height(root->leftchild);
    right = tree_height(root->rightchild);
    return (left > right) ? (left+1) : (right+1);
}

4.3 求二叉树结点的最大距离

《编程之美: 求二叉树中节点的最大距离》的另一个解法

最大距离.png 情况A和情况B.png

因此情况A需要子树的最大深度,B需要子树的最大距离。

struct result{
    int nMaxDistance; // 最大距离
    int nMaxDepth; // 最大深度
};

result getMaximumDistance(BinaryNode *root){
    if (!root){
        result empty = { 0, -1 }; //最大深度初始化为 -1,调用者要对它加1,然后变为0
        return empty;
    }
    result res;
    result lhs = getMaximumDistance(root->leftchild);
    result rhs = getMaximumDistance(root->rightchild);
    res.nMaxDepth = max(lhs.nMaxDepth + 1, rhs.nMaxDepth + 1);
    res.nMaxDistance = max(max(lhs.nMaxDistance, rhs.nMaxDistance),
        lhs.nMaxDepth + rhs.nMaxDepth + 2);
    return res;

}

4.4 由遍历序列重建二叉树

BinaryNode *Construct(int *preorder, int *inorder, int length){
    if (preorder == NULL || inorder == NULL || length <= 0)
        return NULL; // 注意return NULL
    // 前序 - 首:尾  中序 - 首:尾
    return ConstructCore(preorder, preorder + length - 1, inorder, inorder + length - 1);
}
BinaryNode *ConsturctCore(int *startPreorder, int *endPreorder,
                int * startInorder, int * endInorder){
    //前序遍历第一个数字是根结点的值
    int rootvalue = startPreorder[0];
    BinaryNode *root = new BinaryNode();//建立结点
    root->data = rootvalue;
    root->leftchild = root->rightchild = NULL;

    if (startPreorder == endPreorder){ // 边界条件
        if (startInorder == endInorder && *startPreorder == *startInorder)
            return root;
        else
            throw std:exception("Invalid input");//非法输入
    }

    // 在中序遍历中找到根结点的值
    int *rootInorder = startInorder;
    while (rootInorder <= endInorder && *rootInorder != rootvalue)
        ++rootInorder;
    if (rootInorder == endInorder && *rootInorder != rootValue)
        throw std::exception("Invaild input");
    
    int leftLength = rootInorder - startInorder;
    int *leftPreorderEnd = startPreorder + leftLength;
    if (leftLength > 0)
        //构建左子树 递归
        //中序遍历后根据根节点获取的 左子树元素  rootinorder是中序遍历中的根元素的位置
        root->leftchild = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
    if (leftLength < endPreorder - startPreorder)
        root->rightchild = ConstructCore(leftPreorderEnd + 1, endPreorder, rootInorder, endInorder);
    return root;

}

4.5 从二叉树中查找值为x的结点

bool findBTree(BinaryNode *root, ElemType &x){
    if (root == NULL)
        return false;
    if (root->data == x){
        x = root->data;
        return true;
    }
    else{
        if (findBTree(root->leftchild, x))
            return true;
        if (findBTree(root->rightchild, x))
            return true;
        return false;
    }
}

4.6 统计二叉树中等于给定值x的结点的个数

int Count(BinaryNode *root, ElemType &x){
    if (root == NULL)
        return -1;
    if (root->data == x){
        return Count(root->leftchild, x) + Count(root->rightchild, x) + 1;
    }
    else
        return Count(root->leftchild, x) + Count(root->rightchild, x);
}

4.7 返回x结点所在的层号


int findlevel(BinaryNode *root, int x){
    if (root == NULL)
        return -1;
    if (root->data == x) // 根结点的层号为1
        return 1;
    else{
        int leftlevel = findlevel(root->leftchild, x);
        if (leftlevel >= 1)
            return leftlevel + 1;
        
        int rightlevel = findlevel(root->rightchild, x);
        if (rightlevel >= 1)
            return rightlevel + 1;
    }
    return -1;
}

4.8 从二叉树中找到所有结点的最大值

int MaxValue(BinaryNode * root){
    if (root == NULL)
        return 0;

    int leftmax, rightmax;
    leftmax = MaxValue(root->leftchild);
    rightmax = MaxValue(root->rightchild);
    int submax = (leftmax >= rightmax) ? leftmax : rightmax;
    return (submax >= root->data) ? submax : root->data;
}

4.9 求二叉树中所有结点数

int TreeCount(BinaryNode *root){
    if (root == NULL)
        return -1;
    else
        return Treecount(root->leftchild) + TreeCount(root->rightchild) + 1;
}

4.10 求二叉树中所有叶子结点数目

int LeafCount(BinaryNode *root){
    if (root == NULL)
        return -1;
    if (root->leftchild == NULL && root->rightchild == NULL)
        return 1;
    else
        return LeafCount(root->leftchild) + LeafCount(root->rightchild);
}

4.11 二叉树路径上结点的和等于定值

一棵二叉树每个结点包含一个整数,请设计一个算法输出所有满足条件的路径,此路径上所有结点的和等于定值,注意此类路径不要求必须从根结点开始

void printbuffer(vector<int> buffer, int level, int end){
    for (int i = level, i <= end; i++)
        cout << buffer[i] << " ";
    cout << endl;
}

// 输入:二叉树 sum定值 存储的vector<buffer>
void findsum(BinaryNode *root, int sum, vector<int> buffer, int level){
    if (root == NULL)
        return;
    int temp = sum;
    buffer.push_back(root->data);
    for (int i = level; i >= 0; i--){
        temp -= buffer[i];
        if (temp ==0)
            printbuffer(buffer,i,level)
    }
    findsum(root->leftchild, sum, buffer, level + 1);
    buffer.pop_back();
    level -= 1;
    findsum(root->rightchild, sum, buffer, level + 1);
}

5. 二叉排序树(树的应用)

// 二叉搜索树的中序遍历是递增的
int prev = INT_MIN;
int judgeBST(BinaryNode *root){
    int b1, b2;
    if (root == NULL)
        return 1;
    else{
        b1 = judgeBST(root->rightchild);
        if (b1 == 0 || prev >= root->data)
            return 0;
        prev = root->data;
        b2 = judgeBST(root->rightchild);
        return b2;
    }
}

二叉排序树(二叉查找树)常用操作


// 查找是否含有key值
// 这是迭代写法 也可以递归写法
bool searchBST(BinarySortTree *root,int key){
    if (root == NULL)
        return false;
    BinarySortTree *current = root;
    while (root != NULL){
        if (key == current->data)
            return true;
        else if (key < current->data)
            current = current->leftchild;
        else
            current = current->rightchild;
    }
    return false;
}

// 插入结点
// 如果遇到重复的元素可以根据自己的定义将它归到左子树或者右子树
void insertBST(BinarySortTree *root,int key){
    BinarySortTree *current = root;
    BinarySortTree *pre = NULL;

    while (current != NULL){
        pre = current;
        if (key < current->data)
            current = current->leftchild;
        else if (key>current->data)
            current = current->rightchild;
        else
            return;
    }

    BinarySortTree newnode;
    newnode = (BinarySortTree)malloc(sizeof(BinarySortTreeNode));
    newnode->data = key;
    newnode->leftchild = newnode->rightchild = NULL;
    
    // prev 是要安放的结点的父亲结点
    if (current == NULL)
        current->data = key;
    else if (key < pre->data)
        pre->leftchild = newnode;
    else
        pre->rightchild = newnode;
}

6. 平衡二叉树(树的应用)

//后序遍历
bool isBalanced(BinaryNode* root, int *pDepth){
    if (root == NULL){
        *pDepth = 0;
        return true;
    }
    int left, right;
    if (isBalanced(root->leftchild, &left) && isBalanced(root->rightchild, &right)){
        int diff = left - right;
        if (diff <= 1 && diff >= -1){
            *pDepth = 1 + (left>right ? left : right);
            return true;
        }
    }
    return false;
}

//最大深度和最小深度的差
int maxDepth(BinaryNode *root){
    if (root == NULL)
        return 0;
    return 1 + max(maxDepth(root->leftchild), maxDepth(root->rightchild));
}

int minDepth(BinaryNode *root){
    if (root == NULL)
        return 0;
    return 1 + min(minDepth(root->leftchild), minDepth(root->rightchild));
}

bool isBalanced(BinaryNode *root){
    return (maxDepth(root) - minDepth(root) <= 1);
}

7. 哈夫曼树和哈夫曼编码

[转载]Huffman树

8. 红黑树

9. B树和B+ 树

上一篇下一篇

猜你喜欢

热点阅读