《代码随想录》二叉树总结
- 题目:二叉树的递归遍历
- 题目:二叉树的迭代遍历
- 题目:二叉树的层序遍历
- 题目:翻转二叉树
- 题目:对称二叉树
- 题目:二叉树的最大深度
- 题目:二叉树的最小深度
- 题目:完全二叉树的节点个数
- 题目:平衡二叉树
- 题目:二叉树的所有路径
- 题目:求相同的树
- 题目:左叶子之和
- 题目:找树左下角的值
- 题目:路径之和
- 题目:路径之和II
- 题目:中序和后序数组构造二叉树
- 题目:中序和前序数组构造二叉树
- 题目:最大二叉树
- 题目:合并二叉树
- 题目:二叉搜索树中的搜索
- 题目:验证二叉搜索树
- 题目:二叉搜索树的最小绝对差
- 题目:二叉搜索树中的众数
- 题目:二叉树的最近公共祖先
- 题目:二叉搜索树的最近公共祖先
- 题目:二叉搜索树中的插入操作
- 总结:二叉树的遍历方法
- 总结:对两个树来做操作
- 总结:求深度问题
- 总结:求路径问题
- 总结:构造二叉树
- 总结:二叉搜索树
- 总结:二叉搜索树的遍历方法
- 总结:求众数问题,或求出现频率最高的前K个数
- 总结:最近公共祖先问题
题目:二叉树的递归遍历
前序遍历
class Solution {
public:
vector<int> result;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
result.push_back(root->val);
traverse(root->left);
traverse(root->right);
}
}
vector<int> preorderTraversal(TreeNode* root) {
traverse(root);
return result;
}
};
中序遍历
class Solution {
public:
vector<int> result;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
result.push_back(root->val);
traverse(root->right);
}
}
vector<int> inorderTraversal(TreeNode* root) {
traverse(root);
return result;
}
};
后序遍历
class Solution {
public:
vector<int> result;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
traverse(root->right);
result.push_back(root->val);
}
}
vector<int> postorderTraversal(TreeNode* root) {
traverse(root);
return result;
}
};
题目:二叉树的迭代遍历
前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root == NULL) return result;//如果是空,那就直接返回,下面也针对左右子树做了相应的判断,确保放入栈中的结点都不是空
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
return result;
}
};
中序遍历
中序遍历不需要像前序遍历一样对每个结点都单独的去判断是不是空,但其实是在循环里面判断的,即cur != NULL
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;//因为传入的是根结点,但要首先操作的是左子树,因此需要用一个结点来记录一下
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
result.push_back(cur->val);
cur = cur->right;
}
}
return result;
}
};
后序遍历
class Solution {
//后序遍历没有更好的方法,只有先前序遍历,但是是 右左中的顺序,然后对result进行反转
public:
void reverse(vector<int>& nums, int left, int right) {
while (left < right) {
swap(nums[left], nums[right]);
left++;
right--;
}
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left);
if (node->right) st.push(node->right);
}
//reverse(result.begin(), result.end());
reverse(result, 0, result.size() - 1);
return result;
}
};
题目:二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
if (root == NULL) return result;
que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> res;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
res.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(res);
}
return result;
}
};
题目:翻转二叉树
前序 递归
class Solution {
public:
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
}
}
TreeNode* invertTree(TreeNode* root) {
//翻转二叉树,其实就是左右子树翻转,只需要遍历每个结点,然后交换左右子树就行
traverse(root);
return root;
}
};
中序 递归
最后该遍历右子树的时候,因为交换了,所以还是左子树
class Solution {
public:
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
invertTree(root->left);
swap(root->left, root->right);
invertTree(root->left);
}
}
TreeNode* invertTree(TreeNode* root) {
//翻转二叉树,其实就是左右子树翻转,只需要遍历每个结点,然后交换左右子树就行
traverse(root);
return root;
}
};
后序 递归
class Solution {
public:
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
invertTree(root->left);
invertTree(root->right);
swap(root->left, root->right);
}
}
TreeNode* invertTree(TreeNode* root) {
//翻转二叉树,其实就是左右子树翻转,只需要遍历每个结点,然后交换左右子树就行
traverse(root);
return root;
}
};
前序 迭代
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return root;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
swap(node->left, node->right);
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
return root;
}
};
中序 迭代
注意最后那里cur = cur->left;
,而不是cur = cur->right;
,因为之前已经交换了嘛
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
swap(cur->left, cur->right);
cur = cur->left;
}
}
return root;
}
};
后序 迭代
和前序一样
层序 迭代
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
if (root == NULL) return root;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
swap(node->left, node->right);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return root;
}
};
题目:对称二叉树
递归
class Solution {
public:
//两棵树的比较,其实就是分别判断两个结点的四种情况,然后在如果两个结点都不为空的情况下去看相等不相等
bool traverse(TreeNode* root1, TreeNode* root2) {
if (root1 == NULL && root2 == NULL) {
return true;
} else if (root1 == NULL && root2 != NULL) {
return false;
} else if (root1 != NULL && root2 == NULL) {
return false;
} else if (root1 != NULL && root2 != NULL) {
//其实这个if else才是递归的if else,上面的所有的都是为了分情况解决特殊情况,即核心逻辑为,如果两个值不相等,那就false,如果相等,那就去递归
if (root1->val != root2->val) {
return false;
} else {
bool outside = traverse(root1->left, root2->right);
bool inside = traverse(root1->right, root2->left);
return outside && inside;
}
}
return true;
}
bool isSymmetric(TreeNode* root) {
return traverse(root->left, root->right);
}
};
迭代 用栈实现
每次放两个,仍然进行一样的比较,需要注意,如果两个结点为空,不是true了,而是continue,表示进行下面的比较
class Solution {
public:
bool isSymmetric(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
st.push(root->left);
st.push(root->right);
while (!st.empty()) {
TreeNode* root1 = st.top();
st.pop();
TreeNode* root2 = st.top();
st.pop();
if (root1 == NULL && root == NULL) {
continue;
} else if (root1 != NULL && root2 == NULL) {
return false;
} else if (root1 == NULL && root2 != NULL) {
return false;
} else if (root1 != NULL && root2 != NULL) {
if (root1->val != root2->val) {
return false;
} else {
st.push(root1->left);
st.push(root2->right);
st.push(root1->right);
st.push(root2->left);
}
}
}
return true;
}
};
迭代 用队列实现
class Solution {
public:
bool isSymmetric(TreeNode* root) {
queue<TreeNode*> que;
if (root == NULL) return true;
que.push(root->left);
que.push(root->right);
while (!que.empty()) {
TreeNode* root1 = que.front();
que.pop();
TreeNode* root2 = que.front();
que.pop();
if (root1 == NULL && root == NULL) {
continue;
} else if (root1 != NULL && root2 == NULL) {
return false;
} else if (root1 == NULL && root2 != NULL) {
return false;
} else if (root1 != NULL && root2 != NULL) {
if (root1->val != root2->val) {
return false;
} else {
que.push(root1->left);
que.push(root2->right);
que.push(root1->right);
que.push(root2->left);
}
}
}
return true;
}
};
题目:二叉树的最大深度
思路1:层序遍历
class Solution {
public:
//层序遍历是很直观的方法,几层就是最大深度就是多少,因为问题转变成求二叉树的层数
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
int count = 0;
if (root == NULL) return 0;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
count++;
}
return count;
}
};
思路2:后序遍历 递归
class Solution {
public:
int traverse(TreeNode* root) {
if (root == NULL) {
return 0;
} else {
int leftLength = traverse(root->left);
int rightLength = traverse(root->right);
int depth = max(leftLength, rightLength) + 1;
return depth;
}
}
int maxDepth(TreeNode* root) {
return traverse(root);
}
};
题目:二叉树的最小深度
思路1:层序遍历
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
int count = 0;
if (root == NULL) return 0;
que.push(root);
while (!que.empty()) {
int size = que.size();
count++;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left == NULL && node->right == NULL) return count;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return count;
}
};
思路2:后序遍历 递归
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) {
return 0;
} else {
int leftLength = minDepth(root->left);
int rightLength = minDepth(root->right);
int depth;
//下面这两个if完全是为了解决根节点没有某一个孩子的时候,会出现最小深度为1的情况,定义最小深度是到叶子节点,也就是根节点这个不能作数
if (root->left == NULL && root->right != NULL) return rightLength + 1;
if (root->left != NULL && root->right == NULL) return leftLength + 1;
depth = min(leftLength, rightLength) + 1;
return depth;
}
}
};
题目:完全二叉树的节点个数
我的解法就是单纯的数数,没有用到完全二叉树的性质,所以不写这个了,单纯的数数其实就是遍历,那些遍历方法其实都行
题目:平衡二叉树
class Solution {
public:
int getHeight(TreeNode* root) {
if (root == NULL) {
return 0;
} else {//仍然是后序遍历,只是说我如果发现已经不是平衡二叉树了,那就直接返回-1就行
int leftHeight = getHeight(root->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(root->right);
if (rightHeight == -1) return -1;
if (abs(leftHeight - rightHeight) > 1) {
return -1;
} else {
return max(leftHeight, rightHeight) + 1;
}
}
}
bool isBalanced(TreeNode* root) {
if (getHeight(root) == -1) {
return false;
} else {
return true;
}
}
};
题目:二叉树的所有路径
迭代 前序遍历
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
//定义两个栈,一个用来存放每个经历的结点,一个用来存在从根节点到目前node的路径,一旦到了叶子结点,那就放入result中,完全的前序遍历
stack<TreeNode*> st;
stack<string> stpath;
vector<string> result;
if (root == NULL) return result;
st.push(root);
stpath.push(to_string(root->val));
while (!st.empty()) {
TreeNode* node = st.top();
string nodePath = stpath.top();
st.pop();
stpath.pop();
if (node->left == NULL && node->right == NULL) result.push_back(nodePath);
if (node->right) {
st.push(node->right);
stpath.push(nodePath + "->" + to_string(node->right->val));
}
if (node->left) {
st.push(node->left);
stpath.push(nodePath + "->" + to_string(node->left->val));
}
}
return result;
}
};
递归 前序遍历
class Solution {
public:
vector<string> result;
void traverse(TreeNode* root, string path) {
if (root == NULL) {
return ;
} else {
//判断如果是叶子结点,那就放入result中
if (root->left == NULL && root->right == NULL) {
result.push_back(path);
}
//其实下面的这两个if本来是不需要的,但是因为这里有->val,如果是空的话这里就会报错
if (root->left) {
traverse(root->left, path + "->" + to_string(root->left->val));
}
if (root->right) {
traverse(root->right, path + "->" + to_string(root->right->val));
}
}
}
vector<string> binaryTreePaths(TreeNode* root) {
traverse(root, to_string(root->val));
return result;
}
};
题目:求相同的树
这个完全可以靠《总结:对两个树来做操作》的方法解决
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p == NULL && q == NULL) return true;
else if (p != NULL && q == NULL) return false;
else if (p == NULL && q != NULL) return false;
else if (p != NULL && q != NULL) {
if (p->val != q->val) {
return false;
} else {
bool leftSame = isSameTree(p->left, q->left);
bool rightSame = isSameTree(p->right, q->right);
return leftSame && rightSame;
}
}
return true;
}
};
题目:左叶子之和
利用标志位的方法,这其实是借鉴了《求路径问题》的思路
class Solution {
public:
//遍历每个结点,标记这是左结点还是右节点,那其实就是再建立一个和遍历的数据结构一样的数据结构,然后去遍历,这个数据结构相当于是个标志位,0是左,1是右
int sum = 0;
void traverse(TreeNode* root, int leftOrRight) {
if (root == NULL) {
return ;
} else {
//if (root->left != NULL && root->left->left == NULL &&)
if (root->left == NULL && root->right == NULL && leftOrRight == 0) {//左右孩子都是NULL,并且标志位是0
sum = sum + root->val;
}
traverse(root->left, 0);
traverse(root->right, 1);
}
}
int sumOfLeftLeaves(TreeNode* root) {
traverse(root, 1);
return sum;
}
};
纯粹的遍历的思路
关键在于这里找左叶子的思路不同了
if (root->left != NULL && root->left->left == NULL && root->left->right == NULL) {
sum = sum + root->left->val;
}
class Solution {
public:
int sum = 0;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
if (root->left != NULL && root->left->left == NULL && root->left->right == NULL) {
sum = sum + root->left->val;
}
traverse(root->left);
traverse(root->right);
}
}
int sumOfLeftLeaves(TreeNode* root) {
traverse(root);
return sum;
}
};
题目:找树左下角的值
直接利用层序遍历就可以,只不过是先放入右孩子,再放入左孩子
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
int result;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
result = node->val;
if (node->right) que.push(node->right);
if (node->left) que.push(node->left);
}
}
return result;
}
};
题目:路径之和
仍然使用求路径问题的通用方法,即使用辅助数据结构
迭代 前序遍历
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
stack<TreeNode*> st;
stack<int> sumSt;
if (root == NULL) return false;
st.push(root);
sumSt.push(root->val);
while (!st.empty()) {
TreeNode* node = st.top();
int sum = sumSt.top();
st.pop();
sumSt.pop();
if (node->left == NULL && node->right == NULL && sum == targetSum) {
return true;
}
if (node->left) {
st.push(node->left);
sumSt.push(sum + node->left->val);
}
if (node->right) {
st.push(node->right);
sumSt.push(sum + node->right->val);
}
}
return false;
}
};
递归 前序遍历
class Solution {
public:
//仍然是路径问题,那我仍然用那一招,添加相同的辅助的数据结构
bool traverse(TreeNode* root, int sum, int targetSum) {
if (root == NULL) {
;
} else {
if (root->left == NULL && root->right == NULL && sum == targetSum) {
return true;
}
bool leftBool = false;
if (root->left) {
leftBool = traverse(root->left, sum + root->left->val, targetSum);
}
bool rightBool = false;
if (root->right) {
rightBool = traverse(root->right, sum + root->right->val, targetSum);
}
return leftBool || rightBool;
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return 0;
else return traverse(root, root->val, targetSum);
}
};
但是会发现其实我根本进入不了root == NULL
的情况,因此也可以简化一下递归的if,但是就看不出来使用的递归的标准套路了,如下:
class Solution {
public:
//仍然是路径问题,那我仍然用那一招,添加相同的辅助的数据结构
bool traverse(TreeNode* root, int sum, int targetSum) {
if (root->left == NULL && root->right == NULL && sum == targetSum) {
return true;
}
bool leftBool = false;
if (root->left) {
leftBool = traverse(root->left, sum + root->left->val, targetSum);
}
bool rightBool = false;
if (root->right) {
rightBool = traverse(root->right, sum + root->right->val, targetSum);
}
return leftBool || rightBool;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return 0;
else return traverse(root, root->val, targetSum);
}
};
题目:路径之和II
仍然是用搭建辅助数据结构的方法
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> result;
stack<TreeNode*> treeSt;
stack<vector<int>> pathSt;
stack<int> sumSt;
if (root == NULL) return result;
treeSt.push(root);
pathSt.push({root->val});
sumSt.push(root->val);
while (!treeSt.empty()) {
TreeNode* node = treeSt.top();
vector<int> res = pathSt.top();
int sumNode = sumSt.top();
treeSt.pop();
pathSt.pop();
sumSt.pop();
if (node->left == NULL && node->right == NULL && sumNode == targetSum) {
result.push_back(res);
}
if (node->left) {
treeSt.push(node->left);
sumSt.push(sumNode + node->left->val);
res.push_back(node->left->val);
vector<int> resL = res;
res.pop_back();
pathSt.push(resL);
}
if (node->right) {
treeSt.push(node->right);
sumSt.push(sumNode + node->right->val);
res.push_back(node->right->val);
vector<int> resR = res;
res.pop_back();
pathSt.push(resR);
}
}
return result;
}
};
题目:中序和后序数组构造二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return NULL;//如果元素个数为0,那肯定是个空结点
TreeNode* root = new TreeNode(postorder[postorder.size() - 1]);//运行到这步,说明肯定不是0,那就是肯定有树了,那先把根结点找到
if (postorder.size() == 1) return root;//如果后序的数组只有一个根,那也不用往下执行了,直接返回就行了
//找到根结点的元素在中序遍历中的位置
int i;
for (i = 0; i < inorder.size(); i++) {
if (inorder[i] == root->val) {
break;
}
}
//知道分割点了,分割中序数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + i);
vector<int> rightInorder(inorder.begin() + i + 1, inorder.end());
//接着需要分割后序数组,那就首先需要知道分割点,分割点就是左中序数组的大小,因为数组大小肯定是相同的
int size = leftInorder.size();
//分割后序数组
vector<int> leftPostOrder(postorder.begin(), postorder.begin() + size);
vector<int> rightPostOrder(postorder.begin() + size, postorder.end() - 1);
//递归
root->left = buildTree(leftInorder, leftPostOrder);
root->right = buildTree(rightInorder, rightPostOrder);
return root;
}
};
题目:中序和前序数组构造二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0) return NULL;
TreeNode* root = new TreeNode(preorder[0]);
if (preorder.size() == 1) return root;
int i;
for (i = 0; i < inorder.size(); i++) {
if (inorder[i] == root->val) {
break;
}
}
//分割中序数组
vector<int> leftInOrder(inorder.begin(), inorder.begin() + i);
vector<int> rightInOrder(inorder.begin() + i + 1, inorder.end());
//找到后序的分割点
int size = leftInOrder.size();
//分割前序数组
vector<int> leftPreOrder(preorder.begin() + 1, preorder.begin() + 1 + size);
vector<int> rightPreOrder(preorder.begin() + 1 + size, preorder.end());
//递归
root->left = buildTree(leftPreOrder, leftInOrder);
root->right = buildTree(rightPreOrder, rightInOrder);
return root;
}
};
题目:最大二叉树
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
//如果数组大小为0,那肯定这棵树是个NULL
if (nums.size() == 0) return NULL;
//接下来就要构造根结点,但就要找最大值,同时记录下来分割点,方便再下一步的分割
int max = nums[0];
int index = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > max) {
max = nums[i];
index = i;
}
}
//构造根结点
TreeNode* root = new TreeNode(max);
//如果数组大小为1,那也就只有根了,直接返回就可以
if (nums.size() == 1) return root;
//现在开始分割
vector<int> leftnums(nums.begin(), nums.begin() + index);
vector<int> rightnums(nums.begin() + index + 1, nums.end());
//递归
root->left = constructMaximumBinaryTree(leftnums);
root->right = constructMaximumBinaryTree(rightnums);
return root;
}
};
题目:合并二叉树
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
TreeNode* root;
if (root1 == NULL && root2 == NULL) {
return NULL;
} else if (root1 != NULL && root2 == NULL){
return root1;
} else if (root1 == NULL && root2 != NULL){
return root2;
} else if (root1 != NULL && root2 != NULL){
root = new TreeNode(root1->val + root2->val);
root->left = mergeTrees(root1->left, root2->left);
root->right = mergeTrees(root1->right, root2->right);
}
return root;
}
};
题目:二叉搜索树中的搜索
递归
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL || root->val == val) return root;
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return NULL;
}
};
下面是用标准的递归的格式写的
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
//依然严格按照递归的形式走,但是左右孩子遍历的时候,用一下形式,会快很多
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL) {
return NULL;
} else {
if (root->val == val) return root;
else if (root->val > val) return searchBST(root->left, val);
else if (root->val < val) return searchBST(root->right, val);
}
return NULL;
}
};
return NULL;
}
};
迭代
可以看出来和递归是很像的
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root != NULL) {
if (root->val == val) return root;
else if (root->val > val) root = root->left;
else root = root->right;
}
return NULL;
}
};
题目:验证二叉搜索树
最朴素,直接中序遍历出来变成一个数组,对数组操作
class Solution {
public:
vector<int> result;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
result.push_back(root->val);
traverse(root->right);
}
}
//这上面的部分完全就是遍历二叉树的操作
bool isValidBST(TreeNode* root) {
traverse(root);
//其实就是相邻两个比较
for (int i = 1; i < result.size(); i++) {
if (result[i] <= result[i - 1]) {
return false;
}
}
return true;
}
};
递归
class Solution {
public:
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if (root == NULL) {//是空树的话当然也是二叉搜索树了
return true;
} else {
bool left = isValidBST(root->left);//左
if (pre != NULL) {
if (pre->val >= root->val) {
return false;
}
}
pre = root;
bool right = isValidBST(root->right);//右
return left && right;
}
}
};
迭代
class Solution {
public:
bool isValidBST(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* pre = NULL;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
//和递归一样,也是加了这几句
if (pre != NULL) {
if (pre->val >= cur->val) {
return false;
}
}
pre = root;
cur = cur->right;
}
}
return true;
}
};
题目:二叉搜索树的最小绝对差
最朴素,直接中序遍历出来变成一个数组,对数组操作
class Solution {
public:
//先遍历出来吧还是
vector<int> result;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
result.push_back(root->val);
traverse(root->right);
}
}
int getMinimumDifference(TreeNode* root) {
traverse(root);
//其实就是看数组相邻两个数的最小差值,就很简单了
int mini = INT_MAX;
for (int i = 1; i < result.size(); i++) {
int sub = result[i] - result[i - 1];
if (sub < mini) {
mini = sub;
}
}
return mini;
}
};
递归
class Solution {
public:
int mini = INT_MAX;
TreeNode* pre = NULL;
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
if (pre != NULL) {
int sub = root->val - pre->val;
if (sub < mini) {
mini = sub;
}
}
pre = root;
traverse(root->right);
}
}
int getMinimumDifference(TreeNode* root) {
traverse(root);
return mini;
}
};
迭代
题目:二叉搜索树中的众数
递归
class Solution {
public:
int count = 0;//目前检索的这个值已经出现了几次
int maxCount = 0;//从开始到现在出现的最多的次数
vector<int> result;//存放众数的数组,因为众数不一定只有一个,所以是个数组
TreeNode* pre = NULL;//为了中序遍历而定义的pre
void traverse(TreeNode* root) {
if (root == NULL) {
return ;
} else {
traverse(root->left);
if (pre == NULL) {//第一个结点
count = 1;
} else if (pre->val == root->val) {//遇到重复的结点
count++;
} else { //又不是第一个,又不是重复了,那就是遇见新的数字了
count = 1;
}
if (maxCount == count) {//当有一个数的出现次数已经到了和最大次数都一样多的了,那肯定他俩都是众数
result.push_back(root->val);
}
if (maxCount < count) {//这个数出现的次数已经超过 最大次数了,那就要替换掉最大次数,同时这个数才是新的众数
result.clear();
maxCount = count;
result.push_back(root->val);
}
pre = root;
traverse(root->right);
}
}
vector<int> findMode(TreeNode* root) {
//既然是二叉搜索树,那中序遍历就是有序的,遍历出来就是111222223333这种样子,那就是找出现频率最高的,既然是有序的,即相同数字的都爱着,那就不需要用unordered_map这种哈希表了,就直接读就行,然后也是以“打擂台”的方式选出现最多的,这种打擂台不是比较的数字的大小,而是比较的数字出现的多少次数
traverse(root);
return result;
}
};
迭代
class Solution {
public:
vector<int> findMode(TreeNode* root) {
stack<TreeNode*> st;
int maxCount = 0;
int count = 0;
vector<int> result;
TreeNode* pre = NULL;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
if (pre == NULL) {
count = 1;
} else if (pre->val == cur->val) {
count++;
} else {
count = 1;
}
if (count == maxCount) {
result.push_back(cur->val);
}
if (count > maxCount) {
result.clear();
maxCount = count;
result.push_back(cur->val);
}
pre = cur;
cur = cur->right;
}
}
return result;
}
};
题目:二叉树的最近公共祖先
递归
class Solution {
public:
//关键的点
//1. 考虑用回溯,其实就是后序遍历,为什么能想到用后序遍历呢?因为是要从下往上看,所以是后序遍历
//2. 返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == NULL) return root;//递归的if
TreeNode* left = lowestCommonAncestor(root->left, p, q);//左右根,也能用回溯的话来讲就是那个for循环,只不过现在只有两个,没必要用个for了
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;//
else if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
}
};
题目:二叉搜索树的最近公共祖先
递归
class Solution {
public:
//因为左子树所有节点都比他小,右子树所有节点都比他大,所以遍历就有了方向,就不需要像普通二叉树那样把所有的结点都遍历一遍了
//其实只要搜索到root->val在p和q中间的时候,那就是对了,返回就行
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//因为已经说了p q均存在于里面,所以肯定不会遍历到root == NULL的情况,所以其实递归的if 是可以不写的,下面的语句严格来说都是在递归的else中的
//接下来只要说搜索到root->val在p和q中间就行,但是我不知道p和q哪个大,所以只能是通过大于他们俩,小于他们俩,其他,这三种情况来判断
if (root->val > p->val && root->val > q->val) {
return lowestCommonAncestor(root->left, p, q);
} else if (root->val < p->val && root->val < q->val) {
return lowestCommonAncestor(root->right, p, q);
} else {
return root;
}
}
};
迭代
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while (root) {//这里只要不是空就行,因为其实不可能为空的,因为p q题目说了一定在里面,那肯定能搜索到,也就是因为不可能为空,所以我才没有写递归时候的if的
if ((root->val > p->val) && (root->val > q->val)) {
root = root->left;
} else if ((root->val < p->val) && (root->val < q->val)) {
root = root->right;
} else {
return root;
}
}
return NULL;
}
};
题目:二叉搜索树中的插入操作
其实仍然是标准的二叉搜索树的遍历操作
递归
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
} else {
if (root->val > val) {
root->left = insertIntoBST(root->left, val);
}
if (root->val < val) {
root->right = insertIntoBST(root->right, val);
}
}
return root;
}
};
迭代
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
root = new TreeNode(val);
return root;
}
//下面才是主要的代码,上面是为了处理当root为空时候的场景
TreeNode* cur = root;
TreeNode* pre = NULL;
while (cur != NULL) {
pre = cur;//这个单纯只是记录一下上一个结点,不然结点没法和树接上去
if (cur->val > val) {
cur = cur->left;
} else if (cur->val < val) {
cur = cur->right;
}
}
cur = new TreeNode(val);
if (pre->val > val) {
pre->left = cur;
} else {
pre->right = cur;
}
return root;
}
};
总结:二叉树的遍历方法
迭代法中,前序6后序和层序遍历都是
- 先放一个
root
进去, - 每次放
root root->left root->right
之前都会判断是不是NULL
中序遍历反而不是
深度优先
前中后序各有一种迭代和一种递归的算法
广度优先
迭代算法
总结:对两个树来做操作
《对称二叉树》《求相同的树》《题目:合并二叉树》的操作,对两棵树先看为空不为空,就有四种情况,如果不为空,进入看看值想不想等,值相等,再去比较下面的结点
这里迭代法中可以用栈也可以用队列实现,原因是这里只是比较,不需要关注是先进后出还是先进先出,所以只要用线性的数据结构就可以了
写法1:递归
写法2:迭代 用栈实现
写法3:迭代 用队列实现
总结:求深度问题
《题目:二叉树的最大深度》《题目:二叉树的最小深度》《题目:平衡二叉树》
思路1:层序遍历,但不普适
关键的思路:层序遍历是很直观的方法,几层就是最大深度就是多少,因为问题转变成求二叉树的层数
思路2:标准思路:考虑后序遍历来递归
我要知道这层的深度,就要知道左右子树的深度,所以我要先遍历左右子树才行,是很自然的思路
深度是左边的深度或者右边的深度加1
如果是求最大深度那就是depth = max(leftLength, rightLength) + 1;
总结:求路径问题
《题目:二叉树的所有路径》《题目:求路径之和》
通用思路:添加相同的辅助数据结构
求路径问题虽然有迭代和递归两种方法,但本质上仍然是同一种方法,其实迭代也是模拟的递归的栈
我的关键方法就是用个栈来模拟从根节点到目前的路径,哪个到了根节点,哪个就放进去vector
中
但是后来可以发现其实也可以用队列来说,因为对这个问题来说先进先出还是先进后出还是没影响,只是看路径
总结:构造二叉树
其实就是很简单的道理,先找到根节点,然后找到分割点,然后分割数组,分割出来的数组去递归,构造二叉树,具体在题目中练习
《题目:中序和后序数组构造二叉树》
《题目:中序和前序数组构造二叉树》
《题目:最大二叉树》
总结:二叉搜索树
性质1:大小关系
二叉搜索树是一个有序树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树
性质2:中序遍历下,输出的二叉搜索树节点的数值是递增。
注意:很多二叉搜索树的题目,如果采用先遍历,然后对数组做操作,那其实就非常容易就解决了,这是最好理解的方法,但是就会有n的空间复杂度,因为你用了数组重新保存了以一下,所以应该注意学习那种用pre和cur的思路,即不读出来操作,而是边遍历边操作,其实思路还是一样,仅仅是边遍历便操作了
关键代码就是这三句
TreeNode* pre = NULL;//这个如果是递归的话要定义成全局的,如果是迭代的话就不用,定义上最上面就可以了
if (pre != NULL) {
//要对pre->val和root->val做的操作
}
pre = root;
如下面是《二叉搜索树的最小绝对差》中用到的代码,就很清晰
TreeNode* pre = NULL;//这个如果是递归的话要定义成全局的,如果是迭代的话就不用,定义上最上面就可以了
if (pre != NULL) {
int sub = root->val - pre->val;
if (sub < mini) {
mini = sub;
}
}
pre = root;
总结:二叉搜索树的遍历方法
见题目《二叉搜索树的搜索》《二叉搜索树的插入操作》
总结:求众数问题,或求出现频率最高的前K个数
普通二叉树(普通数组)
步骤:
- 先遍历一遍数据,用unordered_map来记录出现的频率
- 对unordered_map进行排序,但是这种无法直接进行排序,应该先转化为vector,然后再排序,排好序以后,取前面K个就可以了
注意:可以尝试用优先级队列或者是堆,因为队列题目中的《求前k个高频元素》就是这样的题
二叉搜索树(有序数组)
见题目《二叉搜索树中的众数》
具体来说就是对有序集合进行找众数
一个小点:如果要输出的是集合呢
- 统计最高出现频率元素集合的技巧:
- 要不然就要遍历两次二叉搜索树才能把这个最高出现频率元素的集合求出来。即第一次遍历先打擂台看看出现最多的是几次,然后第二次遍历看看出现最多的次数相等的数是哪些,因为可能不止一个
总结:最近公共祖先问题
普通二叉树
直接去看题目,关键是想到用回溯(后序遍历),然后用
二叉搜索树
二叉搜索树自然是可以用普通二叉树的方法,不过也有二叉搜索树自己的方法,那就是
利用左子树所有结点都比他小,右子树所有节点都比他大,这样子就可以有方向的搜索,反而代码变得非常简单了