标题划分方式部分根据公众号代码随想录的推文《刷题攻略》,部分改编自刷题过程中总结的部分思想。
放一下作者的网站:代码随想录
二叉树的属性
二叉树的深度
int TreeDepth(TreeNode* pRoot) {
if(!pRoot) return 0;
int cnt_left = TreeDepth(pRoot->left);
int cnt_right = TreeDepth(pRoot->right);
return max(cnt_left,cnt_right) + 1;
}
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
else if(!root->left && !root->right) return 1;
int min_depth = INT_MAX;
if(root->left) min_depth = min(minDepth(root->left), min_depth);
if(root->right) min_depth = min(minDepth(root->right), min_depth);
return min_depth + 1;
}
key point
- “无穷大”
INT_MAX,“无穷小”INT_MIN
平衡二叉树
左右子树高度差<=1
/*from top to down*/
int height(TreeNode* root)
{
if(root == nullptr) return 0;
return max(height(root->left),height(root->right))+1;
}
bool isBalanced(TreeNode* root) {
if(root == nullptr) return true;
return isBalanced(root->left) && isBalanced(root->right) && abs(height(root->left) - height(root->right)) <=1;
}
/*from down to top*/
int height(TreeNode* root)
{
if(root == nullptr) return 0;
int left_h = height(root->left);
int right_h = height(root->right);
if(left_h == -1 || right_h == -1 || abs(height(root->left) - height(root->right)) > 1 ) return -1;
return max(left_h, right_h) + 1;
}
bool isBalanced(TreeNode* root)
{
return height(root) >= 0;
}
完全二叉树
int countNodes(TreeNode* root) {
return getsum(root);
}
int getsum(TreeNode* root)
{
if(root == nullptr) return 0;
int cnt_f = getsum(root->left);
int cnt_r = getsum(root->right);
return 1 + cnt_f + cnt_r;
}
相同的树
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p && q) return false;
else if (p && !q) return false;
else if (!p && !q) return true;
else if(p->val != q->val) return false;
return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}
key point
注意使用递归,不要一直想用迭代完成
[变形]对称二叉树
bool function(TreeNode* p, TreeNode* q)
{
if(p == nullptr && q == nullptr) return true;
else if(p != nullptr && q == nullptr) return false;
else if(p == nullptr && q != nullptr) return false;
else if(p->val != q->val) return false;
return p->val == q->val && function(p->left,q->right) && function(p->right,q->left);
}
bool isSymmetric(TreeNode* root) {
return function(root,root);
}
key point
相同的树变形版。
左叶子之和
int sumOfLeftLeaves(TreeNode* root) {
if(root == nullptr) return 0;
int leftval = sumOfLeftLeaves(root->left);
int rightval = sumOfLeftLeaves(root->right);
int midval = 0;
if(root->left && root->left->left == nullptr && root->left->right == nullptr) midval = root->left->val;
int sum = midval + leftval + rightval;
return sum;
}
二叉树的遍历
前序遍历
递归
vector<int> dlr;
vector<int> preorderTraversal(TreeNode* root) {
if(!root) return {};
dlr.push_back(root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
return dlr;
}
迭代
vector<int> preorderTraversal(TreeNode* root) {
if(!root) return {};
/*迭代*/
vector<int> res;
stack<TreeNode*> sk;
TreeNode* ptr = root;
//true = 1, false = 0
while(!sk.empty() || ptr )//栈不空 or ptr不为空
{
while(ptr)// 一路向左
{
res.push_back(ptr->val);
sk.push(ptr);
ptr = ptr->left;
}//ptr == NULL
ptr = sk.top();
sk.pop();
ptr = ptr->right;
}
return res;
}
中序遍历
递归
vector<int> ldr;
vector<int> inorderTraversal(TreeNode* root) {
if(!root) return {};
inorderTraversal(root->left);
ldr.push_back(root->val);
inorderTraversal(root->right);
return ldr;
}
迭代
vector<int> inorderTraversal(TreeNode* root) {
if(!root) return {};
/*迭代*/
stack<TreeNode*> sk;
vector<int> ldr;
TreeNode* ptr = root;
while(!sk.empty() || ptr)
{
while(ptr)//一路向左
{
sk.push(ptr);
ptr = ptr->left;
}//ptr == null
ptr = sk.top();
sk.pop();
ldr.push_back(ptr->val);
ptr = ptr->right;
}
return ldr;
}
后序遍历
递归
vector<int> lrd;
vector<int> postorderTraversal(TreeNode* root) {
if(!root) return {};
/*postorderTraversal(root->left);
postorderTraversal(root->right);
lrd.push_back(root->val);
return lrd;*/
}
迭代
vector<int> postorderTraversal(TreeNode* root) {
if(!root) return {};
/*迭代*/
vector<int> lrd;
stack<TreeNode*> sk;
TreeNode* ptr = root;
TreeNode* pre = nullptr;
while(!sk.empty() || ptr)
{
while(ptr) //找到以ptr为根节点的子树的最左结点
{
sk.push(ptr);
ptr = ptr->left;
}// ptr = nul
ptr = sk.top();
sk.pop();
if(ptr->right == nullptr || pre == ptr->right) //右子树为空 or ptr指向结点的右子树访问完毕
{
lrd.push_back(ptr->val);
pre = ptr;
ptr = nullptr; //避免执行while(ptr)循环
}
else if(ptr->right)//该结点右子树不为空,则要压栈,再次寻找以该结点为根结点的左子树
{
sk.push(ptr);
ptr = ptr->right; // 执行while(ptr)循环
}
}
return lrd;
}
key point
右子树按照后序遍历LRD的顺序访问完后,最后一个访问结点用pre 标记,如果ptr->right == pre, 则说明右子树已经访问完毕,可以访问根节点了。
层次遍历(广度优先遍历)
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(!root) return res;
queue<TreeNode*> que;
vector<int> tmp;
que.push(root);
while(!que.empty())
{
int cur_len = que.size();
for(int i = 0; i < cur_len; i++)
{
TreeNode* ptr = que.front();
que.pop();
tmp.push_back(ptr->val);
if(ptr->left) que.push(ptr->left);
if(ptr->right) que.push(ptr->right);
}
res.push_back(tmp);
tmp.clear();
}
return res;
}
key point
-
借助队列,当前队列长度为该层结点个数
-
先出队,再判断左右子树是否为空
-
注意清空临时容器
[变形] 找树左下角的值
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if(root) que.push(root);
int res = 0;
while(!que.empty())
{
int size = que.size();
for(int i = 0; i < size; i++)
{
TreeNode* cur = que.front();
que.pop();
if(i == 0) res = cur->val;
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
}
return res;
}
深度优先遍历DFS
递归
void DFS(TypeName* root)
{
if(root == nullptr) return;
DFS(root->left);
DFS(root->right);
}
[变形] 路径
void findpath(TreeNode* root, string path, vector<string>&paths)
{
if(root == nullptr) return;
// root != nullptr
path += to_string(root->val);
//is leaf node
if(root->left == nullptr && root->right == nullptr) paths.push_back(path);
//is not leaf node
else
{
path += "->";
findpath(root->left,path,paths);
findpath(root->right,path,paths);
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
findpath(root,"", paths);
return paths;
}
key point
格式控制:叶子结点只输出值,非叶子结点时要输出->。
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr) return false;
//is leaf node
else if(root->left == nullptr && root->right == nullptr) return targetSum == root->val;
//is not leaf node
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
[变形] 公共祖先
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p->val < root->val && q->val < root->val) return lowestCommonAncestor(root->left,p,q);
else if(p->val > root->val && q->val > root->val) return lowestCommonAncestor(root->right,p,q);
return root;
}
key point
- 二叉搜索树子树和根节点之间的关系是严格的,即小于或大于。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(p == root || q == root || root == nullptr) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left && right) return root;
else if(left == nullptr && right) return right;
else if(left && right == nullptr) return left;
else return nullptr;
}
key point
- from down to top : 后序遍历逻辑
二叉树的修改与构造
翻转二叉树
TreeNode* invertTree(TreeNode* root) {
/*from top to down*/
if(!root) return root;
TreeNode* left_tree = invertTree(root->left);
TreeNode* right_tree = invertTree(root->right);
root->left = right_tree;
root->right = left_tree;
return root;
/*from down to top*/
if(!root) return root;
TreeNode* left_tree = root->right;
TreeNode* right_tree = root->left;
root->left = left_tree;
root->right = right_tree;
invertTree(root->left);
invertTree(root->right);
return root;
}
key point
- 自顶向下:先递归,再处理
2.自底向上:先处理,再递归
构造二叉树
LDR+LRD
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
if(inorder.size() == 0 || postorder.size() == 0) return nullptr;
return traversal(inorder,postorder);
}
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder)
{
/*LDR + LRD*/
//空树
if(postorder.size() == 0) return nullptr;
//LRD 中的D
int rootvalue = postorder[postorder.size()-1];
TreeNode* root = new TreeNode(rootvalue);
//only 1 node
if(postorder.size() == 1) return root;
//分割inorder
int delimitation;
for(delimitation = 0; delimitation < inorder.size(); delimitation++)
{
if(inorder[delimitation] == rootvalue) break;
}
//[0,delimitation)
vector<int> leftinorder(inorder.begin(), inorder.begin()+delimitation);//vector initialization
//[delimitation+1,end)
vector<int> rightinorder(inorder.begin()+ delimitation+1, inorder.end());//+1
//舍弃postorder末尾元素
postorder.erase(postorder.end()-1,postorder.end());
//切割后序数组
//[0,leftinorder.size)
vector<int>leftpostorder(postorder.begin(), postorder.begin()+leftinorder.size());
//[leftinorder.size+1,end)
vector<int>rightpostorder(postorder.begin()+leftinorder.size(), postorder.end());//not +1
root->left = traversal(leftinorder, leftpostorder);
root->right = traversal(rightinorder, rightpostorder);
return root;
}
DLR+LDR
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
if(preorder.size() == 0 || inorder.size() == 0) return nullptr;
return traversal(preorder,inorder);
}
TreeNode* traversal(vector<int>& preorder, vector<int>& inorder)
{
if(preorder.size() == 0) return nullptr;
int rootvalue = preorder[0];
TreeNode* root = new TreeNode(rootvalue);
if(preorder.size() == 1) return root;
int delimination;
for(delimination = 0; delimination < inorder.size(); delimination++)
{
if(inorder[delimination] == rootvalue) break;
}
vector<int> leftinorder(inorder.begin(), inorder.begin()+delimination);
vector<int> rightinorder(inorder.begin()+delimination+1, inorder.end());
preorder.erase(preorder.begin(), preorder.begin()+1);
vector<int> leftpreorder(preorder.begin(), preorder.begin()+leftinorder.size());
vector<int> rightpreorder(preorder.begin()+leftinorder.size(), preorder.end());
root->left = traversal(leftpreorder,leftinorder);
root->right = traversal(rightpreorder,rightinorder);
return root;
}
合并二叉树
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == nullptr) return root2;
if(root2 == nullptr) return root1;
root1->val += root2->val;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* node = new TreeNode(-1);
if(nums.size() == 1)
{
node->val = nums[0];
return node;
}
int maxvalue = 0;
int maxvalueindex = 0;
for(int i = 0; i < nums.size(); i++)
{
if(nums[i] > maxvalue)
{
maxvalue = nums[i];
maxvalueindex = i;
}
}
node->val = maxvalue;
if(maxvalueindex > 0)
{
vector<int> leftvec(nums.begin(), nums.begin()+maxvalueindex);
node->left = constructMaximumBinaryTree(leftvec);
}
if(maxvalueindex < nums.size()-1)
{
vector<int> rightvec(nums.begin()+maxvalueindex+1, nums.end());
node->right = constructMaximumBinaryTree(rightvec);
}
return node;
}
二叉搜索树/二叉查找树/二叉排序树
中序遍历是有序数组!!
属性
左子树的所有结点值 < 根结点值 < 右子树所有结点值
TreeNode* searchBST(TreeNode* root, int val) {
if(root == nullptr) return nullptr;
else if(root->val == val) return root;
else if(root->val < val) return searchBST(root->right, val);
else if(root->val > val) return searchBST(root->left, val);
return root;
}
long maxval = LONG_MIN;
bool isValidBST(TreeNode* root) {
if(root == nullptr) return true;
bool left = isValidBST(root->left);
if(maxval < root->val) maxval = root->val;
else return false;
bool right = isValidBST(root->right);
return left && right;
}
key point
- 中序遍历逻辑
int res = INT_MAX;
TreeNode* pre;
int getMinimumDifference(TreeNode* root)
{
traversal(root);
return res;
}
void traversal(TreeNode* cur)
{
if(cur == nullptr) return;
traversal(cur->left);
if(pre != nullptr) res = min(res, cur->val - pre->val);//中序遍历为有序数列,后比前大
pre = cur;
traversal(cur->right);
}
int maxcount;
int count;
TreeNode* pre;
vector<int> res;
vector<int> findMode(TreeNode* root) {
count = 0;
maxcount = 0;
pre = nullptr;
res.clear();
BST(root);
return res;
}
void BST(TreeNode* cur)
{
if(cur == nullptr) return;
BST(cur->left);
if(pre == nullptr) count = 1;
else if(pre->val == cur->val) count++;
else count = 1;
pre = cur;
if(count == maxcount) res.push_back(cur->val);
if(count > maxcount)
{
maxcount = count;
res.clear();
res.push_back(cur->val);
}
BST(cur->right);
return;
}
key point
-
pre指针的使用
-
动态更新
int pre;
void traversal(TreeNode* cur)
{
if(cur == nullptr) return ;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
TreeNode* convertBST(TreeNode* root) {
/*RDL遍历*/
pre = 0;
traversal(root);
return root;
}
修改与构造
TreeNode* sortedArrayToBST(vector<int>& nums)
{
return construct(nums,0,nums.size()-1);
}
TreeNode* construct(vector<int>&nums,int left, int right)
{
if(left > right) return nullptr;
int mid = (left + right) /2;
TreeNode* ptr = new TreeNode(nums[mid]);
ptr->left = construct(nums,left,mid-1);
ptr->right = construct(nums,mid+1,right);
return ptr;
}
key point
-
思路同中序遍历DLR序列,先对结点进行操作,然后进行迭代。
-
递归思路同二叉树的遍历【其实,对二叉树的操作都基于对二叉树的遍历】
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == nullptr)
{
root = new TreeNode(val);
return root;
}
else if(root->val == val) return root;
else if(root->val > val) root->left = insertIntoBST(root->left,val);
else if(root->val < val) root->right = insertIntoBST(root->right,val);
return root;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == nullptr) return root;
if(root->val == key)
{
//is leaf node
if(root->left == nullptr && root->right == nullptr)
{
delete root;
return nullptr;
}
//is not leaf node
else if(root->right == nullptr)
{
auto retNode = root->left;
delete root;
return retNode;
}
else if(root->left == nullptr)
{
auto retNode = root->right;
delete root;
return retNode;
}
// 左右子树均不空 very important
else
{
TreeNode* cur = root->right;
while(cur->left != nullptr)
{
cur = cur->left;
}
cur->left = root->left;
TreeNode* tmp = root;
root = root->right;
delete tmp;
return root;
}
}
else if(root->val > key) root->left = deleteNode(root->left,key);
else if(root->val < key) root->right = deleteNode(root->right,key);
return root;
}
key point
主要是左右子树均不空的情况没有想到。属于固定思路:左子树放至右子树的最左结点的左指针上。
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == nullptr) return nullptr;
if(root->val < low)
{
root = root->right;
root = trimBST(root,low,high);
}
else if(root->val > high)
{
root = root->left;
root = trimBST(root,low,high);
}
else
{
root->left = trimBST(root->left,low,high);
root->right = trimBST(root->right,low,high);
}
return root;