二叉查找树之二

BST树的经典问题

首先构造如下一棵二元查找树(BST树):

C++代码实现:

typedef struct _BSTreeNode {

    int value;
    struct _BSTreeNode *left;
    struct _BSTreeNode *right;

} BSTreeNode;

static BSTreeNode* insert(BSTreeNode* q, int x)
{
    BSTreeNode* p = (BSTreeNode*) new(BSTreeNode);

    p->value = x;
    p->left = NULL;
    p->right = NULL;

    if (q == NULL) {
        q = p;

    } else if (x < q->value) {
        q->left = insert(q->left, x);

    } else {
        q->right = insert(q->right, x);
    }

    return q;
}

static BSTreeNode* search(BSTreeNode* p, int x)
{
    int find = 0;

    while (p && !find) {

        if (x == p->value) {
            find = 1;

        } else if (x < p->value) {
            p = p->left;

        } else {
            p = p->right;
        }
    }

    if (p == NULL) {
        cout<< "not found" <<endl;
    }

    return p;
}

int main()
{

    int n, key;
    vector<int> path;
    BSTreeNode *BT = NULL;

    int array[11] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};

    for (n=0; n<11; n++) {
        key = array[n];
        BT = insert(BT, key);
    }

    n = 0;
    return 0;
}

对二叉树的遍历除了常见的先序遍历/中序遍历/后序遍历意外,还有广度优先,深度优先遍历。

广度优先遍历(BFS)

沿着树的宽度遍历树的结点,自上而下,从左往右的遍历一棵二叉树。
思路:考虑用队列的数据结构来实现,将根结点放入队列之后。
每次从队列头取出一个结点的同时,将其左孩子、右孩子分别放入队列尾。

void BreadthFirstSearch(BSTreeNode *root, vector<int> &result)
{
    if (!root) return;

    BSTreeNode *q;
    deque<BSTreeNode *> queue;

    queue.push_back(root);

    while (queue.size()) {

        // 从队头取出一个节点
        q = queue.front();
        queue.pop_front();
        result.push_back(q->value);

        if (q->left)
            queue.push_back(q->left);

        if (q->right)
            queue.push_back(q->right);
    }
}

int main()
{

    int n, key;
    vector<int> path;
    BSTreeNode *p1, *p2, *p3;
    BSTreeNode *BT = NULL;

    int array[11] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};

    for (n=0; n<11; n++) {
        key = array[n];
        BT = insert(BT, key);
    }

    BreadthFirstSearch(BT, path);

    vector<int>::iterator it = path.begin();

    for (; it!=path.end(); ++it) {
        cout<<*it<<"\t";
    }
    cout<<endl;
    return 0;
}

深度优先遍历(DFS)

沿着树的深度遍历树的结点,尽可能的搜索树的分支。

思路:考虑用stack的数据结构来实现,将根结点放入stack之后。
每次从栈顶取出一个结点的同时,将其右孩子、左孩子分别压入stack。

void DepthFirstSearch(BSTreeNode* root, vector<int> &result)
{
     if (!root) return;

    BSTreeNode *q;
    stack<BSTreeNode *> nodeStack;

    nodeStack.push(root);

    while (!nodeStack.empty()) {

        q = nodeStack.top();
        nodeStack.pop();

        result.push_back(q->value);

        if(q->right)
            nodeStack.push(q->right);

        if(q->left)
            nodeStack.push(q->left);
    }
}

将BST树转为一个有序的双向链表

将上面的BST树转换为双向链表:如2==3==4==6==7==9==13==15==17==18==20。

我们知道,BST树的中序遍历是有序的,所以只要按这种方式将当前访问的节点加到链表末尾就可以了。

void ConvertNode(BSTreeNode *pNode, BSTreeNode** pLastNodeOfList)
{
    if (NULL == pNode)
        return;

    if (pNode->left) {
        ConvertNode(pNode->left, pLastNodeOfList);
    }

    pNode->left = *pLastNodeOfList;

    if (*pLastNodeOfList) {
        (*pLastNodeOfList)->right = pNode;
    }

    *pLastNodeOfList = pNode;

    if (pNode->right) {
        ConvertNode(pNode->right, pLastNodeOfList);
    }
}

BSTreeNode* Convert_Solution(BSTreeNode* pHeadOfTree)
{
    BSTreeNode *pLastNodeInList = NULL;

    ConvertNode(pHeadOfTree, &pLastNodeInList);

    // 找到双向链表的头指针
    BSTreeNode *pHeaderOfList = pLastNodeInList;

    while (pHeaderOfList && pHeaderOfList->left)
        pHeaderOfList = pHeaderOfList->left;

    return pHeaderOfList;
}

在二叉树中找到和为特定值的所有路径

实现一个FindPath函数,能找到所有节点的和等于50的路径:

void FindPath(BSTreeNode* root, int sum, vector<int>&path, int &current)
{
     if (!root) return;

     current += root->value;
     path.push_back(root->value);

      // find one path
     if (NULL == root->left && NULL == root->right && current == sum) {

          vector<int>::iterator it = path.begin();

          for (; it!=path.end(); ++it) {
               cout<<*it<<"\t";
          }

          cout<<endl;
     }

     if (root->left)
          FindPath(root->left, sum, path, current);

     if (root->right)
          FindPath(root->right, sum, path, current);

     current -= root->value;
     path.pop_back();
}

运行结果为:

[[email protected] cq]# ./a.out
15      6       7       13      9
15      18      17

实现思路:

当访问到某一节点时,将该节点添加到路径上,并累加当前节点的值:

1、如果当前节点为叶节点并且当前路径的和刚和等于输入的值,则当前路径就符合要求;

2、如果当前节点不是叶节点,则继续访问它的子节点;

3、当前节点访问结束后,递归函数将自动回到父节点。因此在函数退出之前要在路径上删除当前节点并减去当前节点的值,以确保返回父节点时路径刚好是根节点到父节点的路径。保存路径的数据结构实际上是一个栈结构,因为路径要与递归调用状态一致,而递归调用本质就是一个压栈和出栈的过程。

判断一个数组是不是一棵BST树的后序遍历

二叉树的后序遍历分为3部分: LETF、RIGHT、ROOT,并且,LEFT部分的值都不大于ROOT,RIGHT部分的值都大于ROOT。

因此,对一个数组,从第一个元素开始遍历,把小于最后一个元素(即ROOT)的部分作为LEFT,大于最后一个元素的部分作为RIGHT。

然后再对LEFT和RIGHT递归判断子树是否也符合条件。

C++代码实现如下:

int verifySequenceOfBST(int *seq, int N)
{
    if (seq == NULL || N <= 0)
        return 0;

    int n, m;

    int root = seq[N-1];                          // 根结点

    for (n=0; n<N-1 && seq[n] <= root; n++);      // 左子树

    for (m=n; m<N-1 && seq[m] > root; m++);      // 右子树

    if (m != N-1) return 0;

    int left = 1, right = 1;

    if (n > 0)
        left = verifySequenceOfBST(seq,n);

    if (n < N-1)
        right = verifySequenceOfBST(seq+n,N-1-n);

    return (left && right);
}

二叉树两结点的最低共同父结点

如果两个节点A、B有共同的父节点C,那么有4种情况:

1、A和B都在C的左子树中;

2、A和B都在C的右子树中;

3、A在C的左子树,B在C的右子树;

4、B在C的左子树,A在C的右子树;

我们从根节点开始,先判断属于上面哪种情况,如果是情况1,只要A、B的共同父节点肯定在左子树中,继续递归;情况2类似处理;

如果是情况3、4,那么A和B的最低共同父节点就是根节点。

C++代码实现如下:

int HasNode(BSTreeNode *pHead, BSTreeNode *pNode)
{
    if (pHead == pNode)
        return 1;

    int has = 0;
    if (pHead->left)
        has = HasNode(pHead->left, pNode);

    if (!has && pHead->right)
        has = HasNode(pHead->right, pNode);

    return has;
}

BSTreeNode *LastCommonParent(BSTreeNode *pHead, BSTreeNode *pNode1, BSTreeNode *pNode2)
{
    if (NULL == pHead || NULL == pNode1 || NULL == pNode2)
        return NULL;

    int leftHasNode1 = 0;
    int leftHasNode2 = 0;

    if (pHead->left) {
        leftHasNode1 = HasNode(pHead->left, pNode1);
        leftHasNode2 = HasNode(pHead->left, pNode2);
    }   

    if (leftHasNode1 && leftHasNode2) {          // 两个结点都在左子树中
        if (pHead->left == pNode1 || pHead->left == pNode2)
            return pHead;
        return LastCommonParent(pHead->left, pNode1, pNode2);
    }   

    int rightHasNode1 = 0;
    int rightHasNode2 = 0;
    if (pHead->right) {
        if (!leftHasNode1)
            rightHasNode1 = HasNode(pHead->right, pNode1);
        if (!leftHasNode2)
            rightHasNode2 = HasNode(pHead->right, pNode2);
    }   

    if (rightHasNode1 && rightHasNode2) {            // 两个结点都在右子树中
        if (pHead->right == pNode1 || pHead->right == pNode2)
            return pHead;
        return LastCommonParent(pHead->right, pNode1, pNode2);
    }   

    if ((leftHasNode1 && rightHasNode2)             // 两个结点一个在左子树中,另一个在右子树中
            || (leftHasNode2 && rightHasNode1))
        return pHead;

    return NULL;
}

树为另一树的子结构

思路:要在树A中查找是否存在和树B结构一样的子树,可以分为两步:

1、在树A中找到和B的根节点一样的节点N;

2、判断树A中以N为根节点的子树是不是包括和树B一样的结构。

递归的实现方式如下:

int TreeEqual(BSTreeNode* Head1, BSTreeNode* Head2)
{
    if (NULL == Head2) return 1;

    if (NULL == Head1) return 0;

    if (Head1->value != Head2->value) return 0;

    return TreeEqual(Head1->left, Head2->left)
        && TreeEqual(Head1->right, Head2->right);

}

int HasSubTreeCore(BSTreeNode* Head1, BSTreeNode* Head2)
{
    int result = 0;

    if (Head1->value == Head2->value) {
        result = TreeEqual(Head1, Head2);
    }   

    if (result == 0 && Head1->left) {
        result = TreeEqual(Head1->left, Head2);
    }   

    if (result == 0 && Head1->right) {
        result = TreeEqual(Head1->right, Head2);
    }   

    return result;
}

int HasSubTree(BSTreeNode* Head1, BSTreeNode* Head2)
{
    if ((Head1 == NULL && Head2 != NULL)
            || (Head1 != NULL && Head2 == NULL))
        return 0;

    if (Head1 == NULL && Head2 == NULL)
        return 1;

    return HasSubTreeCore(Head1, Head2);
}
时间: 2024-11-23 23:50:13

二叉查找树之二的相关文章

数据结构|-二叉查找树(二叉搜索树)的链式存储结构的实现

二叉排序树,又称为二叉查找树. 它或者是一棵空树,或者是具有下列性质的二叉树. 若它的左子树不为空,则左子树上所有的结点的值均小于根结构的值: 若它的右子树不为空,则右字数上所有结点的值均大于它的根结点的值: 它的左右子树也分别为二叉排序树. 优点: 1,排序方便 2,方便查找 3,方便插入和删除 二叉排序树的插入数据: 因为二叉排序树中所有的数都符合排序树的特点,所以任意插入一个数时,都能在遍历树的过程中找到其应该放置的正确位置 二叉排序树的删除数据: 三种情况: 1,叶子结点:直接删除该叶子

PHP 二叉查找树(二叉搜索树)的查找

如下图:在二叉查找树中,搜索节点19是否存在 代码实现: 测试结果: 原文地址:https://www.cnblogs.com/qiangqiangge/p/12283930.html

数据结构(二)非线性结构之二叉树

没有天生的信心,只有不断培养的信心. /** *@author StormMaybin @Date 2016-07-17 */ 上上一篇文章总结了一下线性表,今天就来总结一下数据结构中非线性部分,非线性数据结构包括树图以及网!今天我们先来看看二叉树!二叉树是一种特殊的树结构.在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 基

二叉查找树 详解

二叉查找树又称二叉搜索树,是一种效率极高的数据结构. 二叉查找树的定义是: 对于一棵二叉查找树上的一个节点,他的左子树上的任何一个值都比它小,右子树上的任何一个值都比它大(不考虑相等的情况).他的左右子树又是一棵二叉查找树. 比如下图就是一个二叉查找树: 主要功能有: 插入,查找和删除. 我们还需要定义一个结构体: 1 struct node{ //结构体 2 int data; //数据 3 node *left,*right,*parent; //指针 4 node() : data(0),

二叉查找树BST----java实现

                                                                二叉查找树BST----java实现 1.二叉查找树简介 二叉查找树又名二叉搜索树和二叉排序树.性质如下: 在二叉查找树中: (01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值: (02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值: (03) 任意节点的左.右子树也分别为二叉查找树. (04) 没有键值相等的节点(no

[数据结构与算法]二叉排序(搜索)树实现

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289804.html 定义 二叉排序树又称二叉查找树或二叉搜索树,它或者是一棵空树,或者是具有如下性质的二叉树:1.若它是左子树非空,则左子树上所有节点的值均小于根节点的值2.若它的右子树非空,则右子树上所有节点的值均大于根节点的值3.左.

浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍的二叉查找树(Binary Search Tree,BST)这一数据结构综合了以上两种数据结构的优点. 二叉查找树具有很高的灵活性,对其优化可以生成平衡二叉树,红黑树等高效的查找和插入数据结构,后文会一一介绍. 一 定义 二叉查找树(B

二叉查找树的查找、插入和删除 - Java实现

http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 作者: yangecnu(yangecnu's Blog on 博客园) 出处:http://www.cnblogs.com/yangecnu/ 英文原文的出处:http://algs4.cs.princeton.edu/32bst/ 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍

二叉树--二叉搜索树

一直对AVL这个英文缩写比较困惑,原来一直以为是平衡二叉树的首字母缩写,但是又想不明白,哈!前段时间才明白原来是种这课树的三个人的名字的首字母的,哎,生活处处有惊喜,无知不可怕,现在我也知道了.废话不多说,下面我们说说,树形结构中的那些平衡二叉树. 二叉排序树 树的遍历顺序有3种,二叉排序树,顾名思义,就是一颗有序的二叉树,是一种按照中序遍历树中节点,而输出有序队列的一种树形结构,一种特殊的树形结构. 定义 对于二叉树,假设x为二叉树中的任意一个结点,x节点包含关键字key,节点x的key值记为