第五章--二叉树 板子

1、并查集(按秩合并+路径压缩优化)

板子

class query_set {//并查集,共三个函数
public:
	//第一个函数:初始化函数
	query_set(int n) {
		this->n = n;
		for (int i = 0; i <= n; i++) {
			this->father[i] = i;//开始时候各自为战
			this->rank[i] = 0;
		}
	}
	
	//第二个函数:查找函数+路径压缩优化
	int find(int item) {//找到item对应的集合的头目,每个集合的头目,找爹数组中都指向自己
		if (item <= 0 || item > n) {//防止越界,从1-n存储节点
			return 0;
		}
		if (father[item] == item) {
			return item;
		}
		father[item] = find(father[item]);
		return father[item];
	}

	//第三个函数,合并,找到x y对应集合的头目,把二者合并,合并时,把秩低的合并到秩高的,采用按秩合并优化
	void merge(int x, int y) {
		link(find(x), find(y));
	}

private:
	int father[1000];//找爹数组,看两个点是不是一伙的
	int rank[1000];//记录每个节点的秩,表示以它为根节点的子树的高度最大值
	int n;//表示总共的节点数

	void link(int x, int y) {//根据秩进行两个树的融合,防止随意合并出现退化树(链表)的情况
		if (rank[x] > rank[y]) {
			father[y] = x;
		}
		else {
			father[x] = y;
		}
		if (rank[x] == rank[y]) {//秩相等,合并之后,子树高度必加一
			rank[y]++;//因为rank[x]<=rank[y]把x连在y上面,所以是以y为根
		}
	}
};


例题

力扣:547. 省份数量(题解在力扣里,自行寻找即可)

2、二叉排序树

计算成功/失败ASL

1、成功的
在这里插入图片描述
2、失败的
在这里插入图片描述
效率分析
在这里插入图片描述

删除节点の板子

1、在左面找最大节点

class Solution {
public:
	TreeNode* deleteNode(TreeNode* root, int key) {
		if (root == nullptr) {//走到头了
			return nullptr;
		}
		if (root->val == key) {
			if (root->left == nullptr || root->right == nullptr) {//前两种删除情况
				return root->left == nullptr ? root->right : root->left;
			}
			TreeNode* item = findLeftNode(root->left);//找到左子树中用于替换的节点
			root->left = deleteNode(root->left, item->val);//左子树中先删去他
			item->left = root->left;//再用他换掉目标节点
			item->right = root->right;
			return item;
		}
		else {//不是目标节点
			if (key < root->val) {
				root->left = deleteNode(root->left, key);
			}
			else {
				root->right = deleteNode(root->right, key);
			}
		}
		return root;
	}

private:
	TreeNode* findLeftNode(TreeNode* root) {//左子树中最大,就是一直往右走
		if (root->right == nullptr) {
			return root;
		}
		return findLeftNode(root->right);
	}
};

2、在右面找最小节点

class Solution {
public:
	TreeNode* deleteNode(TreeNode* root, int key) {
		if (root == nullptr) {
			return nullptr;
		}
		if (root->val == key) {
			if (root->left == nullptr || root->right == nullptr) {
				return root->left == nullptr ? root->right : root->left;
			}
			TreeNode* item = findLeftNode(root->right);//找右儿子中最小
			root->right = deleteNode(root->right, item->val);//从右子树中删除它
			item->left = root->left;
			item->right = root->right;
			return item;
		}
		else {
			if (key < root->val) {
				root->left = deleteNode(root->left, key);

			}
			else {
				root->right = deleteNode(root->right, key);
			}
		}
		return root;
	}

private:
	TreeNode* findLeftNode(TreeNode* root) {//右子树中一直往左走
		if (root->left == nullptr) {
			return root;
		}
		return findLeftNode(root->left);
	}
};


例题

力扣:450. 删除二叉搜索树中的节点(题解在力扣里,自行寻找即可)

增加节点の板子

class Solution {
public:
	TreeNode* insertIntoBST(TreeNode* root, int val) {
		if (root == nullptr) {//到空节点了,说明该插入新节点
			return new TreeNode(val, nullptr, nullptr);
		}
		if (root->val > val) {//找到合适应该继续往下走的路径
			root->left = insertIntoBST(root->left, val);
		}
		else {
			root->right = insertIntoBST(root->right, val);
		}
		return root;
	}
};

例题

701. 二叉搜索树中的插入操作(题解在力扣里,自行寻找即可)

3、AVL树

增加/删除节点画图

增加节点
删除节点

判断是否是AVL树

class Solution {
public:
	bool isBalanced(TreeNode* root) {
		if (subHights(root) != -1) {//出现-1即为不平衡
			return true;
		}
		return false;
	}
private:
	int subHights(TreeNode* root) {//用概念即可,任意一个节点左右高度差的绝对值小于1
		if (root == nullptr) {
			return 0;
		}
		int left = subHights(root->left);
		int right = subHights(root->right);
		if (left == -1 || right == -1) {//快速失败通道,一旦发现有一个节点不平衡,整个树都不平衡,快速返回-1退出
			return -1;
		}
		if (abs(left - right) > 1) {
			return -1;
		}
		int ans = max(left, right) + 1;//本层高度:左右高度最大值+1
		return ans;
	}
};

例题

力扣:面试题 04.04. 检查平衡性(题解在力扣里,自行寻找即可)

AVL树增加节点

注:AVL树重在"平衡"
1、AVL增加节点,最可能考的是,手画四种旋转LL、RR、LR、RL,所以这四种必须掌握画法,代码作为一种补充,以防万一让写出AVL加入节点方法。
2、这种方法是很取巧的,并没有旋转,而是利用二叉排序树(AVL树也是二叉排序树的一种)自身特点以及贪心策略重构了树,所以这种做法的前提是,他至少是二叉平衡树,才能用这个方法调平,并不是AVL树调平的通解。
3、考试如果要求AVL树加节点
<1> 调用二叉排序树插入节点方法,把目标节点全部插入
<2> 最后调用此函数一次性把二叉搜索树调平,成为AVL树即可。

class Solution {
public:
	TreeNode* balanceBST(TreeNode* root) {
		getInorder(root);
		return build(0, size - 1);
	}
	void getInorder(TreeNode* root) {//获取中序序列,即升序序列
		if (root == nullptr) {
			return;
		}
		getInorder(root->left);
		trees[size++] = root->val;
		getInorder(root->right);
		return;
	}
	TreeNode* build(int left, int right) {//贪心思想,AVL树左右平衡,所以尽可能每次吧中间当成根节点,这样左右节点数一样,子树也就平衡了
		if (left > right) {//退出条件
			return nullptr;
		}
		int mid = (left + right) / 2;
		TreeNode* l = build(left, mid - 1);
		TreeNode* r = build(mid + 1, right);
		TreeNode* item = new TreeNode(trees[mid], l, r);
		return item;
	}
private:
	int trees[10005];//存储 升序序列,具体大小按照考试当天来
	int size = 0;//记录大小
};

例题

力扣1382. 将二叉搜索树变平衡(题解在力扣里,自行寻找即可)

4、利用“前序中序后序”来构造二叉树问题

在这里插入图片描述
!!!! 考试时候划分不明白,自己找个例子具体确定一下!!!!

前序后序构造二叉树(不唯一,给出一个正确的即可)

1、考试的时候,肯定会给传入,两个数组,以及对应的大小,为private里面的东西
2、注意边界值,用长度当边界值,当长度恰好为0,即一个节点,可以创建返回,当长度小于他,说明不合法,返回空,长度大于他说明还需要继续分治
3、具体理解过程看图:
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
	TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
		for (int i = 0; i < preorder.size(); i++) {//因为考研不让用vector等容器,所以自己转化一下
			this->preorder[size_pre++] = preorder[i];
		}
		for (int i = 0; i < postorder.size(); i++) {
			this->postorder[size_post++] = postorder[i];
		}
		return build(0, 0, size_post - 1);
	}
	//接口解释:
	//int start_pre:左子树(或右子树)preorder序列从何处开始
	//int start_post:左子树(或右子树)postorder序列从何处开始
	//int N:左子树(或右子树)序列长度,从零开始
	//用子树序列长度判断边界值,长度最少为0,一个节点,创建返回即可,小于他,为非法序列返回空
	TreeNode* build(int start_pre, int start_post, int N) {
		if (N < 0) {//因为涉及子树序列长度问题,先处理不合法的,长度小于0即为错,返回空
			return nullptr;
		}
		if (N == 0) {//序列长度为1,说明只有一个节点,直接创建返回即可
			return new TreeNode(preorder[start_pre]);
		}
		//这里的理解看图即可。
		int s = start_pre + 1;
		int length = 0;
		for (; length <= N; length++) {
			if (preorder[s] == postorder[start_post + length]) {
				break;
			}
		}
		//自底向上建树过程
		TreeNode* l = build(s, start_post, length);
		TreeNode* r = build(s + length + 1, start_post + length + 1, N - length - 2);
		return new TreeNode(preorder[start_pre], l, r);
	}
	int preorder[5000];//因为考研不让用vector等容器,所以自己转化一下
	int postorder[5000];
	int size_pre = 0;
	int size_post = 0;
};

例题

力扣:889. 根据前序和后序遍历构造二叉树(题解在力扣里,自行寻找即可)

中序后序创建二叉树(递归法)

考试的时候,肯定会给传入,两个数组,以及对应的大小,为private里面的东西
在这里插入图片描述

在这里插入图片描述

 class Solution {
 public:
	 TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
		 for (int i = 0; i < postorder.size(); i++) {//因为考研不让用vector等容器,所以自己转化一下
			 this->postorder[size_p++] = postorder[i];
		 }
		 for (int i = 0; i < inorder.size(); i++) {
			 this->inorder[size_i++] = inorder[i];
		 }
		 return build(0, size_i - 1, 0, size_p - 1);
	 }
	 TreeNode* build(int in_left, int in_right, int post_left, int post_right) {//主函数
		 if (post_left > post_right) {//边界条件,只能是中序中,左大于右
			 return nullptr;
		}
		int root_pos = -1;//寻找中序中根位置
		for (int i = in_left; i <= in_right; i++) {
			if (postorder[post_right] == inorder[i]) {
				root_pos = i;
				break;
			}
		}
		int right_num = in_right - root_pos;//右子树节点数量
		TreeNode* l = build(in_left, root_pos - 1, post_left, post_right - 1 - right_num);//划分
		TreeNode* r = build(root_pos + 1, in_right, root_pos, post_right - 1);
		return new TreeNode(postorder[post_right], l, r);
	 }
 private:
	 int postorder[5000];//因为考研不让用vector等容器,所以自己转化一下
	 int inorder[5000];
	 int size_p = 0;
	 int size_i = 0;
 };

例题

力扣106. 从中序与后序遍历序列构造二叉树(题解在力扣里,自行寻找即可)

先序中序创建二叉树(递归法)

考试的时候,肯定会给传入,两个数组,以及对应的大小,为private里面的东西

在这里插入图片描述

在这里插入图片描述

 class Solution {
 public:
	 TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
		 for (int i = 0; i < preorder.size(); i++) {//因为考研不让用vector等容器,所以自己转化一下
			 this->preorder[size_p++] = preorder[i];
		 }
		 for (int i = 0; i < inorder.size(); i++) {
			 this->inorder[size_i++] = inorder[i];
		 }
		 return build(0, size_p - 1, 0, size_i - 1);
	 }

	 TreeNode* build(int pre_left, int pre_right, int in_left, int in_right) {//主函数
		 if (in_left > in_right) {//边界条件
			 return nullptr;
		 }
		 int root_pos = -1;//中序中根位置
		 for (int i = in_left; i <= in_right; i++) {
			 if (preorder[pre_left] == inorder[i]) {
				 root_pos = i;
				 break;
			 }
		 }
		 int left_num = root_pos - in_left;//左子树节点数
		 //开始分治
		 TreeNode* l = build(pre_left + 1, root_pos, in_left, root_pos - 1);
		 TreeNode* r = build(pre_left + 1 + left_num, pre_right, root_pos + 1, in_right);
		 return new TreeNode(preorder[pre_left], l, r);//合并
	 }

 private:
	 int preorder[5000];//因为考研不让用vector等容器,所以自己转化一下
	 int inorder[5000];
	 int size_p = 0;
	 int size_i = 0;
 };

例题

力扣105. 从前序与中序遍历序列构造二叉树(题解在力扣里,自行寻找即可)

5、前中后序遍历(迭代+递归)+层次遍历

题目链接

力扣145. 二叉树的后序遍历
力扣144. 二叉树的前序遍历
力扣94. 二叉树的中序遍历
力扣102. 二叉树的层序遍历

前中后序迭代法(统一模板法)

1、用栈实现
2、对于每个节点,第一次出栈为了把左右儿子和自己按照顺序加入栈,第二次出现是为了加入结果集,为了区分这两次出现,在第一次出现结束后,后面加一个"空节点"来标识,如果识别到了空,就把后面对应的节点加入结果集
3、图示如下:
在这里插入图片描述
在这里插入图片描述

后序(超重点)

class Solution {
public:
	vector<int> postorderTraversal(TreeNode* root) {
		vector<int> ans;
		stack<TreeNode*> helping;
		if (root != nullptr) {//边界一定注意好,不空才遍历
			helping.push(root);
		}
		while (helping.size() != 0) {
			TreeNode* item = helping.top();//先获取当前第一个元素
			if (item != nullptr) {//顺序:先弹栈再入栈
				helping.pop();//第一步弹栈
				//按照后序以及栈的规律:根、右、左
				helping.push(item);//加入根
				helping.push(nullptr);
				
				if (item->right != nullptr) {//右
					helping.push(item->right);
				}
				
				if (item->left != nullptr) {//左
					helping.push(item->left);
				}
			}
			else {
				helping.pop();
				TreeNode* tmp = helping.top();
				helping.pop();
				ans.push_back(tmp->val);
			}
		}
		return ans;
	}
};

前序

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		vector<int> ans;
		stack<TreeNode*> helping;
		if (root != nullptr) {//边界条件
			helping.push(root);
		}
		while (helping.size() != 0) {
			TreeNode* item = helping.top();
			if (item != nullptr) {
				helping.pop();//顺序:先弹栈再入栈
				//按照后序以及栈的规律:右、左、根
				if (item->right != nullptr) {
					helping.push(item->right);
				}
				
				if (item->left != nullptr) {
					helping.push(item->left);
				}

				helping.push(item);
				helping.push(nullptr);
			}
			else {
				helping.pop();
				ans.push_back(helping.top()->val);
				helping.pop();
			}
		}
		return ans;
	}
};          

前序补充方法(推荐)

1、因为前序遍历,第一个处理的就是根,所以,栈里面就只需要处理子节点就行,所以说,用栈类模拟队列的用法,先将右孩子入栈然后再将左孩子入栈,这样访问时就是前序的顺序。

2、这种方法也可以应用于N叉树的前序遍历,一定要记住,这种思路无法应用于非递归的中序和后序遍历。


class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        if (root == nullptr) return {};
        vector<int> ans;
        stack<TreeNode*> sta;
        sta.push(root);
        while (!sta.empty())
        {
            TreeNode* cur = sta.top();
            sta.pop();
            ans.push_back(cur->val);
            if (cur->right) sta.push(cur->right);
            if (cur->left) sta.push(cur->left);
        }
        return ans;
    }

中序

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
		vector<int> ans;
		stack<TreeNode*> help;
		if (root != nullptr) {//边界一定注意好,不空才遍历
			help.push(root);
		}
		while (help.size() > 0) {
			TreeNode* item = help.top();
			if (item != nullptr) {
				help.pop();//先弹再入
				//按照后序以及栈的规律:右、根、左
				if (item->right != nullptr) {
					help.push(item->right);
				}

				help.push(item);//根
				help.push(nullptr);

				if (item->left != nullptr) {
					help.push(item->left);
				}
			}
			else {
				help.pop();
				ans.push_back(help.top()->val);
				help.pop();
			}
		}
		return ans;
	}
};

前中后序递归法

后序

class Solution {
public:
	vector<int> postorderTraversal(TreeNode* root) {
		postOrder(root);
		return ans;
	}
	void postOrder(TreeNode* root) {
		if (root == nullptr) {
			return;
		}
		postOrder(root->left);
		postOrder(root->right);
		ans.push_back(root->val);
		return;
	}
private:
	vector<int> ans;
};


前序

class Solution {
public:
	vector<int> preorderTraversal(TreeNode* root) {
		preOrder(root);
		return ans;
	}
	void preOrder(TreeNode* root) {
		if (root == nullptr) {
			return;
		}
		ans.push_back(root->val);
		preOrder(root->left);
		preOrder(root->right);
		return;
	}
private:
	vector<int> ans;
};


中序

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
		inOrder(root);
		return ans;
	}
	void inOrder(TreeNode* root) {
		if (root == nullptr) {
			return;
		}
		inOrder(root->left);
		ans.push_back(root->val);
		inOrder(root->right);
		return;
	}
private:
	vector<int> ans;
};


层次遍历

分两种:
<1>一种就是单纯的获得层次遍历序列,用队列就行,每出一次,(如果有的话)加入两个子节点,一直到空为止
<2>第二种是要求分开打印每一层的节点,这样的话,每次出一层的节点,进入一层的节点,然后记录每层节点数。
本体为第二种

class Solution {
public:
	vector<vector<int>> levelOrder(TreeNode* root) {

		vector<vector<int>> ans;
		queue<TreeNode*> level_order;//用队列
		if (root != nullptr) {
			level_order.push(root);//一定注意边界值,空树不操作
		}
		int levelNum = 1;//每层节点数,初始为1
		while (level_order.size() != 0) {
			int tmpLevelNum = 0;//记录下一层节点数
			vector<int> tmp;//记录该层节点信息
			for (int i = 0; i < levelNum; i++) {//一次出一层,一次进一层
				TreeNode* item = level_order.front();
				level_order.pop();
				tmp.push_back(item->val);
				if (item->left != nullptr) {
					level_order.push(item->left);
					tmpLevelNum++;
				}
				if (item->right != nullptr) {
					level_order.push(item->right);
					tmpLevelNum++;
				}
			}
			levelNum = tmpLevelNum;
			ans.push_back(tmp);
		}
		return ans;
	}
};

6、公共祖先问题(一次遍历法)

普通二叉树

class Solution {
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		if (root == q || root == p|| root == nullptr) {//出边界或者找到目标都是截止
			return root;
		}
		TreeNode* left = lowestCommonAncestor(root->left, p, q);
		TreeNode* right = lowestCommonAncestor(root->right, p, q);
		if (left == nullptr&&right == nullptr) {//两边都空,说明目标不在这一支上
			return nullptr;
		}
		else if (left == nullptr || right == nullptr) {//有一边不空,说明他是目标的祖先,但不是最近的,只需要最近的祖先返回上去就行
			return left == nullptr ? right : left;
		}
		return root;//左右都发现了,说明是最近的祖先
	}
};

例题

剑指 Offer 68 - II. 二叉树的最近公共祖先

二叉搜索树

1、因为二叉排序树自身特点,很多无意义节点不用找,所以不存在的情况不予以考虑,是有针对性的寻找。
2、这个是针对性的找,上面那个是回溯寻找

class Solution {
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		TreeNode* ans = root;//初始化
		while (true) {
			if (ans->val > p->val&&ans->val > q->val) {//目标都在左面,答案也在左
				ans = ans->left;
			}
			else if (ans->val < p->val&&ans->val < q->val) {//目标都在左面,答案也在左
				ans = ans->right;
			}
			else {//左右都有即为答案
				break;
			}
		}
		return ans;
	}
};

7、完全二叉树的判定

1、完全二叉树性质:除了最后一层以外完全填充,并且最后一层的节点都尽可能的出现在左侧。只有倒数第二层(如果存在)若干节点以及最后一层全部节点才可继续添加子节点,其余全部已经是两个子节点。
2、所以,只要是完全二叉树,所有节点必然是一个挨着一个的排布的,所以从上到下从左到右只要碰到了空节点,说明完全二叉树已经走完了,后面如果再碰到非空节点,说明不是完全二叉树。

#include <iostream>
#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
bool function_seven(TreeNode* root) {
	if (root == nullptr) {//特殊情况,空树是完全二叉树
		return true;
	}
	queue<TreeNode*> help;
	help.push(root);//开始层序遍历
	while (!help.empty()) {
		TreeNode* tmp = help.front();
		help.pop();
		if (tmp == nullptr) {//遇到空节点,说明完全二叉树应该已经全都遍历完了
			break;
		}
		help.push(tmp->left);//否则就是入队
		help.push(tmp->right);
	}
	while (!help.empty()) {
		TreeNode* tmp = help.front();
		help.pop();
		if (tmp != nullptr) {//再出现非空节点必错
			return false;
		}
	}
	return true;
}

8、无向图判定是否是一棵树

总思路:

给一个图,n个点(默认点的编号 0 -> n-1),让判定是否是树:遍历一遍图,统计走过了point个节点,edge条边 -> {point==n(防止出现森林)&& point == edge+1}
就是一棵树

统计策略:
1、遍历图:利用辅助数组visit每次只走没走过的
2、统计point:每走到了一个新的节点,就意味着走到了一个新的点 point++
3、统计edge:为了防止丢边,所以只要是到一个节点,我们把与他相连接的所有边都统计上,这样的话每条边统计了两次,只要除以2就是真正的edge

代码

#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
//参数:图、记录访问情况的数组、当前走到哪一个节点、点数、边数(为了把数值带出去,所以传入指针,每次++需要访问到对应地址才可以)
void judge(vector<vector<int>> graph, bool visit[], int p, int *point, int *edge)  {
	visit[p] = true;//新走到一个节点,改一下访问状态
	(*point)++;//遇到的点数++
	for (int e : graph[p]) {//访问它所能到的所有目标点
		(*edge)++;//重点:!!!!!为了不让统计的边少了,对于每个点而言,统计它所有的边(所以造成了一条边被统计两次)
		if (!visit[e]) {//继续走那些没走到过的点
			judge(graph, visit, e, point, edge);
		}
	}
	return;
}
void function_six_one() {//共n个顶点,m对边的关系
	//建图,节点编号默认为 0 -> n-1
	int n, m;//共n个顶点,m对边的关系
	cin >> n >> m;

	vector<vector<int>> graph;//存储图的结构
	graph.resize(n);
	for (int i = 0; i < m; i++) {//根据关系存储图
		int s = 0, e = 0;
		cin >> s >> e;
		graph[s].push_back(e);
		graph[e].push_back(s);
	}

	bool visit[100];//用于辅助遍历,防止无限次遍历
	for (int i = 0; i < n; i++) {
		visit[i] = false;//最开始都默认未访问过
	}

	//建图结束,开始判定

	//用于统计我一次遍历访问了多少个节点,此时我们是不知道图具体什么样的,所以都默认是0
	int point = 0;
	int edge = 0;

	judge(graph, visit, 0, &point, &edge);//遍历

	edge /= 2;//因为我的原则是见到边就算,且是无向图同一条边会在起点和终点个算一次,所以/2才是真正的边数
	if (point == n && edge + 1 == point) {//点数==总点数(防止出现森林) && 边数+1==点数 
		cout << "1";//无向图是树
	}
	else {
		cout << "0";
	}
	return;
}

int main()
{
	function_six_one();

	return 0;

}

9、补充

1、判断二叉树是否对称

经典习题:力扣101. 对称二叉树

在这里插入图片描述

思路

如图所示:
在这里插入图片描述
1、因为,判断是否是轴对称,必须两边一起看,只看一边的情况i下根本无从得知另一边的情况
2、拿来两个根节点(A、B),由上图所示,他们
<1>A的左子树和B的右子树一样
<2>A的右子树和B的左子树一样
<3>A和B本身的值也一样
3、三者都对,返回true,否则返回false
4、边界处理:同时走到空,为true,反之为false

代码

class Solution {
public:
	bool isSymmetric(TreeNode* root) {
		return dfs(root->left, root->right);
	}
	bool dfs(TreeNode *r1, TreeNode* r2) {
		if (r1 == nullptr&&r2 == nullptr) {
			return true;
		}
		else if (r1 == nullptr || r2 == nullptr) {
			return false;
		}
		return dfs(r1->left, r2->right) && dfs(r1->right, r2->left) && (r1->val == r2->val);
	}
};

2、二叉搜索树变形问题

二叉树节点变化

多了一个count,用来记录子树中节点个数

struct TreeNode {
	int val;
	int count;//以该节点为根的子树当中节点个数
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

问题

在O(logn)内,查找二叉搜索树中第k(1<=k<=n)小节点

思路&&代码

(我不太会,直接背下来,一共处理六种情况即可)
在这里插入图片描述

六大情况:
1、判断k是否合法
2、左子树是空且k=1:root为目标答案,返回
3、左子树是空且k>1:往右走且用k-1
4、左子树不是空且左子树的count=k-1:root为目标答案,返回
5、左子树不是空且左子树的count>k-1:往左走且用k
6、左子树不是空且左子树的count<k-1:往右走且用k-1-左子树的count

#include <iostream>
#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
	int val;
	int count;//以该节点为根的子树当中节点个数
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(nullptr), right(nullptr) {}
};

//2021计学1(直接背下来就好,对二叉搜索树的节点改动了一下)
//把原有的二叉搜索树节点进行改动,加一个count记录子树节点个数
//logn内查找第k(1<=k<=n)小节点
//共六种情况
TreeNode* Find(TreeNode* root, int k) {
	if (k<1 || k>root->count) {//第一种,对k先判断
		return nullptr;
	}
	if (root->left == nullptr) {//第二类:左为空
		if (k == 1) {//第二种情况,找到目标(左为空且k==1)
			return root;
		}
		else {//第三种情况,左为空,还需要继续右走,K-1
			return Find(root->right, k - 1);
		}
	}
	else {//第三类情况,左面不是空,找他的count和k-1比较
		if (root->left->count == k - 1) {
			return root;
		}
		else if (root->left->count > k - 1) {
			return Find(root->left, k);
		}
		else {
			return Find(root->right, k - root->left->count - 1);
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLU_LYM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值