二叉树

本文深入探讨了二叉树及其变体,包括二叉搜索树、二叉树的直径、重建二叉树、判断树的子结构、寻找最近公共祖先等问题,以及如何在有序数组中构建二叉搜索树、将二叉搜索树转换为排序的双向链表等。文章详细阐述了每种问题的解决方法,提供了完整的代码实现,旨在帮助读者全面掌握二叉树与数据结构的应用。
摘要由CSDN通过智能技术生成


题目:输入两棵二叉树A和B,判断树B是不是A的子结构

bool IsChildTree(Node * father, Node * son)

{

    if(father == NULL && son == NULL)

        return true;

 

    if(father == NULL && son != NULL)

        return false;

 

    if(father != NULL && son == NULL)

        return true;

   

    //如果当前结点相同,判断左右子树是否子结构

    if(father->data == son->data )

    {

         return IsChildTree(father->left, son->left)&& IsChildTree(father->right, son->right);

    }

 

    //判断son子树是否是左子树的子结构

    if(IsChildTree(father->left, son))

        return true;

 

    //判断son子树是否是右子树的子结构

    if(IsChildTree(father->right, son))

        return true;

 

    return false;

}

题目:寻找最近公共祖先LCA(Lowest Common Ancestor)

Node* getLCA(Node* root, Node*x, Node* y)

{

    if(root == NULL) return NULL;

 

    if(x == root || y == root)

        return root;

 

    Node* pleft = getLCA(root->left, x, y);

    Node* pright = getLCA(root->right, x,y);

 

    if(pleft == NULL)

        return pright;

    else if(pright == NULL)

        return pleft;

 

    else return root;

}

分别得到根节点root到结点x的路径和到结点y的路径,由于这个路径是从跟结点开始的,最低的共同父结点就是路径中的最后一个共同结点,即是LCA。

//得到根节点pHeadpNode的路径

bool GetNodePath(Node* pHead, Node* pNode, std::list<Node*>& path)

{

    if(pHead == pNode)

        return true;

 

    path.push_back(pHead);

 

    bool found = false;

 

    if(pHead->left != NULL)

        found = GetNodePath(pHead->left,pNode, path);

 

    if(!found && pHead->right)

        found = GetNodePath(pHead->right,pNode, path);

 

    if(!found)

        path.pop_back();

 

    return found;

}

 

// 从两个列表path1path2中得到最后一个共同的结点

TreeNode* LastCommonNode(conststd::list<TreeNode*>& path1, const std::list<TreeNode*>& path2)

{

    std::list<TreeNode*>::const_iteratoriterator1 = path1.begin();

    std::list<TreeNode*>::const_iteratoriterator2 = path2.begin();   

    TreeNode* pLast = NULL;

    while(iterator1 != path1.end() && iterator2 !=path2.end())

    {

        if(*iterator1 == *iterator2)

            pLast = *iterator1;

        iterator1++;

        iterator2++;

    }

    return pLast;

}

题目:寻找二叉搜索树(BST)的最低公共祖先(LCA)

利用BST的性质:从根结点开始搜索,当第一次遇到当前结点的值介于两个给定的结点值之间时,这个当前结点就是要找的LCA。

Node* FindLCA(Node* root,int x, int y)

{

    Node * t = root;

    while(1)

    {

        if(t->data > x && t->data >y)

            t = t->left;

        else if(t->data < x && t->data < y)

            t = t->right;

        else return t;

    }

}

题目:寻找BST中序遍历序列中第k个元素

void findK(Node* p, int& k)

{

    if(!p || k < 0) return;

    findK(p->left, k);

    -- k;

    if(k == 0)

    {

        print p->data;

        return

    }

    findK(p->right, k);

}

题目:求二叉树中相距最远的两个节点之间的距离

求两节点的最远距离,实际就是求二叉树的直径。本问题可以转化为求“二叉树每个节点的左右子树高度和的最大值”。

int TreeHeight(Node* root, int& max_distance)

{

    if(root == NULL)

    {

        max_distance = 0;

        return 0;

    }

 

    int left_height,right_height;

 

    if(root->left)

        left_height =TreeHeight(root->left,max_distance)+1;

    else

        left_height = 0;

 

    if(root->right)

        right_height =TreeHeight(root->right,max_distance)+1;

    else

        right_height = 0;

 

    int distance = left_height + right_height;

    if (max_distance < distance) max_distance = distance;

 

    return (left_height > right_height ? left_height :right_height);

}

 

int TreeDiameter(Node* root)

{

    int max_distance = 0;

    if(root) TreeHeight(root, max_distance);

    return max_distance;

}

题目:求二叉树中节点的最大距离:如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,定义“距离”为两节点之间边的个数。(此题就是求二叉树中相距最远的两个节点之间的距离,代码来自《编程之美》)

//结点的定义

typedef struct Node

{

    Node * left;

    Node * right;

    int maxLeft;

    int maxRight;

    char chValue;

}Node,*pNode;

 

//最大距离

int maxLen = 0;

 

//寻找二叉树中节点的最大距离

void findMaxLength(Node* root)

{

    if(root == NULL) return;

 

    //如果左子树为空,则该节点左边最长距离为0

    if(root->left == NULL)     root->maxLeft = 0;

 

    //如果右子树为空,则该节点右边最长距离为0

    if(root->right == NULL)    root->maxRight = 0;

 

    //如果左子树不为空,递归寻找左边最长距离

    if(root->left != NULL)     findMaxLength(root->left);

 

    //如果右子树不为空,递归寻找右边最长距离

    if(root->right != NULL)    findMaxLength(root->right);

 

    //计算左子树最长节点距离

    if(root->left != NULL)

    {

        int tempMax = 0;

        if(root->left->maxLeft >root->left->maxRight)

            tempMax =root->left->maxLeft;

        else tempMax = root->left->maxRight;

        root->maxLeft = tempMax+1;

    }

 

    //计算右子树最长节点距离

    if(root->right != NULL)

    {

        int tempMax = 0;

        if(root->right->maxLeft >root->right->maxRight)

            tempMax =root->right->maxLeft;

        else tempMax = root->right->maxRight;

        root->maxRight = tempMax+1;

    }

 

    //更新最长距离

    if(root->maxLeft+root->maxRight > maxLen)

        maxLen =root->maxLeft+root->maxRight;

}

题目:重建二叉树,通过先序遍历和中序遍历的序列可以唯一确定一棵二叉树(通过中序和后序遍历的序列也可以唯一确定一棵二叉树,但是不能通过先序和后序遍历序列唯一确定二叉树)

//通过先序遍历和中序遍历的序列重建二叉树

void ReBuild(char* pPreOrder,char* pInOrder,int nTreeLen,Node**pRoot)

{

    //检查边界条件

    if(pPreOrder == NULL || pInOrder == NULL)

        return;

 

    //获得前序遍历的第一个节点

    Node* temp = new Node;

    temp->data = *pPreOrder;

    temp->left = NULL;

    temp->right = NULL;

 

    //如果节点为空,把当前节点复制到根节点

    if(*pRoot == NULL) *pRoot = temp;

 

    //如果当前树长为1,那么已经是最后一个节点

    if(nTreeLen == 1) return;

 

    //寻找子树长度

    char* pOrgInOrder = pInOrder;

    char* pLeftEnd = pInOrder;

    int nTempLen = 0;

 

    //找到左子树的结尾

    while(*pPreOrder != *pLeftEnd)

    {

        if(pPreOrder == NULL || pLeftEnd == NULL)

            return;

        //记录临时长度,以免溢出

        nTempLen++;

        if(nTempLen > nTreeLen) break;

        pLeftEnd++;

    }

 

    //寻找左子树长度

    int nLeftLen = (int)(pLeftEnd-pOrgInOrder);

 

    //寻找右子树长度

    int nRightLen = nTreeLen-nLeftLen-1;

 

    //重建左子树

    if(nLeftLen > 0)

        ReBuild(pPreOrder+1,pInOrder,nLeftLen,&((*pRoot)->left));

 

    //重建右子树

    if(nRightLen > 0)

        ReBuild(pPreOrder+nLeftLen+1,pInOrder+nLeftLen+1,nRightLen,&((*pRoot)->right));

}

题目:输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。

例如输入整数22和如下二元树

                                            10
                                           /  \
                                          5   12
                                        /   \   
                                      4      7 

则打印出两条路径:10, 12和10, 5, 7。

分析:这是百度的一道笔试题,考查对树这种基本数据结构以及递归函数的理解。 当访问到某一结点时,把该结点添加到路径上,并累加当前结点的值。如果当前结点为叶结点并且当前路径的和刚好等于输入的整数,则当前的路径符合要求,把它打印出来。如果当前结点不是叶结点,则继续访问它的子结点。当前结点访问结束后,递归函数将自动回到父结点。因此我们在函数退出之前要在路径上删除当前结点并减去当前结点的值,以确保返回父结点时路径刚好是根结点到父结点的路径。我们不难看出保存路径的数据结构实际上是一个栈结构,因为路径要与递归调用状态一致,而递归调用本质就是一个压栈和出栈的过程。

#include <iostream>

#include <vector>

using namespace std;

 

struct Node

{

    int value;

    Node* left;

    Node* right;

    Node(){ left = NULL; right = NULL; }

    Node(int v){ value = v; left = NULL; right = NULL; }

};

 

void findpath(Node* root,vector<int>& nodes,int sum)

{

    if(root == NULL) return;

    nodes.push_back(root->value);

    if(root->left == NULL && root->right == NULL)

    {

        if(root->value == sum)

        {

            for(int i=0; i<nodes.size(); i++)

                cout<<nodes[i]<<" ";

            cout<<endl;

        }

    }

    else

    {

        if(root->left != NULL)

        {

            findpath(root->left,nodes,sum-root->value);

        }

        if(root->right != NULL)

        {

           findpath(root->right,nodes,sum-root->value);

        }

    }

    nodes.pop_back();

}

 

int main()

{

    Node *tmp ;

    Node* root = new Node(10);

    tmp = new Node(5);

    root->left = tmp ;

    tmp = new Node(12);

    root->right = tmp;

    tmp = new Node(4);

    root->left->left = tmp;

    tmp = new Node(7);

    root->left->right = tmp;

    vector<int> v;

    findpath(root,v,22);

    return 0;

}

题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回true,否则返回false。

例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:

        8
       /   \
      6     10
     / \     /  \
   5   7  9    11

因此返回true。如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。

分析:在后续遍历得到的序列中,最后一个元素为树的根结点。从头开始扫描这个序列,比根结点小的元素都应该位于序列的左半部分;从第一个大于根节点开始到根结点前面的一个元素为止,所有元素都应该大于根结点,因为这部分元素对应的是树的右子树。根据这样的划分,把序列划分为左右两部分,我们递归地确认序列的左、右两部分是不是都是二元查找树。

bool verifySquenceOfBST(int squence[], int length)

{

    if(squence == NULL || length <= 0)

        return false;

 

    // root of aBST is at the end of post order traversal squence

    int root = squence[length - 1];

 

    // the nodesin left sub-tree are less than the root

    int i = 0;

    for(; i < length - 1; ++ i)

    {

        if(squence[i] > root)

            break;

    }

 

    // the nodesin the right sub-tree are greater than the root

    int j = i;

    for(; j < length - 1; ++ j)

    {

        if(squence[j] < root)

            return false;

    }

 

    // verifywhether the left sub-tree is a BST

    bool left = true;

    if(i > 0)

        left = verifySquenceOfBST(squence, i);

 

    // verifywhether the right sub-tree is a BST

    bool right = true;

    if(i < length - 1)

        right = verifySquenceOfBST(squence + i,length - i - 1);

 

    return (left && right);

}

题目:怎样编写一个程序,把一个有序整数数组放到二叉搜索树中?

分析:本题考察二叉搜索树的建树方法。关于树的算法设计一定要联想到递归,因为树本身就是递归的定义。

Node* array_to_tree(int array[], int start, int end)

{

    if (start > end) return NULL;

    int m = start + (end-start)/2;

    Node* root = new Node(array[m]);

    root->left = array_to_tree(array, start,m-1);

    root->right = array_to_tree(array, m+1, end);

    return root;

}

 

Node* array2Tree(int array[], int n)

{

    return array_to_tree(array,0,n-1);

}

题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。比如将二元查找树

                                           10
                                         /    \
                                       6       14
                                     /  \     /  \
                                 4    8  12  16

转换成双向链表 4=6=8=10=12=14=16。

  分析:本题是微软的面试题。很多与树相关的题目都是用递归的思路来解决,本题也不例外。下面我们用两种不同的递归思路来分析。

  思路一:当我们到达某一结点准备调整以该结点为根结点的子树时,先调整其左子树,将左子树转换成一个排好序的左子链表,再调整其右子树转换右子链表。最后链接左子链表的最右结点(左子树的最大结点)、当前结点和右子链表的最左结点(右子树的最小结点)。从树的根结点开始递归调整所有结点。

  思路二:我们可以中序遍历整棵树。按照这个方式遍历树,比较小的结点先访问。如果我们每访问一个结点,假设之前访问过的结点已经调整成一个排序双向链表,我们再把调整当前结点的指针将其链接到链表的末尾。当所有结点都访问过之后,整棵树也就转换成一个排序双向链表了。

    //定义二元查找树结点的数据结构如下:

   struct BSTreeNode // a node inthe binary search tree

    {

        int       m_nValue; // value of node

        BSTreeNode *m_pLeft; // left child of node

        BSTreeNode *m_pRight; // right child of node

    };

思路一对应的代码:

// Covert a subbinary-search-tree into a sorted double-linked list

// Input: pNode - the head ofthe sub tree

//        asRight - whether pNode is the rightchild of its parent

// Output: if asRight is true,return the least node in the sub-tree

//         else return the greatest node in thesub-tree

BSTreeNode*ConvertNode(BSTreeNode* pNode, bool asRight)

{

    if(!pNode) return NULL;

 

    BSTreeNode *pLeft = NULL;

    BSTreeNode *pRight = NULL;

 

    // Convertthe left sub-tree

    if(pNode->m_pLeft)

        pLeft = ConvertNode(pNode->m_pLeft, false);

 

    // Connectthe greatest node in the left sub-tree to the current node

    if(pLeft)

    {

        pLeft->m_pRight = pNode; //m_pRight pointer works as the "next" pointer

        pNode->m_pLeft = pLeft;  //m_pLeftpointer works as the "previous" pointer

    }

 

    // Convertthe right sub-tree

    if(pNode->m_pRight)

        pRight =ConvertNode(pNode->m_pRight, true);

 

    // Connectthe least node in the right sub-tree to the current node

    if(pRight)

    {

        pNode->m_pRight = pRight;

        pRight->m_pLeft = pNode;

    }

 

    BSTreeNode *pTemp = pNode;

 

    // If thecurrent node is the right child of its parent,

    // return the least node in the tree whoseroot is the current node

    if(asRight)

    {

        while(pTemp->m_pLeft)

            pTemp = pTemp->m_pLeft;

    }

    // If thecurrent node is the left child of its parent,

    // return the greatest node in the treewhose root is the current node

    else

    {

        while(pTemp->m_pRight)

            pTemp = pTemp->m_pRight;

    }

 

    return pTemp;

}

 

// Covert a binary search treeinto a sorted double-linked list

// Input: the head of tree

// Output: the head of sorteddouble-linked list

BSTreeNode* Convert(BSTreeNode*pHeadOfTree)

{

    // As we wantto return the head of the sorted double-linked list,

    // we set the second parameter to be true

    return ConvertNode(pHeadOfTree, true);

}

思路二对应的代码:

// Covert a sub binary-search-treeinto a sorted double-linked list

// Input: pNode - the head ofthe sub tree

//        pLastNodeInList - the tail of thedouble-linked list

void ConvertNode(BSTreeNode* pNode, BSTreeNode*& pLastNodeInList)

{

    if(pNode == NULL)

        return;

 

    BSTreeNode *pCurrent = pNode;

 

    // Convertthe left sub-tree

    if (pCurrent->m_pLeft != NULL)

        ConvertNode(pCurrent->m_pLeft,pLastNodeInList);

 

    // Put thecurrent node into the double-linked list

    pCurrent->m_pLeft = pLastNodeInList;

    if(pLastNodeInList != NULL)

        pLastNodeInList->m_pRight =pCurrent;

 

    pLastNodeInList = pCurrent;

 

    // Convertthe right sub-tree

    if (pCurrent->m_pRight != NULL)

        ConvertNode(pCurrent->m_pRight,pLastNodeInList);

}

 

// Covert a binary search treeinto a sorted double-linked list

// Input: pHeadOfTree - thehead of tree

// Output: the head of sorteddouble-linked list

BSTreeNode* Convert(BSTreeNode*pHeadOfTree)

{

    BSTreeNode *pLastNodeInList = NULL;

    ConvertNode(pHeadOfTree, pLastNodeInList);

 

    // Get thehead of the double-linked list

    // m_pLeft pointer works as the"previous" pointer in the double-linked list

    BSTreeNode *pHeadOfList = pLastNodeInList;

    while(pHeadOfList && pHeadOfList->m_pLeft)

        pHeadOfList = pHeadOfList->m_pLeft;

 

    return pHeadOfList;

}

 

(1)非递归定义 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T: ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除结点外n0 , 其余的每一个结点都有且仅有一个直接前驱结点;有零个或多个直接后继结点。 (2)递归定义 一颗大树分成几个大的分枝,每个大分枝再分成几个小分枝,小分枝再分成更小的分枝,… ,每个分枝也都是一颗树,由此我们可以给出树的递归定义。 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T: ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除根结点之外的其他结点分为m(m≥0)个互不相交的集合T0,T1,…,Tm-1,其中每个集合Ti(0≤i<m)本身又是一棵树,称为根的子树(subtree)。 2、掌握树的各种术语: (1) 父母、孩子与兄弟结点 (2) 度 (3) 结点层次、树的高度 (4) 边、路径 (5) 无序树、有序树 (6) 森林 3、二叉树的定义 二叉树(binary tree)是由n(n≥0)个结点组成的有限集合,此集合或者为空,或者由一个根结点加上两棵分别称为左、右子树的,互不相交的二叉树组成。 二叉树可以为空集,因此根可以有空的左子树或者右子树,亦或者左、右子树皆为空。 4、掌握二叉树的五个性质 5、二叉树的二叉链表存储。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值