Leetcode刷题笔记--Hot41-50

目录

1--二叉树的层序遍历(102)

2--二叉树的最大深度(104)

3--从前序与中序遍历序列构造二叉树(105)

4--二叉树展开为链表(114)

5--买卖股票的最佳时机(121)

6--二叉树中的最大路径和(124)

7--最长连续序列(128)

8--只出现一次的数字(136)

9--单词拆分(139)

10--环形链表(141)


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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值