整理一下刷题过程中的思路,在这里进行一下总结与分享。
github地址:https://github.com/lvjian0706/Leetcode-solutions
github项目是刚刚新建的,陆续会将整理的代码以及思路上传上去,代码是基于C++与python的。同时会将基础的排序算法等也一并进行整理上传。
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
层序遍历:宽度优先搜索(bfs)
1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
2. 如果树为空,返回空ans数组;
3. 首先将进栈,并且定义idx为当前level;
4. 循环遍历队头中的元素:
4.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
4.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,直接push进subAns;否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAnspush进ans中,再清空subAns,并将队头元素的值push进去;
4.3 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
5. 将最后一组subAns的值push进ans中,返回ans;
*/
vector<vector<int>> levelOrder(TreeNode* root) {
queue<pair<TreeNode*, int>> qnode;
vector<vector<int>> ans;
if(!root) return ans;
vector<int> subAns;
qnode.push(make_pair(root, 0));
int idx = 0;
while(!qnode.empty()){
pair<TreeNode*, int> pnode = qnode.front();
qnode.pop();
TreeNode* node = pnode.first;
int nowIdx = pnode.second;
if(nowIdx != idx){
ans.push_back(subAns);
subAns.clear();
idx = nowIdx;
}
subAns.push_back(node->val);
if(node->left) qnode.push(make_pair(node->left, nowIdx+1));
if(node->right) qnode.push(make_pair(node->right, nowIdx+1));
}
ans.push_back(subAns);
return ans;
}
};
给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回锯齿形层次遍历如下:
[
[3],
[20,9],
[15,7]
]
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
层序遍历:宽度优先搜索(bfs)
1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
2. 如果树为空,返回空ans数组;
3. 首先将进栈,并且定义idx为当前level;
4. 定义flag变量用于判断当前行是从左往右还是从右往左进行遍历;
5. 循环遍历队头中的元素:
5.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
5.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,通过flag的状态判断是将当前元素push到数组末尾还是insert到数组头部;
5.3. 否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAns插入ans的第一个位置,再清空subAns,并通过flag的状态判断是将当前元素push到数组末尾还是insert到数组头部;;
5.4 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
6. 将最后一组subAns的值插入ans的第一个位置,返回ans;
*/
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> ans;
if(!root) return ans;
vector<int> subAns;
queue<pair<TreeNode*, int>> nodeQ;
nodeQ.push(make_pair(root, 0));
int idx = 0;
bool flag = true;
while(!nodeQ.empty()){
TreeNode* node = nodeQ.front().first;
int nowIdx = nodeQ.front().second;
nodeQ.pop();
if(nowIdx!=idx){
ans.push_back(subAns);
subAns.clear();
flag = !flag;
idx = nowIdx;
}
if(flag) subAns.push_back(node->val);
else subAns.insert(subAns.begin(), node->val);
if(node->left) nodeQ.push(make_pair(node->left, nowIdx+1));
if(node->right) nodeQ.push(make_pair(node->right, nowIdx+1));
}
ans.push_back(subAns);
return ans;
}
};
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
深度优先搜索:
1. 传入参数:当前节点以及上一层节点的深度;
2. 如果当前节点为空,返回上一层节点的深度;
3. 否则,采用分治思想,递归计算左子树的深度和右子树的深度,返回深度最大值;
*/
int dfs(TreeNode* root, int depth){
if(!root) return depth;
int leftDepth = dfs(root->left, depth+1);
int rightDepth = dfs(root->right, depth+1);
return max(leftDepth, rightDepth);
}
/*
树的遍历:求树的最长路径的深度,可以使用深度优先搜索(dfs)
*/
int maxDepth(TreeNode* root) {
int ans = dfs(root, 0);
return ans;
}
};
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
层序遍历:宽度优先搜索(bfs)
1. 创建队列存放二叉树的节点以及节点对应的level,其中使用pair来存放二叉树的节点以及节点对应的level;
2. 如果树为空,返回空ans数组;
3. 首先将进栈,并且定义idx为当前level;
4. 循环遍历队头中的元素:
4.1 使用pnode存储队头元素,保存队头元素的值以及level,并将队头元素出栈;
4.2 判断队头元素的level与idx是否一致,一致说明队头元素与之前的元素属于相同的level,直接push进subAns;否则说明队头元素与之前的元素属于不同的level,更新idx,先将subAns插入ans的第一个位置,再清空subAns,并将队头元素的值push进去;
4.3 判断是否还有左右孩子,有的话将左右孩子和对应的level(nowIdx+1)存入队列;
5. 将最后一组subAns的值插入ans的第一个位置,返回ans;
*/
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> ans;
if(!root) return ans;
vector<int> subAns;
queue<pair<TreeNode*, int>> nodeQ;
nodeQ.push(make_pair(root, 0));
int idx = 0;
while(!nodeQ.empty()){
TreeNode* node = nodeQ.front().first;
int nowIdx = nodeQ.front().second;
nodeQ.pop();
if(nowIdx!=idx){
ans.insert(ans.begin(), subAns);
subAns.clear();
idx = nowIdx;
}
subAns.push_back(node->val);
if(node->left) nodeQ.push(make_pair(node->left, nowIdx+1));
if(node->right) nodeQ.push(make_pair(node->right, nowIdx+1));
}
ans.insert(ans.begin(), subAns);
return ans;
}
};
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/
9 20
/
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/
2 2
/
3 3
/
4 4
返回 false 。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
计算二叉树的深度:
1. 如果不存在该节点,返回0;
2. 计算左子树深度和右子树深度;
3. 该树的深度为左子树深度和右子树深度的最大值加1(1代表根节点深度);
*/
int height(TreeNode* root){
if(!root) return 0;
int left = height(root->left);
int right = height(root->right);
return max(left, right)+1;
}
/*
判断二叉树是否平衡:考虑到需要判断二叉树中每个节点并计算深度,可以使用深度优先搜索(dfs)
1. 如果不存在节点,返回true;
2. 判断左子树和右子树是否是平衡二叉树;
3. 计算左子树深度和右子树深度;
4. 当左子树和右子树都是平衡二叉树,且左子树深度和右子树深度差的绝对值不超过1时,返回true,否则,返回false;
*/
bool isBalanced(TreeNode* root) {
if(!root) return true;
bool leftBanlanced = isBalanced(root->left);
bool rightBanlanced = isBalanced(root->right);
int lheight = height(root->left);
int rheight = height(root->right);
if(leftBanlanced && rightBanlanced && lheight <= rheight+1 && rheight <= lheight+1){
return true;
}
else return false;
}
};
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
示例 1:
输入: [1,2,3]
1
/
2 3
输出: 6
示例 2:
输入: [-10,9,20,null,null,15,7]
-10
/
9 20
/
15 7
输出: 42
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
计算每个节点的贡献值,同时使用全局变量maxSum记录遍历到当前节点时的最大路径和:
1. 当该节点为空时,贡献值为0;
2. 计算该节点左子树和右子树的贡献值,其中,需要保证贡献值大于等于0(贡献值为负数时,不将该子树的路径和加入到最终的结果中,因此,相当于加0);
3. 计算该节点的最大路径和,更新maxSum;
4. 返回该节点的贡献值(该节点的值加max(左子树的最大路径和,右子树的最大路径和));
*/
int nodeGain(TreeNode* root, int &maxSum){
if(!root) return 0;
int leftSum = max(nodeGain(root->left, maxSum), 0);
int rightSum = max(nodeGain(root->right, maxSum), 0);
int nowSum = root->val + leftSum + rightSum;
maxSum = max(maxSum, nowSum);
return root->val + max(leftSum, rightSum);
}
/*
最大路径和:需要进行深度遍历,使用深度优先搜索(dfs)
1. 定义maxSum变量实时保存每个节点的最大路径和;
2. 计算每个节点的贡献值,同时使用全局变量maxSum记录遍历到当前节点时的最大路径和:
3. 返回maxSum;
*/
int maxPathSum(TreeNode* root) {
int maxSum = INT_MIN;
int rootPathSum = nodeGain(root, maxSum);
return maxSum;
}
};
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
最近公共祖先:可以用深度优先遍历(dfs)
注意:p、q 为不同节点且均存在于给定的二叉树中;
1. 如果节点为空,直接返回NULL;
2. 当遍历到p或q时,直接返回该节点;(找到了其中一个节点)
3. 递归遍历,如果左子树中遍历到了某一个节点,返回该节点;右子树同理;
4. 当左子树和右子树都存在某个节点时,返回根节点root;(找到了最近公共节点)
5. 当只有左子树存在节点时,返回左子树的节点;(有可能只找到了一个节点,也有可能两个节点都在左子树);右子树同理;
6. 否则,不存在两个节点,返回null;
由于是自底向上,而且一定会存在p和q节点,因此在递归过程中最近公共节点会覆盖子节点
*/
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root) return NULL;
if(root->val==p->val || root->val==q->val) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left!=NULL && right!=NULL) return root;
else if(left!=NULL) return left;
else if(right!=NULL) return right;
else return NULL;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
/*
递归遍历节点,判断该节点是否是p或q的祖先,同时更新ans:
1. 如果root不存在,返回false;
2. 判断左孩子是否为p和q的公共节点以及右孩子是否为p和q的公共节点;
3. 如果左右孩子同时为p和q的公共节点,或者root是q或者p中的一个且左右孩子中有一个为p和q的公共节点,则说明root为p和q的最近公共节点;
4. 如果root是q或者p中的一个或者左右孩子中有一个为p和q的公共节点,则说明该节点是p或q的祖先,返回true;
由于是自底向上判断,且一定存在p和q两个节点,因此找到的一定是最近公共节点;
*/
bool isAncestor(TreeNode* root, TreeNode* p, TreeNode* q, TreeNode* &ans){
if(!root) return false;
bool isleft = isAncestor(root->left, p, q, ans);
bool isright = isAncestor(root->right, p, q, ans);
if((isleft && isright) || ((root->val==q->val || root->val==p->val) && (isleft || isright))){
ans = root;
}
return isleft || isright || (root->val==q->val || root->val==p->val);
}
/*
最近公共祖先:可以使用深度遍历的方法,使用深度优先搜索(dfs)
1. 定义变量ans存放最近公共节点
2. 定义函数isAncestor递归遍历节点,判断该节点是否是p或q的祖先,同时更新ans
*/
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* ans;
bool isroot = isAncestor(root, p, q, ans);
return ans;
}
};