LeetCode刷题记(五):121~150题

news2024/12/31 5:12:59

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^4
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		int maxProfit(vector<int>& prices) {
			int minPrice = INT_MAX;
			int maxProfit = 0;
			for(int i = 0; i < prices.size(); i++) {
				if(prices[i] < minPrice) {
					minPrice = prices[i];
				} else if(prices[i] - minPrice > maxProfit) {
					maxProfit = prices[i] - minPrice;
				}
			}
			return maxProfit;
		}
};

int main() {
	vector<int> prices;
	int temp;
	while(cin >> temp) {
		prices.push_back(temp);
		if(cin.get() == '\n') 
			break;
	}
	Solution solution;
	cout << solution.maxProfit(prices);
	return 0;
}

122. 买卖股票的最佳时机Ⅱ

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

提示:

  • 1 <= prices.length <= 3 * 10^4
  • 0 <= prices[i] <= 10^4
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class Solution {
	public:
		int maxProfit(vector<int>& prices) {
			int n = prices.size();
			if(n <= 1) {
				return 0;
			}
			
			int maxProfit =  0;
			for(int i = 1; i < n; i++) {
				if(prices[i] > prices[i - 1]) {
					maxProfit += prices[i] - prices[i - 1];
				}
			}
			
			return maxProfit;
		}
};

int main() {
	int tmp;
	vector<int> prices;
	while(cin >> tmp) {
		prices.push_back(tmp);
		if(cin.get() == '\n')
			break;
	}
	
	Solution solution;
	cout << solution.maxProfit(prices) << endl;
	
	return 0;
}

123. 买卖股票的最佳时机Ⅲ

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

输入:prices = [1]
输出:0

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^5
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class Solution {
	public:
		int maxProfit(vector<int>& prices) {
			int n = prices.size();
			if(n <= 1) {
				return 0;
			}
			
			int maxProfit =  0;
			vector<int> leftProfit(n, 0);
			vector<int> rightProfit(n, 0);
			
			int minPrice = prices[0];
			// 每一天左侧的最大利润 
			for(int i = 1; i < n; i++) {
				minPrice = min(minPrice, prices[i]);
				leftProfit[i] = max(leftProfit[i - 1], prices[i] - minPrice);
			}
			
			int maxPrice = prices[n - 1];
			// 每一天右侧的最大利润 
			for(int i = n - 2; i >= 0; i--) {
				maxPrice = max(maxPrice, prices[i]);
				rightProfit[i] = max(rightProfit[i + 1], maxPrice - prices[i]);
			}
			
			// 将左右两侧的最大利润相加,找出最大的利润值 
			for(int i = 0; i < n; i++) {
				maxProfit = max(maxProfit, leftProfit[i] + rightProfit[i]);
			}
			
			return maxProfit;
		}
};

int main() {
	int tmp;
	vector<int> prices;
	while(cin >> tmp) {
		prices.push_back(tmp);
		if(cin.get() == '\n')
			break;
	}
	
	Solution solution;
	cout << solution.maxProfit(prices) << endl;
	
	return 0;
}

124. 二叉树中的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

示例 1:

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2:

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

提示:

  • 树中节点数目范围是 [1, 3 * 104]
  • -1000 <= Node.val <= 1000
#include<algorithm>
#include<iostream>
#include<queue>

using namespace std;

// Definition for a binary tree node
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		int maxPathSum(TreeNode* root) {
			int maxSum = INT_MIN;
			maxPathSumRecursive(root, maxSum);
			
			return maxSum;
		}
		
		int maxPathSumRecursive(TreeNode* node, int& maxSum) {
			if(node == NULL) {
				return 0;
			}
			
			int leftSum = max(0, maxPathSumRecursive(node->left, maxSum));
			int rightSum = max(0, maxPathSumRecursive(node->right, maxSum));
			
			int currentSum = node->val + leftSum + rightSum;
			maxSum = max(maxSum, currentSum);
			
			return node->val + max(leftSum, rightSum);
		}
};


TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	
	Solution solution;
	cout << solution.maxPathSum(root) << endl;
	
	return 0;
}

125. 验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。

示例 2:

输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。

示例 3:

输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

提示:

  • 1 <= s.length <= 2 * 10^5
  • s 仅由可打印的 ASCII 字符组成
#include<iostream>
#include<cctype>
using namespace std;

class Solution {
	public:
		bool isPalindrome(string s) {
			string processedStr = "";
			for(int i = 0; i < s.length(); i++) {
				if(isalnum(s[i])) {
					processedStr += tolower(s[i]);
				}
			}
			int left = 0, right = processedStr.length() - 1;
			while(left < right) {
				if(processedStr[left] != processedStr[right]) {
					return false;
				}
				left++;
				right--;
			}
			return true;
		}
};

int main() {
	string str;
	cin >> str;
	Solution solution;
	cout << boolalpha << solution.isPalindrome(str);
	return 0;
}

126. 单词接龙Ⅱ

按字典 wordList 完成从单词 beginWord 到单词 endWord 转化,一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> ... -> sk 这样的单词序列,并满足:

  • 每对相邻的单词之间仅有单个字母不同。
  • 转换过程中的每个单词 si1 <= i <= k)必须是字典 wordList 中的单词。注意,beginWord 不必是字典 wordList 中的单词。
  • sk == endWord

给你两个单词 beginWord 和 endWord ,以及一个字典 wordList 。请你找出并返回所有从 beginWord 到 endWord 的 最短转换序列 ,如果不存在这样的转换序列,返回一个空列表。每个序列都应该以单词列表 [beginWord, s1, s2, ..., sk] 的形式返回。

示例 1:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:[["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
解释:存在 2 种最短的转换序列:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"

示例 2:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:[]
解释:endWord "cog" 不在字典 wordList 中,所以不存在符合要求的转换序列。

提示:

  • 1 <= beginWord.length <= 5
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 500
  • wordList[i].length == beginWord.length
  • beginWordendWord 和 wordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有单词 互不相同

解法一:超出时间限制

#include<iostream>
#include<vector>
#include<queue>
#include<unordered_set>
using namespace std;

class Solution {
	public:
		// 使用广度优先搜索(BFS)的方法,通过逐步转换单词并在转换过程中保持跟踪路径,
		// 直到找到最短转换序列或者无法转换为止
		vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
			unordered_set<string> dict(wordList.begin(), wordList.end());
			vector<vector<string>> result;
			queue<vector<string>> q;
			q.push({beginWord});
			
			if(dict.find(endWord) == dict.end()) {
				return result;
			}
			
			bool found = false;
			while(!q.empty() && !found) {
				int size = q.size();
				unordered_set<string> visited;
				
				for(int i = 0; i < size; i++) {
					vector<string> path = q.front();
					q.pop();
					
					string word = path.back();
					for(int j = 0; j < word.length(); j++) {
						char originalChar = word[j];
						for(char c = 'a'; c <= 'z'; ++c) {
							if(c == originalChar) continue;
							word[j] = c;
							if(word == endWord) {
								path.push_back(word);
								result.push_back(path);
								found = true;
							}
							
							if(dict.find(word) != dict.end() && !found) {
								visited.insert(word);
								vector<string> newPath = path;
								newPath.push_back(word);
								q.push(newPath);
							}
						}
						
						word[j] = originalChar;
					}
				}
				
				for(const string& word : visited) {
					dict.erase(word);
				}
			}
			
			return result;
		}
};

int main() {
	vector<string> wordList;
	string s;
	string beginWord, endWord;
	while(cin >> s) {
		wordList.push_back(s);
		if(cin.get() == '\n')
			break;
	}
	cin >> beginWord >> endWord;
	
	Solution solution;
	vector<vector<string>> result = solution.findLadders(beginWord, endWord, wordList);
	
	 for (const std::vector<std::string>& path : result) {
        for (const std::string& word : path) {
            std::cout << word << " ";
        }
        std::cout << std::endl;
    }
	
	
	return 0;
}

解法二:

#include<iostream>
#include<vector>
#include<queue>
#include<unordered_map>
#include<unordered_set>
#include<set>
using namespace std;

class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string> &wordList) {
        vector<vector<string>> res;
        // 因为需要快速判断扩展出的单词是否在 wordList 里,因此需要将 wordList 存入哈希表,这里命名为「字典」
        unordered_set<string> dict = {wordList.begin(), wordList.end()};
        // 修改以后看一下,如果根本就不在 dict 里面,跳过
        if (dict.find(endWord) == dict.end()) {
            return res;
        }
        // 特殊用例处理
        dict.erase(beginWord);

        // 第 1 步:广度优先搜索建图
        // 记录扩展出的单词是在第几次扩展的时候得到的,key:单词,value:在广度优先搜索的第几层
        unordered_map<string, int> steps = {{beginWord, 0}};
        // 记录了单词是从哪些单词扩展而来,key:单词,value:单词列表,这些单词可以变换到 key ,它们是一对多关系
        unordered_map<string, set<string>> from = {{beginWord, {}}};
        int step = 0;
        bool found = false;
        queue<string> q = queue<string>{{beginWord}};
        int wordLen = beginWord.length();
        while (!q.empty()) {
            step++;
            int size = q.size();
            for (int i = 0; i < size; i++) {
                const string currWord = move(q.front());
                string nextWord = currWord;
                q.pop();
                // 将每一位替换成 26 个小写英文字母
                for (int j = 0; j < wordLen; ++j) {
                    const char origin = nextWord[j];
                    for (char c = 'a'; c <= 'z'; ++c) {
                        nextWord[j] = c;
                        if (steps[nextWord] == step) {
                            from[nextWord].insert(currWord);
                        }
                        if (dict.find(nextWord) == dict.end()) {
                            continue;
                        }
                        // 如果从一个单词扩展出来的单词以前遍历过,距离一定更远,为了避免搜索到已经遍历到,且距离更远的单词,需要将它从 dict 中删除
                        dict.erase(nextWord);
                        // 这一层扩展出的单词进入队列
                        q.push(nextWord);
                        // 记录 nextWord 从 currWord 而来
                        from[nextWord].insert(currWord);
                        // 记录 nextWord 的 step
                        steps[nextWord] = step;
                        if (nextWord == endWord) {
                            found = true;
                        }
                    }
                    nextWord[j] = origin;
                }
            }
            if (found) {
                break;
            }
        }
        // 第 2 步:回溯找到所有解,从 endWord 恢复到 beginWord ,所以每次尝试操作 path 列表的头部
        if (found) {
            vector<string> Path = {endWord};
            backtrack(res, endWord, from, Path);
        }
        return res;
    }

    void backtrack(vector<vector<string>> &res, const string &Node, unordered_map<string, set<string>> &from,
             vector<string> &path) {
        if (from[Node].empty()) {
            res.push_back({path.rbegin(), path.rend()});
            return;
        }
        for (const string &Parent: from[Node]) {
            path.push_back(Parent);
            backtrack(res, Parent, from, path);
            path.pop_back();
        }
    }
};


int main() {
	vector<string> wordList;
	string s;
	string beginWord, endWord;
	while(cin >> s) {
		wordList.push_back(s);
		if(cin.get() == '\n')
			break;
	}
	cin >> beginWord >> endWord;
	
	Solution solution;
	vector<vector<string>> result = solution.findLadders(beginWord, endWord, wordList);
	
	 for (const std::vector<std::string>& path : result) {
        for (const std::string& word : path) {
            std::cout << word << " ";
        }
        std::cout << std::endl;
    }
	
	
	return 0;
}

127. 单词接龙

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk

  • 每一对相邻的单词只差一个字母。
  •  对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
  • sk == endWord

给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。

示例 1:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
输出:5
解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。

示例 2:

输入:beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
输出:0
解释:endWord "cog" 不在字典中,所以无法进行转换。

提示:

  • 1 <= beginWord.length <= 10
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 5000
  • wordList[i].length == beginWord.length
  • beginWordendWord 和 wordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有字符串 互不相同
#include<iostream>
#include<vector>
#include<string>
#include<unordered_set>
#include<queue>

using namespace std;

class Solution {
	public:
		int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
			unordered_set<string> dict(wordList.begin(), wordList.end());
			if(dict.find(endWord) == dict.end()) {
				return 0;
			}
			
			queue<string> q;
			q.push(beginWord);
			int level = 1;
			
			while(!q.empty()) {
				int size = q.size();
				for(int i = 0; i < size; i++) {
					string word = q.front();
					q.pop();
					
					if(word == endWord) {
						return level;
					}
					
					for(int j = 0; j < word.length(); j++) {
						char originalChar = word[j];
						for(char c = 'a'; c <= 'z'; c++) {
							word[j] = c;
							if(dict.find(word) != dict.end()) {
								q.push(word);
								dict.erase(word);
							}
						}
						word[j] = originalChar;
					}
				}
				level++;
			} 
			
			return 0;
		}
};

int main() {
	vector<string> wordList;
	string s;
	string beginWord, endWord;
	while(cin >> s) {
		wordList.push_back(s);
		if(cin.get() == '\n')
			break;
	}
	cin >> beginWord >> endWord;
	
	Solution solution;
	cout << solution.ladderLength(beginWord, endWord, wordList);
	
	return 0;
}

128. 最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

提示:

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
	public:
		int longestConsecutive(vector<int>& nums) {
			unordered_set<int> numSet(nums.begin(), nums.end());
			int longestStreak = 0;
			
			for(int num : numSet) {
				if(numSet.find(num - 1) == numSet.end()) {
					// 检查这个数字是否是序列的开头
					int currentNum = num;
					int currentStreak = 1;
					
					while(numSet.find(currentNum + 1) != numSet.end()) {
						currentNum++;
						currentStreak++;
					} 
					
					longestStreak = max(longestStreak, currentStreak);
				}
			}
			
			return longestStreak;
		}
}; 

int main() {
	int tmp;
	vector<int> nums;
	while(cin >> tmp) {
		nums.push_back(tmp);
		if(cin.get() == '\n')
			break;
	}
	
	Solution solution;
	cout << solution.longestConsecutive(nums) << endl;
	
	return 0;
}

129. 求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。

每条从根节点到叶节点的路径都代表一个数字:

  • 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。

计算从根节点到叶节点生成的 所有数字之和 。

叶节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2,代表数字 12
从根到叶子节点路径 1->3,代表数字 13
因此,数字总和 = 12 + 13 = 25

示例 2:

输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5,代表数字 495
从根到叶子节点路径 4->9->1,代表数字 491
从根到叶子节点路径 4->0,代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

提示:

  • 树中节点的数目在范围 [1, 1000] 内
  • 0 <= Node.val <= 9
  • 树的深度不超过 10
#include<iostream>
#include<vector>
#include<queue>

using namespace std;

// Definition for a binary tree node
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		int sumNumbers(TreeNode* root) {
			return dfs(root, 0);
		}
		
		int dfs(TreeNode* node, int currentSum) {
			if(node == NULL) {
				return 0;
			}
			
			currentSum = currentSum * 10 + node->val;
			
			if(node->left == NULL && node->right == NULL) {
				return currentSum;
			}
			
			int leftSum = dfs(node->left, currentSum);
			int rightSum = dfs(node->right, currentSum);
			
			return leftSum + rightSum;
		}
};

TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	
	Solution solution;
	cout << solution.sumNumbers(root) << endl;
	
	return 0;
}

130. 被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

示例 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

示例 2:

输入:board = [["X"]]
输出:[["X"]]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j] 为 'X' 或 'O'
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		void solve(vector<vector<char>>& board) {
			if(board.empty() || board[0].empty()) {
				return;
			}

			int m = board.size();
			int n = board[0].size();

			// mark '0' on the boundaries and connect to boundaries
			for(int i = 0; i < m; i++) {
				markBoundary(board, i, 0);
				markBoundary(board, i, n - 1);
			}

			for(int j = 0; j < n; j++) {
				markBoundary(board, 0, j);
				markBoundary(board, m - 1, j);

			}

			// convert marked '0's back to '0' and unmarked '0's to 'X'
			for(int i = 0; i < m; i++) {
				for(int j = 0; j < n; j++) {
					if(board[i][j] == '0') {
						board[i][j] = 'X';
					} else if(board[i][j] == '#') {
						board[i][j] = '0';
					}
				}
			}
		}


		void markBoundary(vector<vector<char>>& board, int i, int j) {
			if(i < 0 || i >= board.size() || j < 0 || j >= board[0].size() || board[i][j] != '0') {
				return;
			}

			board[i][j] = '#';
			markBoundary(board, i - 1, j);
			markBoundary(board, i + 1, j);
			markBoundary(board, i, j - 1);
			markBoundary(board, i, j + 1);
		}
};

int main() {
	int m, n;
	cin >> m >> n;

	vector<vector<char>> board(m, vector<char>(n));

	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			cin >> board[i][j];
		}
	}

	Solution solution;
	solution.solve(board);

	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			cout << board[i][j] << " ";
		}
		cout << endl;
	}

	return 0;
}

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

提示:

  • 1 <= s.length <= 16
  • s 仅由小写英文字母组成
#include<iostream>
#include<vector>
#include<string>
using namespace std;

class Solution {
	public:
		vector<vector<string>> partition(string s) {
			vector<vector<string>> result;
			vector<string> current;
			backtrack(result, current, s, 0);
			return result;
		}
		
		void backtrack(vector<vector<string>>& result, vector<string>& current, string& s, int start) {
			if(start == s.length()) {
				result.push_back(current);
				return;
			}
			
			for(int i = start; i < s.length(); i++) {
				if(isPalindrome(s, start, i)) {
					string substring = s.substr(start, i - start + 1);
					current.push_back(substring);
					backtrack(result, current, s, i + 1);
					current.pop_back();
				}
			}
		}
		
		bool isPalindrome(string& s, int start, int end) {
			while(start < end) {
				if(s[start] != s[end]) {
					return false;
				}
				start++;
				end--;
			}
			
			return true;
		}
};

int main() {
	string s;
	cin >> s;
	
	Solution solution;
	vector<vector<string>> result = solution.partition(s);
	
	cout << "[";
	for(int i = 0; i < result.size(); i++) {
		cout << "[";
		for(int j = 0; j < result[i].size(); j++) {
			if(j == result[i].size() - 1) {
				cout << result[i][j];
			} else {
				cout << result[i][j] << " ";
			}
		}
		
		if(i == result.size() - 1) {
			cout << "]";
		} else {
			cout << "], ";
		}
	}	
	cout << "]";
	
	return 0;
}

132. 分割回文串Ⅱ

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的 最少分割次数 。

示例 1:

输入:s = "aab"
输出:1
解释:只需一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。

示例 2:

输入:s = "a"
输出:0

示例 3:

输入:s = "ab"
输出:1

提示:

  • 1 <= s.length <= 2000
  • s 仅由小写英文字母组成
#include<iostream>
#include<vector> 
using namespace std;

class Solution {
	public:
		int minCut(string s) {
			int n = s.size();
			vector<vector<bool>> isPalindrome(n, vector<bool>(n, false));
			vector<int> dp(n, 0);
			
			for(int i = 0; i < n; i++) {
				int minCut = i;
				for(int j = 0; j <= i; j++) {
					if(s[j] == s[i] && (i - j <= 2 || isPalindrome[j + 1][i - 1])) {
						isPalindrome[j][i] = true;
						minCut = (j == 0) ? 0 : min(minCut, dp[j - 1] + 1);
					}
				}
				dp[i] = minCut; // 将字符串s的前i+1个字符分割成回文子串的最少次数 
			}
			
			return dp[n - 1];
		}
};
a

int main() {
	string s;
	cin >> s;
	
	Solution solution;
	cout << solution.minCut(s) << endl;
	
	return 0;
}

133. 克隆图

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

图中的每个节点都包含它的值 valint) 和其邻居的列表(list[Node])。

class Node {
    public int val;
    public List<Node> neighbors;
}

测试用例格式:

简单起见,每个节点的值都和它的索引相同。例如,第一个节点值为 1(val = 1),第二个节点值为 2(val = 2),以此类推。该图在测试用例中使用邻接列表表示。

邻接列表 是用于表示有限图的无序列表的集合。每个列表都描述了图中节点的邻居集。

给定节点将始终是图中的第一个节点(值为 1)。你必须将 给定节点的拷贝 作为对克隆图的引用返回。

示例 1:

输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
输出:[[2,4],[1,3],[2,4],[1,3]]
解释:
图中有 4 个节点。
节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
节点 4 的值是 4,它有两个邻居:节点 1 和 3 。

示例 2:

输入:adjList = [[]]
输出:[[]]
解释:输入包含一个空列表。该图仅仅只有一个值为 1 的节点,它没有任何邻居。

示例 3:

输入:adjList = []
输出:[]
解释:这个图是空的,它不含任何节点。

提示:

  • 这张图中的节点数在 [0, 100] 之间。
  • 1 <= Node.val <= 100
  • 每个节点值 Node.val 都是唯一的,
  • 图中没有重复的边,也没有自环。
  • 图是连通图,你可以从给定节点访问到所有节点。
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;

class Node {
	public:
		int val;
		vector<Node*> neighbors;
		Node() {
			val = 0;
			neighbors = vector<Node*>();
		}
		Node(int _val) {
			val = _val;
			neighbors = vector<Node*>();
		}
		Node(int _val, vector<Node*> _neighbors) {
			val = _val;
			neighbors = _neighbors;
		}
};

class Solution {
	public:
		Node* cloneGraph(Node* node) {
			if(!node) {
				return NULL;
			}
			
			// 存储已经访问过的节点及其对应的克隆节点 
			unordered_map<Node*, Node*> visited;
			
			return cloneNode(node, visited);
		}
		
		// 对于每个节点,我们递归的克隆其邻居节点,并将克隆节点添加到当前节点的邻居列表中,最后返回克隆图的起始节点 
		Node* cloneNode(Node* node, unordered_map<Node*, Node*>& visited) {
			if(visited.find(node) != visited.end()) {
				return visited[node];
			}
			
			Node* newNode = new Node(node->val);
			visited[node] = newNode;
			
			for(Node* neighbor : node->neighbors) {
				newNode->neighbors.push_back(cloneNode(neighbor, visited));
			}
			
			return newNode;
		}
};




int main() {
    // 从控制台输入测试用例
    vector<vector<int>> adjList = {{2, 4}, {1, 3}, {2, 4}, {1, 3}};
    
    // 创建图
    vector<Node*> nodes;
    for (int i = 0; i < adjList.size(); ++i) {
        nodes.push_back(new Node(i + 1));
    }
    
    for (int i = 0; i < adjList.size(); ++i) {
        for (int j = 0; j < adjList[i].size(); ++j) {
            nodes[i]->neighbors.push_back(nodes[adjList[i][j] - 1]);
        }
    }
    
    // 深度拷贝图
    Solution solution;
    Node* clonedNode = solution.cloneGraph(nodes[1]);

    // 输出克隆图的邻居节点值
    for (Node* neighbor : clonedNode->neighbors) {
        cout << neighbor->val << " ";
    }
    
    return 0;
}

134. 加油站

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

示例 1:

输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

示例 2:

输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

提示:

  • gas.length == n
  • cost.length == n
  • 1 <= n <= 10^5
  • 0 <= gas[i], cost[i] <= 10^4
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
			int total_tank = 0;
			int curr_tank = 0;
			int starting_station = 0;
			
			for(int i = 0; i < gas.size(); i++) {
				total_tank += gas[i] - cost[i];
				curr_tank += gas[i] - cost[i];
				
				if(curr_tank < 0) {
					starting_station = i + 1;
					curr_tank = 0;
				}
			}
			
			return total_tank >= 0 ? starting_station : -1; 
		}
};

int main() {
	vector<int> gas, cost;
	int tmp;
	
	while(cin >> tmp) {
		gas.push_back(tmp);
		if(cin.get() == '\n') {
			break;
		}
	}
	
	while(cin >> tmp) {
		cost.push_back(tmp);
		if(cin.get() == '\n') {
			break;
		}
	}
	
	Solution solution;
	cout << solution.canCompleteCircuit(gas, cost);
	
	return 0;
}

135. 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

提示:

  • n == ratings.length
  • 1 <= n <= 2 * 10^4
  • 0 <= ratings[i] <= 2 * 10^4
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		int candy(vector<int>& ratings) {
			int n = ratings.size();
			vector<int> candies(n, 1);
			
			for(int i = 1; i < n; i++) {
				if(ratings[i] > ratings[i - 1]) {
					candies[i] = candies[i - 1] + 1;
				}
			}
			
			for(int i = n - 2; i >= 0; i--) {
				if(ratings[i] > ratings[i + 1]) {
					candies[i] = max(candies[i], candies[i + 1] + 1);
				}
			}
			
			int total = 0;
			for(int candy : candies) {
				total += candy;
			}
			
			return total;
		} 
};

int main() {
	vector<int> ratings;
	int tmp;
	
	while(cin >> tmp) {
		ratings.push_back(tmp);
		if(cin.get() == '\n') {
			break;
		}
	}
	
	Solution solution;
	cout << solution.candy(ratings) << endl;
	
	return 0;
}

136. 只出现一次的数字

给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1 :

输入:nums = [2,2,1]
输出:1

示例 2 :

输入:nums = [4,1,2,1,2]
输出:4

示例 3 :

输入:nums = [1]
输出:1

提示:

  • 1 <= nums.length <= 3 * 10^4
  • -3 * 10^4 <= nums[i] <= 3 * 10^4
  • 除了某个元素只出现一次以外,其余每个元素均出现两次。
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		int singleNumber(vector<int>& nums) {
			int result = 0;
			for(int i = 0; i < nums.size(); i++) {
				result ^= nums[i];  // 相同为0,相异为1 
			}
			return result;
		}
};

int main() {
	int temp;
	vector<int> nums;
	while(cin >> temp) {
		nums.push_back(temp);
		if(cin.get() == '\n')
			break;
	}
	Solution solution;
	cout << solution.singleNumber(nums);
	return 0;
}
#include<iostream>
#include<vector>
#include<numeric>
using namespace std;

class Solution {
	public:
		int singleNumber(vector<int>& nums) {
			return accumulate(nums.begin(),nums.end(),0,bit_xor<int>());
		}
};

int main() {
	int temp;
	vector<int> nums;
	while(cin >> temp) {
		nums.push_back(temp);
		if(cin.get() == '\n')
			break;
	}
	Solution solution;
	cout << solution.singleNumber(nums);
	return 0;
}

137. 只出现一次的数Ⅱ

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

示例 1:

输入:nums = [2,2,3,2]
输出:3

示例 2:

输入:nums = [0,1,0,1,0,1,99]
输出:99

提示:

  • 1 <= nums.length <= 3 * 10^4
  • -2^31 <= nums[i] <= 2^31 - 1
  • nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
#include<iostream>
#include<vector>
using namespace std;

class Solution {
	public:
		int singleNumber(vector<int>& nums) {
			int seen_once = 0;
			int seen_twice = 0;
			
			for(int num : nums) {
				seen_once = ~seen_twice & (seen_once ^ num); // 当前元素num出现的次数为1次 
				seen_twice = ~seen_once & (seen_twice ^ num);  // 当前元素num出现的次数为2次 
			}
			
			return seen_once;
		}
};

int main() {
	vector<int> nums;
	int tmp;
	
	while(cin >> tmp) {
		nums.push_back(tmp);
		if(cin.get() == '\n')
			break;
	}
	
	Solution solution;
	cout << solution.singleNumber(nums);
	
	return 0;
}

138. 随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码  接受原链表的头节点 head 作为传入参数。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -10^4 <= Node.val <= 10^4
  • Node.random 为 null 或指向链表中的节点。
#include<iostream>
#include<unordered_map>
using namespace std;

// Definition for a node
class Node {
	public:
		int val;
		Node* next;
		Node* random;
		
		Node(int _val) {
			val = _val;
			next = NULL;
			random = NULL;
		}
};

class Solution {
	public:
		Node* copyRandomList(Node* head) {
			if(!head) {
				return NULL;
			}
			
			unordered_map<Node*, Node*> nodeMap;
			Node* cur = head;
			
			// 第一遍循环,复制每个结点的值并创建新结点
			while(cur) {
				nodeMap[cur] = new Node(cur->val);
				cur = cur->next;
			} 
			
			cur = head;
			// 第二遍循环,连接新结点的next和random指针
			while(cur) {
				nodeMap[cur]->next = nodeMap[cur->next];
				nodeMap[cur]->random = nodeMap[cur->random];
				cur = cur->next;
			} 
			
			return nodeMap[head];
		}
};

/*
Node* createList() {
	Node* head = NULL;
	Node* current = NULL;
	int val;
	
	while(cin >> val) {
		Node* newNode = new Node(val);
		if(head == NULL) {
			head = newNode;
			current = newNode;
 		} else {
 			current->next = newNode;
 			current = current->next;
		}
		if(cin.get() == '\n') {
			break;
		} 
	}
	
	
	return head;
}*/

int main() {
	Node* node1 = new Node(7);
	Node* node2 = new Node(13);
	Node* node3 = new Node(11);
	Node* node4 = new Node(10);
	Node* node5 = new Node(1);
	
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = node5;
	
	node1->random = NULL;
	node2->random = node1;
	node3->random = node5;
	node4->random = node3;
	node5->random = node1;
	
	Solution solution;
	Node* copiedHead = solution.copyRandomList(node1);
	
	return 0;
}

139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同
#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
	public:
		bool wordBreak(string s, vector<string>& wordDict) {
			unordered_set<string> dict(wordDict.begin(), wordDict.end());
			
			int n = s.size();
			// dp[i]表示s的前i个字符是否可以被拆分成字典中的单词
			vector<bool> dp(n + 1, false);
			dp[0] = true;
			
			for(int i = 1; i <= n; i++) {
				for(int j = 0; j < i; j++) {
					if(dp[j] && dict.count(s.substr(j, i - j))) {
						dp[i] = true;
						break;
					}
				}
			}
			
			return dp[n];
		}
};

int main() {
	string s, tmp;
	vector<string> wordDict;
	
	cin >> s;
	while(cin >> tmp) {
		wordDict.push_back(tmp);
		if(cin.get() == '\n')	
			break;
	}
	
	Solution solution;
	cout << boolalpha << solution.wordBreak(s, wordDict) << endl;
	
	return 0;
}

140. 单词拆分Ⅱ

给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

注意:词典中的同一个单词可能在分段中被重复使用多次。

示例 1:

输入:s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
输出:["cats and dog","cat sand dog"]

示例 2:

输入:s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"]
输出:["pine apple pen apple","pineapple pen apple","pine applepen apple"]
解释: 注意你可以重复使用字典中的单词。

示例 3:

输入:s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
输出:[]

提示:

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s 和 wordDict[i] 仅有小写英文字母组成
  • wordDict 中所有字符串都 不同
#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
	public:
		vector<string> wordBreak(string s, vector<string>& wordDict) {
			unordered_set<string> dict(wordDict.begin(), wordDict.end());
			vector<string> result;
			backtrack(s, 0, wordDict, dict, "", result);
			
			return result;
		}
		
		void backtrack(string s, int start, vector<string>& wordDict, unordered_set<string>& dict, string path, vector<string>& result) {
			if(start == s.length()) {
				result.push_back(path);
				return;
			}
			
			for(int i = start; i < s.length(); i++) {
				string word = s.substr(start, i - start + 1);
				if(dict.count(word)) {
					string newPath = path.empty() ? word : path + " " + word;
					backtrack(s, i + 1, wordDict, dict, newPath, result);
				}
			}
		}
};

int main() {
	string s, tmp;
	vector<string> wordDict;
	
	cin >> s;
	while(cin >> tmp) {
		wordDict.push_back(tmp);
		if(cin.get() == '\n')	
			break;
	}
	
	Solution solution;
	vector<string> result = solution.wordBreak(s, wordDict);
	
	cout << "[";
	for(int i = 0; i < result.size(); i++) {
		if(i == result.size() - 1) {
			cout << "\"" << result[i] << "\"";
		} else {
			cout << "\"" << result[i] << "\", ";
		}
	}
	cout << "]";
	
	return 0;
}

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos 为 -1 或者链表中的一个 有效索引 。
#include<iostream>
#include<cctype>
using namespace std;

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
	public:
		bool hasCycle(ListNode *head) {  // 快慢指针法
			ListNode *slow = head;
			ListNode *fast = head;
			
			while(fast != NULL && fast->next != NULL) {
				slow = slow->next;
				fast = fast->next->next;
				if(slow == fast) 
					return true;
			}
			return false;
		}
};

ListNode* buildLinkedList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	while(cin >> val) {
		if(head == NULL) {
			head = new ListNode(val);
			current = head;
		} else{
			current->next = new ListNode(val);
			current = current->next;
		}
		if(cin.get() == '\n') 
			break;
	}
	return head;
}

int main() {
	ListNode* head = buildLinkedList();;
	Solution solution;
	cout << boolalpha << solution.hasCycle(head);
	return 0;
}

142. 环形链表Ⅱ

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -10^5 <= Node.val <= 10^5
  • pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

#include<iostream>
#include<cctype>
using namespace std;

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
	public:
		ListNode *detectCycle(ListNode *head) {
			if(head == NULL || head->next == NULL) {
				return NULL;
			}
			ListNode *slow = head;
			ListNode *fast = head;
			while(fast != NULL && fast->next != NULL) {
				slow = slow->next;
				fast = fast->next->next;
				if(slow == fast) {
					ListNode *slow2 = head;
					while(slow2 != slow) {
						slow = slow->next;
						slow2 = slow2->next;
					}
					return slow;
				}
			}
			return NULL;
		}
};

ListNode* buildLinkedList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	while(cin >> val) {
		if(head == NULL) {
			head = new ListNode(val);
			current = head;
		} else{
			current->next = new ListNode(val);
			current = current->next;
		}
		if(cin.get() == '\n') 
			break;
	}
	return head;
}

int main() {
	ListNode* head = buildLinkedList();;
	Solution solution;
	ListNode *cycleStart = solution.detectCycle(head);
	if(cycleStart != NULL) 
		cout << cycleStart->val;
	else
		cout << "null";
	return 0;
}

143. 重排链表

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示:

  • 链表的长度范围为 [1, 5 * 10^4]
  • 1 <= node.val <= 1000
#include<iostream>
#include<vector>
using namespace std;

// definition for singly-linked list
struct ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(NULL) {}
	ListNode(int x) : val(x), next(NULL) {}
	ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
	public:
		void reorderList(ListNode* head) {
			if(head == NULL || head->next == NULL) 
				return;
			
			vector<ListNode*> nodes;
			ListNode* current = head;
			while(current != NULL) {
				nodes.push_back(current);
				current = current->next;
			}
			int left = 0; 
			int right = nodes.size() - 1;
			while(left < right) {
				nodes[left]->next = nodes[right];
				left++;
				if(left == right) 
					break;
				nodes[right]->next = nodes[left];
				right--;
			}
			nodes[left]->next = NULL;
		}
};

ListNode* buildLinkedList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	while(cin >> val) {
		if(head == NULL) {
			head = new ListNode(val);
			current = head;
		} else{
			current->next = new ListNode(val);
			current = current->next;
		}
		if(cin.get() == '\n') 
			break;
	}
	return head;
}

int main() {
	ListNode* head = buildLinkedList();;
	Solution solution;
	solution.reorderList(head);
	ListNode *current = head;
	while(current != NULL) {
		cout << current->val << " ";
		current = current->next;
	}
	return 0;
}

144.  二叉树的前序遍历

 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

输入:root = [1,2]
输出:[1,2]

示例 5:

输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?

解法一:递归

#include<iostream>
#include<vector>
#include<queue>
using namespace std;


//Definition for a binary tree node.
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		vector<int> preorderTraversal(TreeNode* root) {
			vector<int> result;
			if(root == NULL)
				return result;
			vector<int> left = preorderTraversal(root->left);
			vector<int> right = preorderTraversal(root->right);
			result.push_back(root->val);
			result.insert(result.end(), left.begin(), left.end());
			result.insert(result.end(), right.begin(), right.end());
			return result;
		}
};

// Function to create a binary tree from user input
TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    if(rootVal == -1)
    	return NULL;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	Solution solution;
	vector<int> result = solution.preorderTraversal(root);
	cout << "[";
	for(int i = 0; i < result.size() - 1; i++) {
		cout << result[i] << ", ";
	}
	cout << result[result.size() - 1];
	cout << "]";
	return 0;
}

解法二:迭代

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;

// Definition for a binary tree node
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		vector<int> preorderTraversal(TreeNode* root) {
			vector<int> result;
			if(root == NULL)
				return result;
			stack<TreeNode*> st;
			st.push(root);
			
			while(!st.empty()) {
				TreeNode* curr = st.top();
				st.pop();
				result.push_back(curr->val);
				if(curr->right != NULL) {  // 后进先出 
					st.push(curr->right);
				} 
				if(curr->left != NULL) {
					st.push(curr->left);
				}
			}
			return result;
		}
};

// Function to create a binary tree from user input
TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    if(rootVal == -1)
    	return NULL;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	Solution solution;
	vector<int> result = solution.preorderTraversal(root);
	cout << "[";
	for(int i = 0; i < result.size() - 1; i++) {
		cout << result[i] << ", ";
	}
	cout << result[result.size() - 1];
	cout << "]";
	return 0;
}

145. 二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

提示:

  • 树中节点的数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?

解法一:递归

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;

// Definition for a binary tree node
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		vector<int> postorderTraversal(TreeNode* root) {
			vector<int> result;
			if(root == NULL)
				return result;
			vector<int> left = postorderTraversal(root->left);
			vector<int> right = postorderTraversal(root->right);
			result.insert(result.end(), left.begin(), left.end());
			result.insert(result.end(), right.begin(), right.end());
			result.push_back(root->val);
			return result;
		}
};

// Function to create a binary tree from user input
TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    if(rootVal == -1)
    	return NULL;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	Solution solution;
	vector<int> result = solution.postorderTraversal(root);
	cout << "[";
	for(int i = 0; i < result.size(); i++) {
		if(i == result.size() - 1) {
			cout << result[i];
		} else{
			cout << result[i] << ", ";
		}
	}
	
	cout << "]";
	return 0;
}

解法二:迭代

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;

// Definition for a binary tree node
struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode() : val(0), left(NULL), right(NULL) {}
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
	public:
		vector<int> postorderTraversal(TreeNode* root) {
			vector<int> result;
			if(root == NULL)
				return result;
			stack<TreeNode*> st;
			st.push(root);
			stack<int> output;  // 用于存储后序遍历的结果 
			
			while(!st.empty()) {
				TreeNode* curr = st.top();
				st.pop();
				output.push(curr->val);  // 将节点值存入output栈 
				
				if(curr->left != NULL) {   
					st.push(curr->left);
				} 
				if(curr->right != NULL) {
					st.push(curr->right);
				}
			}
			// 从output栈中将结果取出,即为后序遍历的倒序结果 
			while(!output.empty()) {
				result.push_back(output.top());
				output.pop();
			}
			return result;
		}
};

// Function to create a binary tree from user input
TreeNode* createBinaryTree() {
    cout << "Enter the value of the root node: ";
    int rootVal;
    cin >> rootVal;
    if(rootVal == -1)
    	return NULL;
    TreeNode* root = new TreeNode(rootVal);
    
    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        TreeNode* curr = q.front();
        q.pop();

        cout << "Enter the left child of '" << curr->val << "' (or -1 if none): ";
        int leftVal;
        cin >> leftVal;
        if (leftVal != -1) {
            curr->left = new TreeNode(leftVal);
            q.push(curr->left);
        }

        cout << "Enter the right child of '" << curr->val << "' (or -1 if none): ";
        int rightVal;
        cin >> rightVal;
        if (rightVal != -1) {
            curr->right = new TreeNode(rightVal);
            q.push(curr->right);
        }
    }

    return root;
}

int main() {
	TreeNode *root = createBinaryTree();
	Solution solution;
	vector<int> result = solution.postorderTraversal(root);
	cout << "[";
	for(int i = 0; i < result.size(); i++) {
		if(i == result.size() - 1) {
			cout << result[i];
		} else{
			cout << result[i] << ", ";
		}
	}
	
	cout << "]";
	return 0;
}

146. LRU缓存

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

提示:

  • 1 <= capacity <= 3000
  • 0 <= key <= 10000
  • 0 <= value <= 10^5
  • 最多调用 2 * 10^5 次 get 和 put
#include<iostream>
#include<unordered_map>
#include<list>
using namespace std;

class LRUCache {
	private:
		int capacity;
		unordered_map<int, pair<int, list<int>::iterator>> cache;  // key -> (value, iterator)
		list<int> keys;  // 保存所有的key,最近使用的在前面
		
	public:
		LRUCache(int capacity) {
			this->capacity = capacity;
		} 
		
		int get(int key) {
			if(cache.find(key) == cache.end()) {
				return -1;
			}
			
			// 将当前访问的key移动到keys的最前面
			keys.erase(cache[key].second);
			keys.push_back(key);
			cache[key].second = keys.begin();
			
			return cache[key].first; 
		}
		
		void put(int key, int value) {
			if(cache.find(key) != cache.end()) {
				// key已存在,更新value并将其移动到keys的最前面
				keys.erase(cache[key].second); 
			} else {
				if(cache.size() >= capacity) {
					// 缓存已满,移除最近最少使用的key
					int leastUsedKey = keys.back();
					keys.pop_back();
					cache.erase(leastUsedKey); 
				}
			}
			
			keys.push_front(key);
			cache[key] = {value, keys.begin()};
		}
};

int main() {
	LRUCache* obj = new LRUCache(2);
	obj->put(1, 1);
	obj->put(2, 2);
	cout << obj->get(1) << endl;
	obj->put(3, 3);
	cout << obj->get(2) << endl;
	obj->put(4, 4);
	cout << obj->get(1) << endl;
	cout << obj->get(3) << endl;
	cout << obj->get(4) << endl;
	
	
	return 0;
}

147. 对链表进行插入排序

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

示例 1:

输入: head = [4,2,1,3]
输出: [1,2,3,4]

示例 2:

输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]

提示:

  • 列表中的节点数在 [1, 5000]范围内
  • -5000 <= Node.val <= 5000
#include<iostream>
using namespace std;

// definition for singly-linked list
struct ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(NULL) {}
	ListNode(int x) : val(x), next(NULL) {}
	ListNode(int x, ListNode* next) : val(x), next(next) {}
};

class Solution {
	public:
		ListNode* insertionSortList(ListNode* head) {
			if(!head || !head->next) {
				return head;
			}
			
			ListNode* dummy = new ListNode(0);
			dummy->next = head;
			
			ListNode* cur = head;  // 当前节点
			ListNode* prev = dummy;  // 当前节点的前一个节点
			
			while(cur) {
				if(cur->next && cur->next->val < cur->val) {
					// 如果下一个节点的值小于当前节点的值,则需要将下一个节点插入到合适的位置
					while(prev->next && prev->next->val < cur->next->val) {
						prev = prev->next;
					} 
					
					// 将下一个节点插入到合适的位置
					ListNode* temp = prev->next;
					prev->next = cur->next;
					cur->next = cur->next->next;
					prev->next->next = temp;
					
					prev = dummy;  // 重置prev为虚拟头节点 
				} else {
					// 下一个节点的值不小于当前节点的值,则继续向后遍历 
					cur = cur->next;
				} 
			} 
			
			return dummy->next; 
		}
};

ListNode* createList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	
	while(cin >> val) {
		ListNode* newNode = new ListNode(val);
		if(head == NULL) {
			head = newNode;
			current = newNode;
 		} else {
 			current->next = newNode;
 			current = current->next;
		}
		if(cin.get() == '\n') {
			break;
		} 
	}
	
	return head;
}

int main() {
	ListNode* head = createList();
	
	Solution solution;
	ListNode* res = solution.insertionSortList(head);
	
	while(res) {
		cout << res->val << " ";
		res = res->next;
	}
	
	return 0;
}

148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 10^4] 内
  • -10^5 <= Node.val <= 10^5

进阶:你可以在 O(nlogn) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

解法一:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

// definition for singly-linked list
struct ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(NULL) {}
	ListNode(int x) : val(x), next(NULL) {}
	ListNode(int x, ListNode* next) : val(x), next(next) {}
};

class Solution {
	public:
		ListNode* sortList(ListNode* head) {
			// 将链表的值提取到一个vector中,对其进行排序,然后再构建一个新的排好序的链表 
			if(!head || !head->next) {
				return head;
			}
			
			vector<int> values;
			ListNode* current = head;
			
			while(current) {
				values.push_back(current->val);
				current = current->next;
			}
			
			sort(values.begin(), values.end());
			
			ListNode* newHead = new ListNode(values[0]);
			ListNode* temp = newHead;
			
			for(int i = 1; i < values.size(); i++) {
				temp->next = new ListNode(values[i]);
				temp = temp->next;
			}
			
			return newHead;
		}
};

ListNode* createList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	
	while(cin >> val) {
		ListNode* newNode = new ListNode(val);
		if(head == NULL) {
			head = newNode;
			current = newNode;
 		} else {
 			current->next = newNode;
 			current = current->next;
		}
		if(cin.get() == '\n') {
			break;
		} 
	}
	
	return head;
}

int main() {
	ListNode* head = createList();
	
	Solution solution;
	ListNode* res = solution.sortList(head);
	
	while(res) {
		cout << res->val << " ";
		res = res->next;
	}
	
	return 0;
}

解法二:合并排序

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

// definition for singly-linked list
struct ListNode {
	int val;
	ListNode *next;
	ListNode() : val(0), next(NULL) {}
	ListNode(int x) : val(x), next(NULL) {}
	ListNode(int x, ListNode* next) : val(x), next(next) {}
};

class Solution {
	public:
		ListNode* sortList(ListNode* head) {
			if(!head || !head->next) {
				return head;
			}
			
			ListNode *middle = findMiddle(head);
			ListNode* secondHalf = middle->next;
			middle->next = NULL;
			
			ListNode* sortedFirstHalf = sortList(head);
			ListNode* sortedSecondHalf = sortList(secondHalf);
			
			return merge(sortedFirstHalf, sortedSecondHalf);
		}
		
		ListNode* findMiddle(ListNode* head) {
			// 快慢指针法 
			ListNode* slow = head;
			ListNode* fast = head->next;
			
			while(fast && fast->next) {
				slow = slow->next;
				fast = fast->next->next;
			}
			
			return slow;
		}
		
		ListNode* merge(ListNode* l1, ListNode* l2) {
			ListNode dummy(0);
			ListNode* current = &dummy;
			
			while(l1 && l2) {
				if(l1->val < l2->val) {
					current->next = l1;
					l1 = l1->next;
				} else {
					current->next = l2;
					l2 = l2->next;
				}
				current = current->next;
			}
			
			// 处理剩下的节点 
			current->next = l1 ? l1 : l2;
			
			return dummy.next;
		}
};

ListNode* createList() {
	ListNode* head = NULL;
	ListNode* current = NULL;
	int val;
	
	while(cin >> val) {
		ListNode* newNode = new ListNode(val);
		if(head == NULL) {
			head = newNode;
			current = newNode;
 		} else {
 			current->next = newNode;
 			current = current->next;
		}
		if(cin.get() == '\n') {
			break;
		} 
	}
	
	return head;
}

int main() {
	ListNode* head = createList();
	
	Solution solution;
	ListNode* res = solution.sortList(head);
	
	while(res) {
		cout << res->val << " ";
		res = res->next;
	}
	
	return 0;
}

149. 直线上最多的点

给你一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。

示例 1:

输入:points = [[1,1],[2,2],[3,3]]
输出:3

示例 2:

输入:points = [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出:4

提示:

  • 1 <= points.length <= 300
  • points[i].length == 2
  • -10^4 <= xi, yi <= 10^4
  • points 中的所有点 互不相同
#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>
using namespace std;

class Solution {
	public:
		int maxPoints(vector<vector<int>>& points) {
			int n = points.size();
			if(n <= 2) {
				return n;
			}

			int maxPointsOnLine = 2;

			for(int i = 0; i < n; i++) {
				unordered_map<string, int> slopeCount;
				int duplicatePoints = 0;
				int localMaxPoints = 1;

				for(int j = i + 1; j < n; j++) {
					int dx = points[j][0] - points[i][0];
					int dy = points[j][1] - points[i][1];

					if(dx == 0 && dy == 0) {
						// 重复的点
						duplicatePoints++;
						continue;
					}

					int gcdValue = gcd(dx, dy);
					// 斜率
					// (计算斜率时,要考虑斜率的精度问题,因为浮点数计算可能会导致精度误差,为了解决这个问题,我们可以使用最大公约数来表示斜率,以避免精度误差)
					string slope = to_string(dx / gcdValue) + "_" + to_string(dy / gcdValue);
					// 斜率相同,即在同一条直线上
					slopeCount[slope]++;
					localMaxPoints = max(localMaxPoints, slopeCount[slope] + 1);
				}

				maxPointsOnLine = max(maxPointsOnLine, localMaxPoints + duplicatePoints);
			}

			return maxPointsOnLine;
		}

		int gcd(int a, int b) {
			return b == 0 ? a : gcd(b, a % b);
		}
};

int main() {
	vector<vector<int>> points;
	int x, y;

	while(cin >> x) {
		cin >> y;
		points.push_back({x, y});
		if(cin.get() == '\n') {
			break;
		}
	}

	Solution solution;
	cout << solution.maxPoints(points) << endl;

	return 0;
}

150. 逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

提示:

  • 1 <= tokens.length <= 104
  • tokens[i] 是一个算符("+""-""*" 或 "/"),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式:

逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
#include<iostream>
#include<vector>
#include<stack>
#include<string>
using namespace std;

class Solution {
	public:
		int evalRPN(vector<string>& tokens) {
			stack<int> st;
			
			for(string token : tokens) {
				if(token == "+" || token == "-" || token == "*" || token == "/") {
					int num2 = st.top();
					st.pop();
					int num1 = st.top();
					st.pop();
					
					if(token == "+") {
						st.push(num1 + num2);
					} else if(token == "-") {
						st.push(num1 - num2);
					} else if(token == "*") {
						st.push(num1 * num2);
					} else if(token == "/") {
						st.push(num1 / num2);
					}
				} else {
					st.push(stoi(token));
				}
			}
			
			return st.top();
		}
}; 

int main() {
	vector<string> tokens;
	string s;
	
	while(cin >> s) {
		tokens.push_back(s);
		if(cin.get() == '\n') {
			break;
		}
	}
	
	Solution solution;
	cout << solution.evalRPN(tokens) << endl;
	
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用PowerPoint创建毛笔字书写动画

先看看下面这个毛笔字书写动画&#xff1a; 这个动画是用PowerPoint创建的。下面介绍创建过程。 1、在任何一款矢量图片编辑软件中创建一个图片&#xff0c;用文字工具输入文字内容。我用的是InkScape。排好版后将图片保存为.svg格式的矢量图片文件。 2、打开PowerPoint&…

强一致性的皇冠:分布式事务模型的至高法则揭秘

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 分布式事务模型是分布式系统设计的核心&#xff0c;关键在于保证数据一致性和事务完整性&#xff0c;尤其强调强一致性。诸如2PC、3PC、Saga、TCC等模型与协议&#xff0c;应运而生以解…

elementui+vue通过下拉框多选字段进行搜索模糊匹配

从字典中选择的值为["01","03"],在最开始的时候进行的处理是类似于表单提交的时候将json对象转换成了String类型 nature:["01","03"] this.queryParams.nature JSON.stringify(this.queryParams.nature); mapper层 <if test&quo…

keystone学习小结

1 keystone middleware 1.1 工作流程 middleware在客户端和服务端之间&#xff0c;会拦截客户端请求并判断请求身份是否是正确合法的&#xff0c;若是&#xff0c;则继续将请求发给其他middleware或app 具体看&#xff0c;干了这些事 1将请求里的auth header去除&#xff0c…

【FL常用插件#1】Ozone11臭氧的安装和使用

本文内容收集自互联网&#xff0c;仅供个人学习参考使用&#xff0c;不允许用于商业用途&#xff0c;造成的侵权行为与本文作者无关 安装 VST2、VST3、AAX和NKS是音频技术界常见的几种插件格式&#xff0c;它们在功能和兼容性上有所不同&#xff1a; VST2 (Virtual Studio Tec…

QT--2

Qt界面设计 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//窗口相关设置this->resize(680,520);this->setFixedSize(680,520);this->setWindowTitle("Tim");this->setWindowFla…

ES集群设置访问密码

1 新增配置 每个集群节点都需要设置&#xff0c;编辑 elasticsearch.yml 文件&#xff1a; cd /home/isearch/es vi es-cluster-01/config/elasticsearch.yml vi es-cluster-02/config/elasticsearch.yml vi es-cluster-03/config/elasticsearch.yml # 设置启用了X-Pack安全…

Linux专栏10:Linux权限详解(上)

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux权限详解&#xff08;上&#xff09; 编号&#xff1a;10 文章…

CANdela/Diva系列1--CANdela Studio的基本介绍

大家好&#xff0c;这个系列主要给大家介绍跟诊断相关的Vector 工具CANdela和Diva&#xff0c;首先介绍CANdela。 目录 1.CANdela的简介&#xff1a; 2.如何打开CANdela 工程&#xff1a; 3.CANdela工程的详细介绍&#xff1a; 3.1 工具栏的介绍&#xff1a; 3.2 工作树的…

HarmonyOS开发案例:【电子相册】

介绍 如何实现一个简单的电子相册应用的开发&#xff0c;主要功能包括&#xff1a; 实现首页顶部的轮播效果。 实现页面跳转时共享元素的转场动画效果。 实现通过手势控制图片的放大、缩小、左右滑动查看细节等效果。 相关概念 [Swiper]&#xff1a;滑块视图容器&#x…

软件系统测试方案书(测试计划-Word原件)

2 引言 2.1 编写目的 2.3 测试人员 2.4 项目背景 2.5 测试目标 2.6 简写和缩略词 2.7 参考资料 2.8 测试提交文档 2.9 测试进度 3 测试环境 3.1 软硬件环境 4 测试工具 5 测试策略 5.1 测试阶段划分及内容 5.1.1 集成测试 5.1.2 系统测试 5.1.2.1 功能测试 5.…

通过mask得到bbox(numpy实现)

在SAM的加持下&#xff0c;我们很容易得到物体的mask&#xff0c;但是物体的bbox信息通常也很有用。那么&#xff0c;我们可以写一个函数&#xff0c;立马可以通过mask得到bbox。 代码如下&#xff1a; import numpy as npdef mask2bbox(mask):nonzero_indices np.nonzero(m…

阿里云CentOS 7.9 64位 Liunx 安装redis

具体的步骤如下&#xff1a; 添加 EPEL 仓库&#xff0c;因为 Redis 在标准的 CentOS 仓库中不可用&#xff1a; sudo yum install epel-release安装 Redis&#xff1a; sudo yum install redis启动 Redis 服务&#xff1a; sudo systemctl start redis如果你想让 Redis 在…

python中numpy库使用

array数组 生成array数组 将list转化为array数组 import numpy as np np.array([1,2],typenp.int32)其中dtype定义的是元素类型&#xff0c;np.int32指32位的整形 如果直接定义dtypeint 默认的是32位整形。 zeors和ones方法 zeros()方法&#xff0c;该方法和ones()类似&a…

Unity EventSystem入门

概述 相信在学习Unity中&#xff0c;一定有被UI事件困扰的时候把&#xff0c;当添加UICanvas的时候&#xff0c;Unity会为我们自动添加EventSystem&#xff0c;这个是为什么呢&#xff0c;Unity的UI事件是如何处理的呢&#xff0c;在使用各个UI组件的时候&#xff0c;一定有不…

35 信号处理

什么时候捕捉 如果信号的处理动作是用户自定义函数&#xff0c;在信号递达时就调用这个函数&#xff0c;称为捕捉信号&#xff0c;由于信号处理函数的代码是在用户空间的&#xff0c;处理过程比较复杂&#xff0c;举例如下&#xff1a;用户程序注册了SIGQUIT信号的处理函数sig…

【C++】C++11--- 类的新功能

目录 类的新功能 默认成员函数 示例 类成员变量初始化 强制生成默认函数的关键字default 禁止生成默认函数的关键字delete 类的新功能 默认成员函数 构造函数析构函数拷贝构造函数拷贝赋值重载取地址重载const取地址重载 C11在原先的6个默认成员函数的基础上&#xff0c…

基于Flask的岗位就业可视化系统(一)

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 前言 本项目综合了基本数据分析的流程&#xff0c;包括数据采集&#xff08;爬虫&#xff09;、数据清洗、数据存储、数据前后端可视化等 推荐…

Redis(Redis配置和订阅发布)

文章目录 1.Redis配置1.网络配置1.配置文件位置 /etc/redis.conf2.bind&#xff08;注销支持远程访问&#xff09;1.默认情况bind 127.0.0.1 只能接受本机的访问2.首先编辑配置文件3.进入命令模式输入/bind定位&#xff0c;输入n查找下一个&#xff0c;shift n查找上一个&…

STM32F407VET6 学习笔记2:定时器、串口、自定义串口打印函数

今日继续学习使用嘉立创购买的 立创梁山派天空星&#xff0c;芯片是 STM32F407VET6 因为已经有学习基础了&#xff0c;所以学习进度十分快&#xff0c;这次也是直接一块学习配置定时器与串口了&#xff0c;文章也愈来愈对基础的解释越来越少了...... 文章提供测试代码讲解、完…