目录
1--二叉树的层序遍历(102)
主要思路:
经典广度优先搜索,基于队列;
对于本题需要将同一层的节点放在一个数组中,因此遍历的时候需要用一个变量 nums 来记录当前层的节点数,即 nums 等于队列元素的数目;
#include <iostream>
#include <vector>
#include <queue>
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) {}
};
class Solution {
public:
std::vector<std::vector<int>> levelOrder(TreeNode* root) {
std::vector<std::vector<int>> res;
if(root == nullptr) return res;
std::queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int nums = q.size(); // 当前层的节点数
std::vector<int> tmp;
while(nums > 0){ // 遍历处理同一层
TreeNode *cur = q.front();
q.pop();
tmp.push_back(cur->val);
if(cur->left != nullptr) q.push(cur->left);
if(cur->right != nullptr) q.push(cur->right);
nums--;
}
res.push_back(tmp); // 记录当前层的元素
}
return res;
}
};
int main(int argc, char* argv[]){
// root = [1, null, 2, 3]
TreeNode *Node1 = new TreeNode(3);
TreeNode *Node2 = new TreeNode(9);
TreeNode *Node3 = new TreeNode(20);
TreeNode *Node4 = new TreeNode(15);
TreeNode *Node5 = new TreeNode(7);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Solution S1;
std::vector<std::vector<int>> res = S1.levelOrder(Node1);
for(auto item : res) {
for (int v : item) std::cout << v << " ";
std::cout << std::endl;
}
return 0;
}
2--二叉树的最大深度(104)
主要思路:
递归计算左右子树的深度,选取两者最大值 +1 返回;
#include <iostream>
#include <vector>
#include <queue>
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) {}
};
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == nullptr) return 0;
int res = dfs(root);
return res;
}
int dfs(TreeNode* root){
if(root == nullptr) return 0;
int left_height = dfs(root->left);
int right_height = dfs(root->right);
int cur_height = std::max(left_height, right_height) + 1;
return cur_height;
}
};
int main(int argc, char* argv[]){
// root = [3,9,20,null,null,15,7]
TreeNode *Node1 = new TreeNode(3);
TreeNode *Node2 = new TreeNode(9);
TreeNode *Node3 = new TreeNode(20);
TreeNode *Node4 = new TreeNode(15);
TreeNode *Node5 = new TreeNode(7);
Node1->left = Node2;
Node1->right = Node3;
Node3->left = Node4;
Node3->right = Node5;
Solution S1;
int res = S1.maxDepth(Node1);
std::cout << res << std::endl;
return 0;
}
3--从前序与中序遍历序列构造二叉树(105)
主要思路:
思路类似于根据中序和后序遍历来构建二叉树;
对于本题,前序遍历的顺序是:根→左→右,则前序遍历的第一个节点是根节点;
中序遍历的顺序是:左→根→右,遍历中序数组找到根节点的位置,根据根节点的位置划分左右子树;
根据左右子树的大小划分前序遍历得到前序遍历的左右子树,递归构建左右子树即可;
#include <iostream>
#include <vector>
#include <queue>
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) {}
};
class Solution {
public:
TreeNode* buildTree(std::vector<int>& preorder, std::vector<int>& inorder) {
TreeNode* res = dfs(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1);
return res;
}
TreeNode* dfs(std::vector<int>& preorder, int p_st, int p_en, std::vector<int>& inorder, int i_st, int i_en){
if(p_st > p_en || i_st > i_en){
return nullptr;
}
// 头结点
TreeNode *root = new TreeNode(preorder[p_st]);
// 划分中序
int pos = -1; // 中序遍历中根节点的位置索引
for(int i = i_st; i <= i_en; i++){
if(inorder[i] == root->val){
pos = i;
break;
}
}
// 左子树的大小
int left_size = pos - i_st;
// 右子树的大小
int right_size = i_en - pos;
root->left = dfs(preorder, p_st+1, p_st+left_size, inorder, i_st, pos-1);
root->right = dfs(preorder, p_st+left_size+1, p_en, inorder, pos+1, i_en);
return root;
}
};
int main(int argc, char* argv[]){
// preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
std::vector<int> preorder = {3, 9, 20, 15, 7};
std::vector<int> inorder = {9, 3, 15, 20, 7};
Solution S1;
TreeNode* res = S1.buildTree(preorder, inorder);
// 层次遍历打印
std::queue<TreeNode*> q;
q.push(res);
while(!q.empty()){
TreeNode* tmp = q.front();
q.pop();
std::cout << tmp->val << " ";
if(tmp->left != nullptr) q.push(tmp->left);
if(tmp->right != nullptr) q.push(tmp->right);
}
std::cout << std::endl;
return 0;
}
4--二叉树展开为链表(114)
主要思路:
根据前序遍历的顺序,遍历二叉树,使用一个指针表示当前遍历到的节点的前驱节点,例如图中 2 的前驱节点是 1,4 的前驱节点是 3(根据前序遍历);
将前驱节点的左孩子为 nullptr,右孩子为当前遍历到的节点;根据前序遍历的顺序,递归更新前驱节点、左孩子和右孩子即可;
#include <iostream>
#include <queue>
#include <vector>
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) {}
};
class Solution {
public:
void flatten(TreeNode* root) {
dfs(root);
}
void dfs(TreeNode* cur){
if(cur == nullptr) return;
pre->left = nullptr; // 前驱节点的左孩子指向 nullptr
// 根
pre->right = cur; // 前驱节点的右孩子指向当前遍历到的节点
pre = cur; // 更新前驱节点
TreeNode* left = cur->left;
TreeNode* right = cur->right;
dfs(left); // 递归处理左子树
dfs(right); // 递归处理右子树
}
private:
TreeNode* pre = new TreeNode(-1); // 前序遍历的前驱节点
};
int main(int argc, char* argv[]){
// root = [1,2,5,3,4,null,6]
TreeNode* Node1 = new TreeNode(1);
TreeNode* Node2 = new TreeNode(2);
TreeNode* Node3 = new TreeNode(5);
TreeNode* Node4 = new TreeNode(3);
TreeNode* Node5 = new TreeNode(4);
TreeNode* Node6 = new TreeNode(6);
Node1->left = Node2;
Node1->right = Node3;
Node2->left = Node4;
Node2->right = Node5;
Node3->right = Node6;
Solution S1;
S1.flatten(Node1);
TreeNode* tmp = Node1;
while(tmp -> right != nullptr){
std::cout << tmp->val << " ";
tmp = tmp->right;
}
std::cout << std::endl;
return 0;
}
5--买卖股票的最佳时机(121)
主要思路:
计算截止到第 i 天的最低买入价格和最高的利润,不断更新即可;
#include <iostream>
#include <vector>
class Solution {
public:
int maxProfit(std::vector<int>& prices) {
int min_cost = prices[0];
int max_profit = 0;
for(int i = 0; i < prices.size(); i++){
min_cost = std::min(min_cost, prices[i]);
max_profit = std::max(max_profit, prices[i] - min_cost);
}
return max_profit;
}
};
int main(int argc, char *argv[]) {
// [7, 1, 5, 3, 6, 4]
std::vector<int> test = {7, 1, 5, 3, 6, 4};
Solution S1;
int res = S1.maxProfit(test);
std::cout << res << std::endl;
return 0;
}
6--二叉树中的最大路径和(124)
主要思路:
采用二叉树深度递归搜索,对于每一个结点,其最大路径和可能为以下四种情况:cur + cur->left, cur + cur->right, cur, cur + cur->left + cur->right;
对于每一个结点,其返回给上层的最大路径,可能为以下三种情况:cur + cur->left, cur + cur->right, cur;
#include <iostream>
#include <vector>
#include <limits.h>
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) {}
};
class Solution {
public:
int maxPathSum(TreeNode* root) {
dfs(root);
return max;
}
int dfs(TreeNode* root){
if(root == nullptr) return 0;
int left = dfs(root->left);
int right = dfs(root->right);
int cur_sum = std::max(
std::max(
std::max(left + root->val, right + root->val),
left + right + root->val),
root->val);
max = std::max(cur_sum, max);
int ret_sum = std::max(std::max(left + root->val, right + root->val), root->val);
return ret_sum;
}
private:
int max = INT_MIN;
};
int main(int argc, char *argv[]) {
// root = [1, 2, 3]
TreeNode* Node1 = new TreeNode(1);
TreeNode* Node2 = new TreeNode(2);
TreeNode* Node3 = new TreeNode(3);
Node1->left = Node2;
Node1->right = Node3;
Solution S1;
int res = S1.maxPathSum(Node1);
std::cout << res << std::endl;
return 0;
}
7--最长连续序列(128)
主要思路:
利用 unordered_set 实现元素的去重,遍历数组 nums,只处理连续序列最左边的元素(根据其上一个元素是否在 unordered_set 中存在),循环查询其下一个元素是否存在,存在则序列长度 +1;
#include <iostream>
#include <vector>
#include <unordered_set>
class Solution {
public:
int longestConsecutive(std::vector<int>& nums) {
std::unordered_set<int> set;
for(int & num : nums){ // 利用 unordered_set 去重
set.insert(num);
}
int max_len = 0;
for(int i = 0; i < nums.size(); i++){
int cur_len = 1;
int cur_num = nums[i];
if(set.count(cur_num - 1) == 0){ // 连续子序列左边界元素,不是左边界元素则跳过,确保每一个元素只遍历一次
while(set.count(cur_num + 1) != 0){ // 利用 unordered_set 实现 O(1) 查询
cur_len++; // 长度++
cur_num = cur_num + 1; // 右移
}
max_len = std::max(max_len, cur_len); // 更新最大长度
}
}
return max_len;
}
};
int main(int argc, char *argv[]) {
// nums = [100, 4, 200, 1, 3, 2]
std::vector<int> test = {100, 4, 200, 1, 3, 2};
Solution S1;
int res = S1.longestConsecutive(test);
std::cout << res << std::endl;
return 0;
}
8--只出现一次的数字(136)
主要思路:
按位异或,出现两次的数字会两两对消,最后只剩下出现过一次的数字;
#include <iostream>
#include <vector>
class Solution {
public:
int singleNumber(std::vector<int>& nums) {
int res = 0;
for(int num : nums){
res = res ^ num; // 按位异或
}
return res;
}
};
int main(int argc, char *argv[]) {
// nums = [4, 1, 2, 1, 2]
std::vector<int> test = {4, 1, 2, 1, 2};
Solution S1;
int res = S1.singleNumber(test);
std::cout << res << std::endl;
return 0;
}
9--单词拆分(139)
主要思路:
基于动态规划,dp[i] 表示字符串 s 前 i 个字符匹配(即可以由字典的字符串构建而成);
初始化 dp[0] = true;
状态转移方程为:dp[i] == dp[j] && s.substr(j, i-j),0 <= j <= i,只要有一个情况匹配即可;
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
class Solution {
public:
bool wordBreak(std::string s, std::vector<std::string>& wordDict) {
std::unordered_set<std::string> hash;
for(std::string str : wordDict){
hash.insert(str);
}
std::vector<bool> dp(s.length() + 1, 0); // dp[i] 表示前 i 个字符构成的字符串匹配
// 初始化
dp[0] = true;
// 遍历
for(int i = 1; i <= s.length(); i++){
for(int j = 0; j <= i; j++){
if (dp[j] && hash.find(s.substr(j, i - j)) != hash.end()) { // 前 j 个字符匹配,且后 i -j 个字符也匹配
dp[i] = true;
break; // 找到一种情况就 break
}
}
}
return dp[s.length()];
}
};
int main(int argc, char *argv[]) {
// s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
std::string str = "catsandog";
std::vector<std::string> wordDict = {"cats", "dog", "sand", "and", "cat"};
Solution S1;
bool res = S1.wordBreak(str, wordDict);
if(res) std::cout << "true" << std::endl;
else std::cout << "false" << std::endl;
return 0;
}
10--环形链表(141)
主要思路:
经典快慢指针,判断两个指针是否相遇即可;
#include <iostream>
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == nullptr) return false;
ListNode* slow = head;
ListNode* fast = head;
while(fast != nullptr && fast->next != nullptr){
fast = fast->next->next;
slow = slow->next;
if(fast == slow) return true;
}
return false;
}
};
int main(int argc, char *argv[]) {
// head = [3,2,0,-4], pos = 1
ListNode *Node1 = new ListNode(3);
ListNode *Node2 = new ListNode(2);
ListNode *Node3 = new ListNode(0);
ListNode *Node4 = new ListNode(-4);
Node1->next = Node2;
Node2->next = Node3;
Node3->next = Node4;
Node4->next = Node2;
Solution S1;
int res = S1.hasCycle(Node1);
if(res) std::cout << "true" << std::endl;
else std::cout << "false" << std::endl;
return 0;
}