二叉查找树之二

 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树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。

例如输入:

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

输出:

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

 

递归解法:

void MirrorRecursively(BSTreeNode *pNode)
{if (!pNode) return;// swap the right and left child sub-treeBSTreeNode *pTemp = pNode->left;pNode->left = pNode->right;pNode->right = pTemp;// mirror left child sub-tree if not nullif (pNode->left)MirrorRecursively(pNode->left);  // mirror right child sub-tree if not nullif (pNode->right)MirrorRecursively(pNode->right); 
}

 

由于递归的本质是编译器生成了一个函数调用的栈,因此用循环来完成同样任务时最简单的办法就是用一个辅助栈来模拟递归。首先我们把树的头结点放入栈中。在循环中,只要栈不为空,弹出栈的栈顶结点,交换它的左右子树。如果它有左子树,把它的左子树压入栈中;如果它有右子树,把它的右子树压入栈中。这样在下次循环中就能交换它儿子结点的左右子树了。

void MirrorIteratively(BSTreeNode *pTreeHead)
{if (!pTreeHead) return;stack<BSTreeNode *> stackTreeNode;stackTreeNode.push(pTreeHead);while (stackTreeNode.size()) {BSTreeNode *pNode = stackTreeNode.top();stackTreeNode.pop();// swap the right and left child sub-treeBSTreeNode *pTemp = pNode->left;pNode->left = pNode->right;pNode->right = pTemp;// push left child sub-tree into stack if not nullif(pNode->left)stackTreeNode.push(pNode->left);// push right child sub-tree into stack if not nullif(pNode->right)stackTreeNode.push(pNode->right);}   
}

 

 

将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 pathif (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();
}

运行结果为:

[root@localhost 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);
}

 

转载于:https://www.cnblogs.com/chenny7/p/4120961.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PS滤镜绘制漂亮线条制作

先看最后效果   1&#xff0e;新建一图层&#xff0d;&#xff0d;&#xff0d;选择画笔画条垂直线&#xff08;颜色为白色&#xff09;。  2&#xff0e;滤镜里面的动感模糊&#xff08;大小视效果而定吧&#xff09;  3&#xff0e;两头细的线条出来了  执行后效果如…

ffmpeg.exe 笔记

2019独角兽企业重金招聘Python工程师标准>>> open_input_file: avformat_open_input 打开了输入文件 av_dict_get 获取信息 setup_find_stream_info_opts avformat_find_stream_info codec avcodec_find_decoder open_output_file avformat_alloc_output_context2(…

java comparator排序顺序_Java 集合排序策略接口 Comparator

1. 前言 最近用到了集合排序&#xff08;基于 Java 8&#xff09;。现在我能用 Stream 的就用 Stream &#xff0c;真香&#xff01;排序可以这么写&#xff1a; List<People> peoples new ArrayList<>();// 中间省略// 按照年龄从小到大排序 peoples.sort(Compar…

TCP/IP、Http的区别

2019独角兽企业重金招聘Python工程师标准>>> TPC/IP协议是传输层协议&#xff0c;主要解决数据如何在网络中传输&#xff0c;而HTTP是应用层协议&#xff0c;主要解决如何包装数据。关于TCP/IP和HTTP协议的关系&#xff0c;网络有一段比较容易理解的介绍&#xff1a…

小程序循环里做字符串拼接_昨天还在for循环里写加号拼接字符串的那个同事,今天已经不在了...

引言都说 StringBuilder 在处理字符串拼接上效率要强于 String&#xff0c;但有时候我们的理解可能会存在一定的偏差。最近我在测试数据导入效率的时候就发现我以前对 StringBuilder 的部分理解是错误的。后来我通过实践测试 找原理 的方式搞清楚了这块的逻辑。现在将过程分享…

【T-SQL系列】新的排序函数

【T-SQL系列】新的排序函数 原文:【T-SQL系列】新的排序函数如&#xff1a;ROW_NUMBER、RANK、DENSE_RANK三个分析函数都是按照col1分组内从1开始排序 ROW_NUMBER() 是没有重复值的排序(即使两天记录相等也是不重复的)&#xff0c;可以利用它来实现分页 DENSE_RANK() 是连续排序…

java mysql修改表结构字段_【开发技术】java+mysql 更改表字段的步骤

1).首先通过SQL更改MYSQL库中的表结构(下面是一些例子)ALTER TABLE illegalactivate ADD macethaddress varchar(250) NOT NULL;Alter TABLE illegalactivate drop primary key;ALTER TABLE illegalactivate ADD CONSTRAINT PK_illegalactivate PRIMARY KEY ( macaddress…

阿里云rds升级mysql8_为更强大而生的开源关系型数据库来了!阿里云RDS for MySQL 8.0 正式上线!...

2019年5月29日15时&#xff0c;阿里云RDS for MySQL 8.0正式上线&#xff0c;使得阿里云成为紧跟社区步伐&#xff0c;发布MySQL最新版本的云厂商。RDS for MySQL 8.0 产品是阿里云推出的 MySQL 系列云产品之一&#xff0c;使用完全兼容 MySQL 8.0 的阿里云 AliSQL 8.0 分支&am…

mysql gtid 还是pxc_记一次 PXC 集群拆分引发的思考

原标题&#xff1a;记一次 PXC 集群拆分引发的思考作者简介冷正磊2018年2月加入去哪儿网 DBA 团队&#xff0c;主要负责机票业务的 MySQL 和 Redis 数据库的运维管理工作&#xff0c;以及数据库自动化运维平台部分功能的开发工作&#xff0c;对数据库技术具有浓厚兴趣&#xff…

java_IO总结(一)

所谓IO&#xff0c;也就是Input与Output的缩写。在java中&#xff0c;IO涉及的范围比较大&#xff0c;这里主要讨论针对文件内容的读写 其他知识点将放置后续章节&#xff08;我想&#xff0c;文章太长了&#xff0c;谁都没耐心翻到最后&#xff09; 对于文件内容的操作主要分为…

ocsng mysql connection problem_OCSNG 介绍及其工作原理

OCSNG部署&#xff1a;http://wowking.blog.51cto.com/1638252/994441OCSNG 是什么呢&#xff1f;OCSNG就是Open Computer and Software Inventory Next Generation是一款免费软件&#xff0c;它使用户能够盘点网络工程师的IT资产。OCS-NG收集有关运行OCS客户端程序(“OCS Inve…

hdu--5135--贪心

尽量选边数大的3根木棍来组成一个三角形 一直到无法选取为止 这边计算三角形面积 还是用 海伦公式比较方便 1 #include <iostream>2 #include <algorithm>3 #include <cmath>4 #include <cstring>5 #include <iomanip>6 using namespace std;7 …

【Daily Scrum】12-08

因为TFS的一些问题&#xff0c;到现在一直都看不了Sprint 3的burndown and burn rate. 今天的scrum发现这个Sprint期间大家组里的事情都比较多&#xff0c;不过大家还是有很努力地在晚上和周末来完成ASC Master的任务&#xff0c;辛苦~ Member Today’s WorkTomorrow’s WorkFe…

java 字符串转成图片_java 转换图片为字符串,将字符串转换成图片显示

java 转换图片为字符串&#xff0c;将字符串转换成图片显示&#xff0c;该方法只适用于比较小的图片传输&#xff0c;50K以内&#xff1a;try{// 将图片转换成字符串File imgFile new File("f:\\Vista.png");FileInputStream fis new FileInputStream( imgFile );b…

转移指令检测题9

补全编程&#xff0c;利用loop指令&#xff0c;实现在内存2000H段中查找第一个值为0的字节&#xff0c;找到后&#xff0c;将它的偏移地址存储在DX中 assume cs:code code segment start:mov ax,2000h mov ds,ax mov bx,0 s: mov cl,[bx] mov ch,0 inc cx ;此处为要…

c语言 java append_C++中append函数的用法和函数定义。谢谢!

展开全部要想使用标准C中string类&#xff0c;必须要包含#include // 注意是&#xff0c;不62616964757a686964616fe78988e69d8331333339663434是&#xff0c;带.h的是C语言中的头文件using std::string;using std::wstring;或using namespace std;下面你就可以使用string/wstr…

很好用的程序员在线画图软件

今天向大家推荐一个很好用的在线画图软件&#xff1a;今天向大家推荐一个很好用的在线画图软件&#xff1a;今天向大家推荐一个很好用的在线画图软件&#xff1a;&#xff08;重要的事情说三篇&#xff09;连接地址如下&#xff1a;https://www.processon.com/i/55e3195de4b0df…

java breakpoint_java断点

第一步&#xff1a;用firefox运行程序&#xff0c;当点击保存&#xff0c;提示保存失败后&#xff0c;启动firebug通过请求找到addNew.ezt出现错误&#xff0c;在eztnews.xml里通过ctrlF查找找到请求执行的类和方法找到NewsAction类的doAddNew方法然后在通过找到NewsActions.ja…

OC之ARC环境中的循环strong问题

2019独角兽企业重金招聘Python工程师标准>>> main.m文件&#xff1a; #import <Foundation/Foundation.h> #import "Person.h" #import "Dog.h"int main() {Person *p [[Person alloc] init];Dog *d [[Dog alloc] init];p.dog d;d.per…

Android自定义view之圆形进度条

本节介绍自定义view-圆形进度条思路&#xff1a;根据前面介绍的自定义view内容可拓展得之&#xff1b;1&#xff1a;新建类继承自View2&#xff1a;添加自定义view属性3&#xff1a;重写onDraw(Canvas canvas)4&#xff1a;实现功能下面上代码1.自定义view代码&#xff1a; pub…