二叉树的结构
class Node<V>{
V value;
Node left;
Node right;
}
1.二叉树的遍历分别是前序、中序、后续、层序遍历。
2.宽度优先遍历(层次遍历)
3.求二叉树的宽度
4.判断搜索二叉树
5.判断平衡二叉树
6.判断完全二叉树
7.判断满二叉树
8. 给定两个节点,找到它们的最低公共祖先节
9. 在二叉树中找到一个节点的后继节点。(j8)
10.寻找一个节点的前驱节点
11.二叉树的序列化和反序列化
12.折纸问题
13.二叉树的镜像
14.判断一个数是不是完全二叉树
15.知道二叉树的先序和中序的遍历结果创建一个树(j7)
判断B是不是A的子结构(j26)
https://blog.csdn.net/snow_5288/article/details/71170904
https://blog.csdn.net/weixin_39411321/article/details/90732295
二叉树的遍历
分别是前序、中序、后续、层序遍历。
//递归实现的前序遍历
static void preOrderRecur(Node* head){
if(head == NULL)
return;
cout << head->data << " ";
preOrderRecur(head->left);
preOrderRecur(head->right);
}
//递归实现的中序遍历
static void inOrderRecur(Node* head){
if(head == NULL)
return;
inOrderRecur(head->left);
cout << head->data << " ";
inOrderRecur(head->right);
}
//递归实现的后序遍历
static void posOrderRecur(Node*head){
if(head == NULL)
return;
posOrderRecur(head->left);
posOrderRecur(head->right);
cout << head->data << " ";
}
//非递归实现的前序遍历
static void preOrderUnRecur(Node* head){
if(head == NULL)
return;
stack<Node*> s;
s.push(head);
while(!s.empty()){
head = s.top();
cout << head->data << " ";
s.pop();
if(head->right != NULL)
s.push(head->right);
if(head->left != NULL)
s.push(head->left);
}
}
//非递归实现的中序遍历
static void inOrderUnRecur(Node* head){
if(head == NULL)
return;
stack<Node*> s;
while(!s.empty()|| head != NULL){
if(head != NULL){
s.push(head);
head = head->left;
}else{
head = s.top();
cout << head->data << " ";
s.pop();
head = head->right;
}
}
}
//非递归实现的后序遍历
static void posOrderUnRecur_1(Node* head){
if(head == NULL){
return;
}
stack<Node*> s1;
stack<Node*> s2;
s1.push(head);
while(!s1.empty()){
head = s1.top();
s1.pop();
s2.push(head);
if(head->left != NULL){
s1.push(head->left);
}
if(head->right != NULL){
s1.push(head->right);
}
}
while(!s2.empty()){
cout << s2.top()->data << " ";
s2.pop();
}
}
宽度优先遍历(层次遍历)
static void breadthFirstTravel(Node* head){
if(head == NULL){
return;
}
queue<Node*> q;
q.push(head);
while(!q.empty()){
head = q.front();
cout << head->data << " ";
q.pop();
if(head->left != NULL){
q.push(head->left);
}
if(head->right != NULL){
q.push(head->right);
}
}
}
求二叉树的宽度
//求一颗二叉树的宽度
static int treeMaxWidth(Node* head){
if(head == NULL)
return 0;
queue<Node*> q;
map<Node*,int> levelMap;
q.push(head);
levelMap[head] = 1;
int maxWidth = 0;
int curWidth = 0;
int curLevel = 0;
while(!q.empty()){
head = q.front();
q.pop();
if(head->left != NULL){
q.push(head->left);
levelMap[head->left] = levelMap.find(head)->second + 1;
//cout << levelMap.find(head)->second + 1 << " ";
}
if(head->right != NULL){
q.push(head->right);
levelMap[head->right] = levelMap.find(head)->second + 1;
}
if(levelMap.find(head)->second > curLevel){
curWidth = 0;
curLevel = levelMap.find(head)->second;
}else{
curWidth++;
}
// cout << curLevel << ": " << curWidth << endl;
maxWidth = maxWidth >= curWidth ? maxWidth:curWidth;
}
return maxWidth + 1;
}
二叉树的相关概念及其判断
搜索二叉树
每个节点的值一定大于左子树的最大值(或左子树为空),小于右子树的最小值(或右子树为空)。
//判断当前二叉树是不是BST树
bool isBSTtree()
{
BSTNode *pre = nullptr;
return isBSTree(_root, pre);
}
bool isBSTree(BSTNode *node,BSTNode *&pre)
{
if (node == nullptr)
{
return true;
}
if (!isBSTree(node->_left, pre))
{
return false;
}
if (pre != nullptr)
{
if (node->_data < pre->_data)
{
return false;
}
}
pre = node;
return isBSTree(node->_right, pre);
}
平衡二叉树:每个节点左子树和右子树的高度相差不超过1.
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
if(pRoot==NULL)
return true;
int rightDepth=getDepth(pRoot->right);
int leftDepth=getDepth(pRoot->left);
if(rightDepth>leftDepth+1||leftDepth>rightDepth+1)
return false;
else return IsBalanced_Solution(pRoot->left)&&IsBalanced_Solution(pRoot->right);
}
int getDepth(TreeNode* pRoot)
{
if(pRoot==NULL)
return 0;
int rightDepth=getDepth(pRoot->right);
int leftDepth=getDepth(pRoot->left);
return 1+(leftDepth>rightDepth?leftDepth:rightDepth);
}
};
完全二叉树与满二叉树:
//判断一颗二叉树是不是完全二叉树
static bool isCompleteBinaryTree(Node* head){
if(head == NULL)
return true;
bool leaf = false;
queue<Node*> q;
q.push(head);
while(!q.empty()){
head = q.front();
q.pop();
if(
(head->left == NULL && head->right != NULL)
||
(leaf && (head->left != NULL || head->right) != NULL)
)
return false;
if(head->left != NULL){
q.push(head->left);
}
if(head->right != NULL){
q.push(head->right);
}
else{
leaf = true;
}
}
return true;
}
//判断一棵树是不是满二叉树
class FullBinaryReturnType{
public:
int height;
int nodes;
FullBinaryReturnType(int height,int nodes){
this->height = height;
this->nodes = nodes;
// cout << "init: height(" << this->height << ") nodes(" << this->nodes << ")" << endl;
}
};
static FullBinaryReturnType fProcess(Node* head){
if(head == NULL){
FullBinaryReturnType res(0,0);
return res;
}
FullBinaryReturnType leftData = fProcess(head->left);
FullBinaryReturnType rightData = fProcess(head->right);
// cout << "LH: " << leftData.height << " RH:" << rightData.height << " LN: " << leftData.nodes << " RN: " << rightData.nodes << endl;
int height = max(leftData.height,rightData.height) + 1;
int nodes = leftData.nodes + rightData.nodes + 1;
//cout << head->data << " height:" << height << " nodes:"<< nodes << endl;
FullBinaryReturnType res(height,nodes);
return res;
}
static bool isFullBinaryTree(Node* head){
FullBinaryReturnType res = fProcess(head);
return ((1 << res.height) - 1 == res.nodes);
}
扩展题目:
1. 给定两个节点,找到它们的最低公共祖先节
static Node* findLowestCommonAncester(Node* head,Node* n1,Node* n2){
if(head == NULL || head == n1 || head == n2)
return head;
Node* l = findLowestCommonAncester(head->left,n1,n2);
Node* r = findLowestCommonAncester(head->right,n1,n2);
if(l != NULL && right != NULL){
return head;
}
return l != NULL ? l : r;
}
2. 在二叉树中找到一个节点的后继节点(j8)中序遍历序列的下一个节点
【题目】 现在有一种新的二叉树节点类型如下:
public class Node {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int val) { value = val; }
}
在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if(pNode==nullptr)
return nullptr;
TreeLinkNode* res=nullptr;
if(pNode->right!=nullptr){
TreeLinkNode* right=pNode->right;;
while(right->left!=nullptr)
right=right->left;
res=right;
}else if(pNode->next!=nullptr){
TreeLinkNode* father=pNode->next;
TreeLinkNode* current=pNode;
while(father!=nullptr&¤t==father->right){
current=father;
father=father->next;
}
res=father;
}
return res;
}
};
3.寻找一个节点的前驱节点
//在二叉树中找到一个节点的前驱节点
static Node_3* findPreNode(Node_3* node){
if(node == NULL)
return NULL;
// cout << node->data << endl;
if(node->l != NULL){
node = node->l;
while(node->r != NULL && node != NULL)
node = node->r;
if(node->l != NULL)
node = node->l;
return node;
}
else{
//cout << node->data << endl;
while(node->parent != NULL && node->parent->r != node){
node = node->parent;
//cout << node->data << endl;
}
return node->parent;
}
}
3.二叉树的序列化和反序列化
序列化:内存里的一棵树如何转化成字符串形式。
反序列化:从字符串形式变成内存里的树。
static string serializePreTree(Node* root){
if(root == NULL)
return "# ";
stringstream ss;
ss << root->data;
string str = ss.str() + " ";
str += serializePreTree(root->left);
str += serializePreTree(root->right);
return str;
}
static Node* recnPreOrder(queue<string>& nodeStr){
string str = nodeStr.front();
nodeStr.pop();
if(str == "#"){
return NULL;
}
int data;
sscanf(str.data(),"%d",&data);
Node* root = new Node(data);
root->left = recnPreOrder(nodeStr);
root->right = recnPreOrder(nodeStr);
return root;
}
static void split(const std::string& s, std::queue<std::string>& v, const std::string& c)
{
std::string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while(std::string::npos != pos2)
{
v.push(s.substr(pos1, pos2-pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if(pos1 != s.length())
v.push(s.substr(pos1));
}
static Node* recnByPreString(string str){
queue<string> q;
string c = " ";
split(str,q,c);
return recnPreOrder(q);
}
这里使用前序遍历的顺序进行序列化。
4.折纸问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后 展开。 此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。 如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从 上到下依次是下折痕、下折痕和上折痕。 给定一个输入参数N,代表纸条都从下边向上方连续对折N次。 请从上到下打印所有折痕的方向。 例如:N=1时,打印: down N=2时,打印: down down up
分析:可以通过实际折纸来寻找规律
当 N = 1: down
当 N = 2: down down up
当 N = 3: down down up down down up up
。。。
可以看出折痕的方向具有一定规律,并且该规律与二叉树十分相似,每一个节点的左子树为 down,右子树为 up。打印结果就是二叉树的中序遍历。
static void paperFolding (int i,int N,bool down){
if(i > N){
return;
}
paperFolding(i + 1,N,true);
cout << (down ? "down " : "up ");
paperFolding(i + 1,N,false);
}
5.二叉树的镜像
递归
void Swap(TreeNode** a,TreeNode** b)
{
TreeNode* tmp = *a;
*a = *b;
*b = tmp;
return;
}
void TreeMirror(TreeNode* root)
{
if(root == NULL)
{
return;
}
Swap(&root->lchild,&root->rchild);
TreeMirror(root->lchild);
TreeMirror(root->rchild);
}
非递归
void TreeMirrorByLoop(TreeNode* root)
{
if(root == NULL)
{
return;
}
SeqQueue queue;
SeqQueueInit(&queue);
SeqQueuePush(&queue,root);
TreeNode* cur = NULL;
while(SeqQueueFront(&queue,&cur))
{
//此处的访问相当于交换左右子树
Swap(&cur->lchild,&cur->rchild);
SeqQueuePop(&queue);
if(cur->lchild != NULL)
{
SeqQueuePush(&queue,cur->lchild);
}
if(cur->rchild != NULL)
{
SeqQueuePush(&queue,cur->rchild);
}
}
return;
}
6.判断一个数是不是完全二叉树
完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个节点的二叉树,当且仅当其每个节点都与深度为K的满二叉树中的编号从1至n的节点一一对应时称之为完全二叉树。
完全二叉树具有以下的特点:
1)只允许最后一层有空缺节点且空缺在右边,即叶子节点只能在层次最大的两层上出现;
2)对任一节点,如果其右子树的深度为j,则其左子树的深度必为j或j+1,即度为1
的点只有1个或0个
int IsCompleteTree(TreeNode* root)
{
if(root == NULL)
{
return 0;
}
SeqQueue queue;
SeqQueueInit(&queue);
SeqQueuePush(&queue,root);
//是否要进入第二阶段
int is_step_tow_flag = 0;
TreeNode* cur = NULL;
while(SeqQueueFront(&queue,&cur))
{
SeqQueuePop(&queue);
//阶段一走这个分支
if(is_step_tow_flag == 0)
{
if(cur->lchild != NULL && cur->rchild != NULL)
{
//同时具备左右子树
SeqQueuePush(&queue,cur->lchild);
SeqQueuePush(&queue,cur->rchild);
}
else if(cur->rchild != NULL && cur->lchild == NULL)
{
//只有右子树,没有左子树,一定不是完全二叉树
return 0;
}
else if(cur->rchild == NULL && cur->lchild != NULL)
{
//只有左子树没有右子树,需要进入第二阶段
is_step_tow_flag = 1;
SeqQueuePush(&queue,cur->lchild);
}
else
{
//没有左右子树
is_step_tow_flag = 1;
}
}
else
{
//阶段二分支
if(cur->lchild == NULL && cur->rchild == NULL)
{
break;
}
else
{
return 0;
}
}//结束阶段一和阶段二的判定
}//循环结束
//所有节点都遍历完了,此时是完全二叉树
return 1;
}
7.知道二叉树的先序和中序的遍历结果创建一个树(j7)
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
int lenpre=pre.size();
int lenvin=pre.size();
if(lenpre==0||lenvin==0)
return NULL;
TreeNode* root=new TreeNode(pre[0]);
vector<int>leftpre,leftvin,rightpre,rightvin;
int rootvalue=pre[0];
int genlocal=0;
for(int i=0;i<lenvin;i++){
if(vin[i]==rootvalue){
genlocal=i;
break;
}
}
for(int j=0;j<genlocal;j++){
leftpre.push_back(pre[j+1]);
leftvin.push_back(vin[j]);
}
for(int k=genlocal+1;k<lenvin;k++){
rightpre.push_back(pre[k]);
rightvin.push_back(vin[k]);
}
root->left=reConstructBinaryTree(leftpre,leftvin);
root->right=reConstructBinaryTree(rightpre,rightvin);
return root;
}
};
树的子结构(j26)
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result=false;
if(pRoot1!=nullptr&&pRoot2!=nullptr){
if(pRoot1->val==pRoot2->val){
result=match(pRoot1,pRoot2);
}
if(!result){
result=HasSubtree(pRoot1->left,pRoot2);
}
if(!result){
result=HasSubtree(pRoot1->right,pRoot2);
}
}
return result;
}
bool match(TreeNode* Root1, TreeNode* Root2){
if(Root2==nullptr)
return true;
if(Root1==nullptr)
return false;
if(Root1->val!=Root2->val)
return false;
return match(Root1->left,Root2->left)&&match(Root1->right,Root2->right);
}
};