二叉树的各种操作(遍历/深度/距离/转换)

来源:互联网 发布:linux文件实时同步ftp 编辑:程序博客网 时间:2024/05/19 20:43

转载出处:http://www.61mon.com/index.php/archives/191/

(1):前序遍历,中序遍历,后序遍历;
(2):层次遍历;
(3):求树的节点数;
(4):求树的叶子数;
(5):求树的深度;
(6):求二叉树第 k 层的节点个数;
(7):判断两棵二叉树是否结构相同;
(8):求二叉树的镜像;
(9):求两个节点的最低公共祖先节点;
(10):求任意两节点距离;
(11):找出二叉树中某个节点的所有祖先节点;
(12):二叉树前序中序推后序;
(13):判断二叉树是不是平衡二叉树;
(14):判断二叉树是不是完全二叉树;
(15):判断是否是二叉查找树的后序遍历结果;
(16):给定一个二叉查找树中的节点,找出在中序遍历下它的后继和前驱;
(17):二分查找树转化为排序的循环双链表;
(18):二叉树路径等于目标值的所有路径。
(19):二叉树的所有路径。

二叉树节点定义如下:

struct BinaryTreeNode{    int m_nValue;    BinaryTreeNode* m_pLeft;    BinaryTreeNode* m_pRight;};
(1):前序遍历,中序遍历,后序遍历;

前序遍历对于当前节点,先输出该节点,然后输出它的左孩子,最后输出它的右孩子。

递归版本:

void PreOrderTraverse(BinaryTreeNode * pRoot)  {      if(pRoot == NULL)          return;      Visit(pRoot);    PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树      PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树  }  
非递归版本:
/* 前序遍历非递归版 */void PreOrderNonRec(Node * node){    stack<Node*> S;    Node * p = node;    while (p || !S.empty())    {        while (p)        {                                        cout << p->data << " ";  //先输出当前节点              S.push(p);            p = p->left;  //然后输出左孩子        }  //while结束意味着左孩子已经全部输出        if (!S.empty())        {            p = S.top()->right;  //最后输出右孩子            S.pop();        }    }}

中序遍历对于当前节点,先输出它的左孩子,然后输出该节点,最后输出它的右孩子

递归版本:

/* 中序遍历递归版 */void InOrderRec(Node * node){    if (node == nullptr)        return;    InOrderRec(node->left);     //先输出左孩子    cout << node->data << " ";  //然后输出当前节点    InOrderRec(node->right);    //最后输出右孩子}
非递归版本:
/* 中序遍历非递归版 */void InOrderNonRec(Node * node){    stack<Node*> S;    Node * p = node;    while (p || !S.empty())    {        while (p)        {            S.push(p);               p = p->left;          }  //while结束意味着左孩子为空        if (!S.empty())        {            cout << S.top()->data << " ";  //左孩子已经全部输出,接着输出当前节点            p = S.top()->right;            //左孩子全部输出,当前节点也输出后,最后输出右孩子            S.pop();        }    }}

后序遍历对于当前节点,先输出它的左孩子,然后输出它的右孩子,最后输出该节点

递归版本:

/* 后序遍历递归版 */void PostOrderRec(Node * node){    if (node == nullptr)        return;    PostOrderRec(node->left);   //先输出左孩子    PostOrderRec(node->right);  //然后输出右孩子    cout << node->data << " ";  //最后输出当前节点}
非递归版本:

/* 后序遍历非递归版 */void PostOrderNonRec(Node * node){    Node * pre = nullptr;    Node * cur = node;    stack<Node*> S;    S.push(cur);    while (!S.empty())    {        cur = S.top();        if ((!cur->left && !cur->right) ||                     //第一个输出的必是无左右孩子的叶子节点,只要第一个节点输出,            (pre && (pre == cur->left || pre == cur->right)))  //以后的pre就不会是空。此处的判断语句加入一个pre,只是用来        {                                                      //确保可以正确输出第一个节点。            cout << cur->data << " ";  //左右孩子都全部输出,再输出当前节点            pre = cur;            S.pop();        }        else        {            if (cur->right)                S.push(cur->right);  //先进右孩子,再进左孩子,取出来的才是左孩子            if (cur->left)                S.push(cur->left);        }    }}

(2):层次遍历;

层次遍历:广度优先算法,分层输出节点,使用队列的先进先出特性保存头节点,把当前层都压入队列后依次出列输出。

void LevelOrder(Node * node){    Node * p = node;    queue<Node*> Q;  //队列    Q.push(p);    while (!Q.empty())    {        p = Q.front();        cout << p->data << " ";        Q.pop();        if (p->left)            Q.push(p->left);  //注意顺序,先进左孩子        if (p->right)            Q.push(p->right);    }}
最大深度:

(3):求树的节点数;

树的节点数采用递归方式:

int CountNodes(Node * node){    if (node == nullptr)        return 0;    return CountNodes(node->left) + CountNodes(node->right) + 1;}

(4):求树的叶子数;

树的叶子:和树的节点数有所不同,更改递归返回点

int CountLeaves(Node * node){    if (node == nullptr)        return 0;    if (!node->left && !node->right)        return 1;    return CountLeaves(node->left) + CountLeaves(node->right);}


(5):求树的深度;

树的深度:

采用递归方式:

int GetDepth(BinaryTreeNode * pRoot)  {      if(pRoot == NULL) // 递归出口          return 0;      int depthLeft = GetDepth(pRoot->m_pLeft);      int depthRight = GetDepth(pRoot->m_pRight);      return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);   }  

可以采用广度优先的方法计算,输出一层后计数,最后得到的数字即为最大层数。

int GetDepth(TreeNode *root) {    if (!root) {        return 0;    }    int curDep = 0;    queue<TreeNode *>queuelist;    queuelist.push(root);    TreeNode *curNode;    while (!queuelist.empty()) {        int size = queuelist.size();        for (int i = 0; i < size; i++) {            curNode = queuelist.front();            queuelist.pop();            if (curNode->left) {                queuelist.push(curNode->left);            }            if (curNode->right) {                queuelist.push(curNode->right);            }        }        curDep++;    }         return curDep;}
(6):求二叉树第 k 层的节点个数;

第K层节点个数:递归控制,每次向下一层则K减1,直到到达K层。

int GetKLevel(Node * node, int k){    if (node == nullptr)        return 0;    if (k == 1)        return 1;    return GetKLevel(node->left, k - 1) + GetKLevel(node->right, k - 1);}
非递归方式,采用和树的深度相同的思想:

int GetKLevel(TreeNode *root, int K) {    if (!root) {        return 0;    }    int curDep = 0;    queue<TreeNode *>queuelist;    queuelist.push(root);    TreeNode *curNode;    while (!queuelist.empty()) {        int size = queuelist.size();        if ( k == curDep ) {            return size;        }        for (int i = 0; i < size; i++) {            curNode = queuelist.front();            queuelist.pop();            if (curNode->left) {                queuelist.push(curNode->left);            }            if (curNode->right) {                queuelist.push(curNode->right);            }        }        curDep++;    }         return 0;}

(7):判断两棵二叉树是否结构相同;

结构相同:不考虑数字是否相同,只考虑左右子树是否一致。递归方法解最简单。

bool StructureCmp(Node * node1, Node * node2){    if (node1 == nullptr && node2 == nullptr)        return true;    else if (node1 == nullptr || node2 == nullptr)        return false;    return StructureCmp(node1->left, node2->left) && Str1uctureCmp(node1->right, node2->right);}

(8):求二叉树的镜像;

镜像:左右翻转。

递归方式:

void Mirror(Node * node){    if (node == nullptr)        return;    Node * temp = node->left;    node->left = node->right;    node->right = temp;    Mirror(node->left);    Mirror(node->right);}

(9):求两个节点的最低公共祖先节点;

最低公共祖先:需要注意两个节点本来就在一个树上的情况,此时最低公共祖先就是其中靠近根节点的那个。

递归方式:

Node * FindLCA(Node * node, Node * target1, Node * target2){    if (node == nullptr)        return nullptr;    if (node == target1 || node == target2)        return node;    Node * left = FindLCA(node->left, target1, target2);    Node * right = FindLCA(node->right, target1, target2);    if (left && right)  //分别在左右子树        return node;    return left ? left : right;  //都在左子树或右子树}
(10):求任意两节点距离;

任意两节点距离:两节点到最低公共祖先的距离之和。

int FindLevel(Node * node, Node * target){    if (node == nullptr)        return -1;    if (node == target)        return 0;    int level = FindLevel(node->left, target);  //先在左子树找    if (level == -1)        level = FindLevel(node->right, target);  //如果左子树没找到,在右子树找    if (level != -1)  //找到了,回溯        return level + 1;    return -1;  //如果左右子树都没找到}int DistanceNodes(Node * node, Node * target1, Node * target2){    Node * lca = FindLCA(node, target1, target2);  //找到最低公共祖先节点    int level1 = FindLevel(lca, target1);     int level2 = FindLevel(lca, target2);    return level1 + level2;}
(11):找出二叉树中某个节点的所有祖先节点;

所有祖先节点:

bool FindAllAncestors(Node * node, Node * target){    if (node == nullptr)        return false;    if (node == target)        return true;    if (FindAllAncestors(node->left, target) || FindAllAncestors(node->right, target))  //找到了    {        cout << node->data << " ";        return true;  //回溯    }    return false;  //如果左右子树都没找到}
(12):二叉树前序中序推后序

/* 前序遍历和中序遍历结果以长度为n的数组存储,pos1为前序数组下标,pos2为后序下标 */int pre_order_arry[n];int in_order_arry[n];void PrintPostOrder(int pos1, int pos2, int n){    if (n == 1)    {        cout << pre_order_arry[pos1];        return;    }    if (n == 0)        return;    int i = 0;    for (; pre_order_arry[pos1] != in_order_arry[pos2 + i]; i++)        ;    PrintPostOrder(pos1 + 1, pos2, i);    PrintPostOrder(pos1 + i + 1, pos2 + i + 1, n - i - 1);    cout << pre_order_arry[pos1];}
(13):判断二叉树是不是平衡二叉树;

平衡二叉树:一棵高度平衡的二叉树的定义是:一棵二叉树中每个节点的两个子树的深度相差不会超过1。 

int getDepth (TreeNode *root) {        if(!root) return 0;          int left = getDepth(root->left);          int right = getDepth(root->right);          return  ((left>right)?left:right)+1;      }    bool isBalanced(TreeNode *root) {        // write your code here        if(!root) return true;          int left = getDepth(root->left);          int right = getDepth(root->right);          if((left > right + 1)||(right > left+1)) {
            return false;  
        }        else{               return isBalanced(root->left)&&isBalanced(root->right);          }      }

(14):判断二叉树是不是完全二叉树;

完全二叉树:除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。

bool IsCBT(Node * node){    bool flag = false;    queue<Node*> Q;    Q.push(node);    while (!Q.empty())    {        Node * p = Q.front();        Q.pop();        if (flag)        {            if (p->left || p->right)                return false;        }        else        {            if (p->left && p->right)            {                Q.push(p->left);                Q.push(p->right);            }            else if (p->right)  //只有右节点                return false;            else if (p->left)  //只有左节点            {                Q.push(p->left);                flag = true;            }            else  //没有节点                flag = true;        }    }    return true;}
flag用来标识是否已经出现了第一个没有右孩子的节点,如果出现了则认为是倒数第二行,然后依次判断倒数第一行的各个节点还有没有孩子。
(15):判断是否是二叉查找树的后序遍历结果;

int array[n];  //长度为n的序列,注意begin和end遵循的是左闭右闭原则bool IsSequenceOfBST(int begin, int end){    if (end - begin <= 0)        return true;    int root_data = array[end];  //数组尾元素为根节点    int i = begin;    for (; array[i] < root_data; i++);  //取得左子树    int j = i;    for (; j < end; j++)        if (array[j] < root_data)  //此时右子树应该都大于根节点;若存在小于,直接return false            return false;    return IsSequenceOfBST(begin, i - 1) && IsSequenceOfBST(i, end - 1);  //左右子树是否都满足}
(16):给定一个二叉查找树中的节点,找出在中序遍历下它的后继和前驱;

查找后续节点:

如果节点中有指向父亲节点的指针(假如根节点的父节点为 nullptr),则:
(1):如果当前节点有右孩子,则后继节点为这个右孩子的最左孩子;
(2):如果当前节点没有右孩子;
  (2.1):当前节点为根节点,返回 nullptr;
  (2.2):当前节点只是个普通节点,也就是存在父节点;
    (2.2.1):当前节点是父亲节点的左孩子,则父亲节点就是后继节点;
    (2.2.2):当前节点是父亲节点的右孩子,沿着父亲节点往上走,直到 n-1 代祖先是 n 代祖先的左孩子,则后继为 n 代祖
          先或遍历到根节点也没找到符合的,则当前节点就是中序遍历的最后一个节点,返回 nullptr。

Node * Increment(Node * node){    if (node->right)    {        node = node->right;        while (node->left)            node = node->left;    }    else    {        Node * p = node->parent;        while (p && p->right == node)        {            node = p;            p = p->parent;        }        node = p;    }    return node;}


(17):二分查找树转化为排序的循环双链表;

循环双链表:使用中序遍历的方法输出索引数组,再进行组合可以达目的,时间O(n),空间O(n);

如果限制使用的空间,则需要在遍历的同时构建链表,使用递归方法:

/* 合并两个a,b两个循环双向链表 */Node * Append(Node * a, Node * b){    if (a == nullptr) return b;    if (b == nullptr) return a;    //分别得到两个链表的最后一个元素    Node * a_last = a->left;    Node * b_last = b->left;    //将两个链表头尾相连      a_last->right = b;    b->left = a_last;    a->left = b_last;    b_last->right = a;    return a;}/* 递归的解决二叉树转换为双链表 */Node * TreeToList(Node * node){    if (node == nullptr) return nullptr;    //递归解决子树    Node * left_list = TreeToList(node->left);    Node * right_list = TreeToList(node->right);    //把根节点转换为一个节点的双链表。方便后面的链表合并      node->left = node;    node->right = node;    //合并之后即为升序排列    left_list = Append(left_list, node);    left_list = Append(left_list, right_list);    return left_list;}

(18):叉树路径等于目标值的所有路径

路径和:最容易想到的办法就是递归,需要注意传入的参数是引用。

class Solution {public:    /**     * @param root the root of binary tree     * @param target an integer     * @return all valid paths     */    void path(vector<vector<int>>&ve, vector<int> v,int val, TreeNode* root){        if(root==NULL)return;        val=val-root->val;        v.push_back(root->val);        if(val==0&&root->left==NULL&&root->right==NULL){            ve.push_back(v);            return;        }        path(ve,v,val,root->left);        path(ve,v,val,root->right);    }    vector<vector<int>> binaryTreePathSum(TreeNode *root, int target) {        // Write your code here        vector<vector<int>> ve;        vector<int>v;        path(ve,v,target,root);        return ve;    }};

(19):叉树的所有路径

vector<string> binaryTreePaths(TreeNode* root) {    // Write your code here    vector<string> res;    if(root==NULL) return res;    binaryTreePathsCore(root,res,to_string(root->val));    return res;}void binaryTreePathsCore(TreeNode* root,vector<string> &str,string strpath){    if(root->left==NULL&&root->right==NULL)//叶子结点    {        str.push_back(strpath);        return;    }    if(root->left!=NULL)        binaryTreePathsCore(root->left,str,strpath+"->"+to_string(root->left->val));    if(root->right!=NULL)        binaryTreePathsCore(root->right,str,strpath+"->"+to_string(root->right->val));}