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

转载出处: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));
}   




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值