自己动手写代码,记录中间出现的错误。
这个网址不错https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E6%A0%91.md
目录
中序遍历!
先序遍历
后序遍历
按层遍历
不分行打印( deque)
分行打印(queue)
Z字遍历(两个栈)
Moris神级遍历
二叉树的层高!
镜像二叉树
是否是对称二叉树?( pRoot1, pRoot2)递归
是否是二叉搜索树的后序数组?
[98] 验证二叉搜索树(BST)
二叉树的左右边界打印
[96] 不同的二叉搜索树
[112] 路径总和
[113] 路径总和 II
树形dp套路
二叉树节点间的最大距离问题!
派对的最大快乐值
最大搜索二叉子树!
是否是平衡二叉树BST
二叉树 最近公共祖先
后继节点(中序遍历)
二叉树 累积和为k的路径(先序遍历做)
二叉树 累积和为k的最长路径(先序遍历做)
重构二叉树
先序+中序
中序+后序
递归遍历是非常重要的,对于二叉树的题目来说,虽然单独让你写中序遍历的时候最好不要用递归,但是其他题目中有用到很多。inorder(Node->left,res);一直递归到最左边的null了,返回,此时压入最左边节点res.push_back(Node->val);然后看看有没有右边的节点,有的话带进去, inorder(Node->right,res);然后又回到了找最左的过程,以此循环。有一个回溯的过程。
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
vector res;// 全局变量放外面,递归
inorder(root,res);
return res;
}
void inorder(TreeNode * Node,vector &res){
if (Node==nullptr) return;// basecase
inorder(Node->left,res);
res.push_back(Node->val);
inorder(Node->right,res);
}
};
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
TreeNode* cur = root;
vector res;
stack helpStack;
while(!helpStack.empty()||cur!=nullptr){//记住这里的条件
if(cur!=nullptr){// while里面if就行
helpStack.push(cur);
cur = cur->left;
}
else{// 如果cur不为空!!
cur = helpStack.top();
helpStack.pop();
res.push_back(cur->val);//注意是->val
cur = cur->right;// 弹出节点的右边!!
}
}
return res;
}
};
cur=stack.top();stack.pop();如果cur->right不为空stack.push_back(cur->right);如果cur->left不为空stack.push_back(cur->left);
利用两个栈的方式
#include
using namespace std;
void PrintFromTopToBottom(BinaryTreeNode* pRoot)
{
if(pRoot == nullptr)
return;
deque dequeTreeNode;
dequeTreeNode.push_back(pRoot);
while(dequeTreeNode.size())//!!!!!!
{
BinaryTreeNode *pNode = dequeTreeNode.front();
dequeTreeNode.pop_front();
printf("%d ", pNode->m_nValue);
if(pNode->m_pLeft)// !!!
dequeTreeNode.push_back(pNode->m_pLeft);
if(pNode->m_pRight)
dequeTreeNode.push_back(pNode->m_pRight);
}
}
void Print2(BinaryTreeNode* pRoot) {
if (pRoot == nullptr)
return;
queueq;
q.push(pRoot);
int len = 0;
while (!q.empty()) {//
len = q.size();// 实时更新len,表示这一层的个数!!
while (len--) {//
BinaryTreeNode *pCur = q.front();
q.pop();
cout << pCur->m_nValue<<" ";
if (pCur->m_pLeft != nullptr) {
q.push(pCur->m_pLeft);
}
if (pCur->m_pRight != nullptr) {
q.push(pCur->m_pRight);
}
}
cout << endl;
}
}
void Print3(BinaryTreeNode* pRoot) {
if (pRoot == nullptr)
return;
stacks1;// 存放奇数层
stacks2;// 存放偶数层
s1.push(pRoot);
int len = 0;
while (!s1.empty()||!s2.empty())
{
while (!s1.empty()){
len = s1.size();
while(len--){
BinaryTreeNode* pCur = s1.top();
s1.pop();
cout << pCur->m_nValue<<" ";
if (pCur->m_pLeft!=nullptr)
s2.push(pCur->m_pLeft);
if (pCur->m_pRight != nullptr)
s2.push(pCur->m_pRight);
}
}
cout << endl;
while (!s2.empty()) {
len = s2.size();
while (len--) {
BinaryTreeNode* pCur = s2.top();
s2.pop();
cout << pCur->m_nValue << " ";
if (pCur->m_pRight != nullptr)
s1.push(pCur->m_pRight);
if (pCur->m_pLeft != nullptr)
s1.push(pCur->m_pLeft);
}
}
cout << endl;
}
}
将原先的二叉树变成镜像二叉树
利用先序遍历的方式,递归,开始写basecase(如果是叶子结点了,直接返回)
主体部分:交换该节点的左右节点,如果有左子节点,递归交换左子节点的左右,如果有右子节点,递归交换右子节点的左右。
void MirrorRecursively(BinaryTreeNode *pNode)
{
if((pNode == nullptr) || (pNode->m_pLeft == nullptr && pNode->m_pRight))
return;
BinaryTreeNode *pTemp = pNode->m_pLeft;
pNode->m_pLeft = pNode->m_pRight;
pNode->m_pRight = pTemp;
if(pNode->m_pLeft)
MirrorRecursively(pNode->m_pLeft);
if(pNode->m_pRight)
MirrorRecursively(pNode->m_pRight);
}
void MirrorIteratively(BinaryTreeNode* pRoot)
{
if(pRoot == nullptr)
return;
std::stack stackTreeNode;
stackTreeNode.push(pRoot);
while(stackTreeNode.size() > 0)
{
BinaryTreeNode *pNode = stackTreeNode.top();
stackTreeNode.pop(); // 根左右,在根处交换左右
BinaryTreeNode *pTemp = pNode->m_pLeft;
pNode->m_pLeft = pNode->m_pRight;
pNode->m_pRight = pTemp;
if(pNode->m_pLeft)
stackTreeNode.push(pNode->m_pLeft);
if(pNode->m_pRight)
stackTreeNode.push(pNode->m_pRight);
}
}
参数是两个(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2);
bool isSymmetrical(BinaryTreeNode* pRoot)
{
return isSymmetrical(pRoot, pRoot);
}
bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
if(pRoot1 == nullptr && pRoot2 == nullptr)//如果都相等
return true;
if(pRoot1 == nullptr || pRoot2 == nullptr)// 判断条件
return false;
if(pRoot1->m_nValue != pRoot2->m_nValue)// 判断条件
return false;
return isSymmetrical(pRoot1->m_pLeft, pRoot2->m_pRight)// 递归
&& isSymmetrical(pRoot1->m_pRight, pRoot2->m_pLeft);
}
递归,参数是(int arr[],length)
后序数组,先找到根节点,然后从开头开始找比根节点小的数,然后在这之后到最后,如果还有比根节点小的数,则返回false。
递归的时候看左右是不是,其实还是用先序遍历的方式~
5 7 6 9 11 10 8
bool VerifySquenceOfBST(int sequence[], int length)
{
if(sequence == nullptr || length <= 0)
return false;
int root = sequence[length - 1]; //更新出根节点value
// 在二叉搜索树中左子树的结点小于根结点
int i = 0;
for(; i < length - 1; ++ i)
{
if(sequence[i] > root)
break;
}
// 在二叉搜索树中右子树的结点大于根结点
int j = i;
for(; j < length - 1; ++ j)
{
if(sequence[j] < root)
return false;
}
// 判断左子树是不是二叉搜索树
bool left = true;
if(i > 0)
left = VerifySquenceOfBST(sequence, i);// 递归,就是数组的范围在变换
// 判断右子树是不是二叉搜索树
bool right = true;
if(i < length - 1)
right = VerifySquenceOfBST(sequence + i, length - i - 1);
return (left && right);//左右是不是
}
/*
*
* 示例 1:
*
* 输入:
* 2
* / \
* 1 3
* 输出: true
*/
class Solution {
double last = LONG_MIN;//temp
public:
bool isValidBST(TreeNode* root) {
if (root == nullptr) {
return true;
}
if (isValidBST(root->left)) {//zuo
if (last < root->val) {//如果左节点满足,
last = root->val;
return isValidBST(root->right);
}
}
return false;
}
};
class Solution {
public:
bool isValidBST(TreeNode* root) {
// 注意 用long的最大最小值
return check(root, LONG_MIN, LONG_MAX);
}
bool check(TreeNode* root, long min, long max){
if (root == NULL)
return true;
if (root->val <= min || root->val >= max)
return false;
return check(root->left, min, root->val) && check(root->right, root->val, max);
}
}
用后序遍历的方式做。
站到头结点,和左树右树,要信息
int getHeight(treeNode * head,int level)
{
//传入头结点,level为0,返回的level,就是层高
if (head == nullptr) return level;
left_height = getHeight(head->left,level+1);//递归的状态,只应用于本次循环,所以每一对leftright都是一对儿。
right_height= getHeight(head->right,level+1);//右树高度
return max(left_height,right_height);
}
int getHeight(treeNode * head)
{
if (head == nullptr) return 0;
left_height = getHeight(head->left);//
right_height= getHeight(head->right);
return max(left_height,right_height)+1;
}
// 非递归,bfs通过队列size得到本层的节点数目
int TreeDepth(TreeNode* pRoot)
{
queue q;
if(!pRoot) return 0;
q.push(pRoot);
int level=0;
while(!q.empty()){
int len=q.size();
level++;
while(len--){
TreeNode* tem=q.front();
q.pop();
if(tem->left) q.push(tem->left);
if(tem->right) q.push(tem->right);
}
}
return level;
}
辅助数据:一个二维数组
内部逻辑:中序遍历,basecase考虑某一节点,if 什么时候是左边界,if 什么时候是右边界,然后遍历左右
凡是和层高有关系的,递归函数的形参里面都有level
#include "stdafx.h"
#include"iostream"
using namespace std;
struct Node {
Node* left;
Node* right;
int data;
Node(int value):data(value), left(NULL), right(NULL){}
};
Node* T;
Node* edgeMap[100 + 5][2];
void setEdgeMap(Node* h, int l)
{
// 根左右,中序遍历,先basecase再判断,根节点的此处的逻辑,然后左右遍历
if (h == NULL) return;
//左边界 如果是第一次遇到就令它等于左边界
if (edgeMap[l][0] == NULL)
edgeMap[l][0] = h;
//右边界 是这一层最后一个遍历到的,所以一直赋予就可以了,到最后就是最右边界
edgeMap[l][1] = h;
setEdgeMap(h->left, l + 1);
setEdgeMap(h->right, l + 1);
}
int main()
{
Node* node1 = new Node(1);
Node*node2 = new Node(2);
Node*node3 = new Node(3);
Node*node4 = new Node(4);
Node*node5 = new Node(5);
Node*node6 = new Node(6);
Node*node7 = new Node(7);
Node*node8 = new Node(8);
Node*node9 = new Node(9);
Node*node10 = new Node(10);
Node*node11 = new Node(11);
Node*node12 = new Node(12);
Node*node13 = new Node(13);
Node*node14 = new Node(14);
Node*node15 = new Node(15);
Node*node16 = new Node(16);
node1->left = node2;
node1->right = node3;
node2->right = node4;
node3->left = node5;
node3->right = node6;
node4->left = node7;
node4->right = node8;
node5->left = node9;
node5->right = node10;
node8->right = node11;
node9->left = node12;
node11->left = node13;
node11->right = node14;
node12->left = node15;
node12->right = node16;
setEdgeMap(node1,0);
for (int i = 0; i < 6; i++)
{
cout << edgeMap[i][0]->data<<" ";
}
cout << endl;
for (int i = 0; i < 6; i++)
{
cout << edgeMap[i][1]->data << " ";
}
cout << endl;
return 0;
}
/* 给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
*
* 1 3 3 2 1
* \ / / / \ \
* 3 2 1 1 3 2
* / / \ \
* 2 1 2 3 */
class Solution {// 总结规律,从遍历的过程。二叉搜索树的中序遍历是递增的。
public:
int numTrees(int n) {
int f[n+1]={0};//注意一定要初始化!!!
f[0]=1;
for(int i=1;i
tricks:
/*
* 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
*
* 5
* / \
* 4 8
* / / \
* 11 13 4
* / \ \
* 7 2 1
*
*
* 返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
*
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == nullptr){
return false;
}
if(root->left==nullptr&&root->right==nullptr&&root->val==sum)
return true;
if (hasPathSum(root->left, sum-root->val))
return true;
if (hasPathSum(root->right, sum-root->val))
return true;
return false;
}
};
是指在中序遍历中紧随其后的节点
搜索二叉树中,删除一个值什么的需要后继节点的函数。
分为三种情况:
1.一个节点有右孩子,则在中序遍历中,该节点的后继是它的右子树的最左节点。
2. 这个节点是它父亲的左孩子,则该节点的后继节点是它的父亲
3. 这个节点是它父亲的右孩子,则需要一直向上搜索,直到它们n-1代祖先是它第n代祖先的左孩子,则它的后继就是第n个祖先。如果一直搜索到根节点,也没有找到n-1代祖先是它第n代祖先的左孩子,则该节点是整个树的中序遍历中的最后一个节点,即它没有后继。
protected Node getMinimum(Node node) {
while (node.left != null) {
node = node.left;
}
return node;
}
//找后继节点
protected Node getSuccessor(Node node) {
// if there is right branch, then successor is leftmost node of that
// subtree
if (node.right != null) {//如果有右子节点,找右子节点最左边的
return getMinimum(node.right);
} else { // otherwise it is a lowest ancestor whose left child is also
// ancestor of node//没有右子节点找父节点
Node currentNode = node;
Node parentNode = node.parent;
while (parentNode != null && currentNode == parentNode.right) {
// go up until we find parent that currentNode is not in right
// subtree.
currentNode = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
}
}
注意这里的路径,强调了必须是从根节点到叶节点的路径才算。
先序遍历做(在根节点判断满不满足,然后左右遍历),递归,形参(root,vector,k,cursum)
每次更新的时候,
void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector& path, int& currentSum);
void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
if(pRoot == nullptr)
return;
std::vector path;
int currentSum = 0;
FindPath(pRoot, expectedSum, path, currentSum);
}
void FindPath
(
BinaryTreeNode* pRoot,
int expectedSum,
std::vector& path,
int& currentSum
)
{ //更新参数
currentSum += pRoot->m_nValue;
path.push_back(pRoot->m_nValue);
// 如果是叶结点,并且路径上结点的和等于输入的值
// 打印出这条路径
bool isLeaf = pRoot->m_pLeft == nullptr && pRoot->m_pRight == nullptr;
if(currentSum == expectedSum && isLeaf)
{
printf("A path is found: ");
std::vector::iterator iter = path.begin();
for(; iter != path.end(); ++ iter)
printf("%d\t", *iter);
printf("\n");
}
// 如果有相应的节点,则遍历它的子结点
if(pRoot->m_pLeft != nullptr)
FindPath(pRoot->m_pLeft, expectedSum, path, currentSum);
if(pRoot->m_pRight != nullptr)
FindPath(pRoot->m_pRight, expectedSum, path, currentSum);
// 在返回到父结点之前,在路径上删除当前结点,
// 并在currentSum中减去当前结点的值
currentSum -= pRoot->m_nValue;
path.pop_back();
}
这道题需要数组里累积和为k的最长路径作为基础,同样是利用map,来做,不同的是,这里开始时map[0]=0,表示什么都不加的时候0层。先序遍历做,和上一题差不多,在根节点处判断,然后遍历左右子节点,返回父节点的时候需要特别注意一下。
需要的参数 root,cursum,k,map,level,maxlen
int getMaxlen(BinaryTreeNode *root, int k) {
if (root == nullptr)return 0;
mapm;
m[0] = 0;
getMaxlen(root, k, 0, 1, 0, m);// 当前第一层
}
int getMaxlen(BinaryTreeNode *root, int k, int cursum, int level, int maxlen, mapm) {
cursum += root->m_nValue;
if (!m.count(cursum)) {
m[cursum] = level;
}
if (m.count(cursum - k)) {
maxlen = max(maxlen, level - m[cursum - k]);
}
maxlen = getMaxlen(root->m_pLeft, k, cursum, level + 1, maxlen, m);
maxlen = getMaxlen(root->m_pRight, k, cursum, level + 1, maxlen, m);
if ( m[cursum]==level ) {
m.erase(cursum);
}
return maxlen;
}
其他的情况我就不写了,值得说明的是先序+后序重构二叉树有些是重构不出来的。而上面的两个,基本思路一致,我就只说明第一个了。注意这里的二叉树都不含重复的数字!
先序 根左右 1 2 4 7 3 5 6 8
中序 左根右 4 7 2 1 5 3 8 6
首先从先序中找到根,然后在中序中找到根的位置,以此判断左子树和右子树。不断递归。每次递归返回的都是当前状态的根节点。
函数的参数有(先序开始,先序的结束,中序的开始,中序的结束)也是那种常见的数组的开头和结尾,不断缩短的形式
作用是:返回当前的根节点,并利用确定的左右子树的范围,构建左右子树(利用递归函数)
// 需要先声明
BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder);
BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
{
if(preorder == nullptr || inorder == nullptr || length <= 0)
return nullptr;
return ConstructCore(preorder, preorder + length - 1,
inorder, inorder + length - 1);
}
BinaryTreeNode* ConstructCore//注意这里的参数,也是那种常见的数组的开头和结尾,不断缩短的形式
(
int* startPreorder, int* endPreorder,
int* startInorder, int* endInorder
)
{
// 前序遍历序列的第一个数字是根结点的值
int rootValue = startPreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();// 注意这里构建的形式
root->m_nValue = rootValue;// 初始化
root->m_pLeft = root->m_pRight = nullptr;
// basecase 先序和中序数组都是剩下一个值,这个值必须还是相等的
if(startPreorder == endPreorder)
{
if(startInorder == endInorder && *startPreorder == *startInorder)
return root;
else
throw std::exception("Invalid input.");
}
// 在中序遍历中找到根结点的值
int* rootInorder = startInorder;
while(rootInorder <= endInorder && *rootInorder != rootValue)
++ rootInorder;
if(rootInorder == endInorder && *rootInorder != rootValue)
throw std::exception("Invalid input.");
int leftLength = rootInorder - startInorder;
int* leftPreorderEnd = startPreorder + leftLength;
if(leftLength > 0)// 如果有左子树
{
// 构建左子树,注意这里函数返回的是左子树的根节点
root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd,
startInorder, rootInorder - 1);
}// 如果有右子树
if(leftLength < endPreorder - startPreorder)
{
// 构建右子树
root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
rootInorder + 1, endInorder);
}
return root;
}