Day1 数组
1、存在重复元素
给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
/**法一:在对数字从小到大排序(使用sort函数)之后,数组的重复元素一定出现在相邻位置中。因此,我们可以扫描已排序的数组,每次判断相邻的两个元素是否相等,如果相等则说明存在重复的元素。
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i = 0;i < n - 1;i++)
{
if(nums[i] == nums[i+1])
{
return true;
}
}
return false;
**/
//法二:哈希表
unordered_map<int, int> map;
for(int i = 0; i < nums.size(); i++){
map[nums[i]]++;
}
for(int j = 0; j < nums.size(); j++){
if(map[nums[j]] != 1){
return true;
}
}
return false;
}
};
2、最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
/**法一:判断前几项之和是否大于0,大于则加,小于舍弃重新开始记录,每次记录最大值,最后返回这个最大值
int sum = nums[0];
int n = nums[0];
for(int i=1;i<nums.size();i++)
{
if(n>0)
{
n += nums[i];
}
else
{
n = nums[i];
}
if(sum<n)
{
sum = n;
}
}
return sum;
**/
//法二:动态规划
int n = nums.size();
vector<int> dp(n);
dp[0] = nums[0];
int res = dp[0];
for(int i = 1; i < n; i++){
if(dp[i - 1] > 0){
dp[i] = dp[i - 1] + nums[i];
}
else{
dp[i] = nums[i];
}
res = max(res, dp[i]);
}
return res;
}
};
Day2 数组
1、两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
/**法一:
int i,j;
for(i=0;i<nums.size()-1;i++)
{
for(j=0;j<nums.size();j++)
{
if(nums[i]+nums[j]==target && i!=j)
return {i,j};
}
}
return {i,j};
**/
//法二:哈希表
unordered_map<int, int> map; //哈希表的键是数组中的值,哈希表的值是数组中的索引下标
for(int i = 0; i < nums.size(); i++){
auto iter = map.find(target - nums[i]);//迭代器访问用first second
if(iter != map.end()){
return {iter->second, i};
}
map[nums[i]] = i; //先找后存 避免nums[i]和自己匹配
}
return {};
}
};
2、合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
/**法一:先将数组nums2放进数组nums1的尾部,然后直接对整个数组进行排序
for(int i=0;i<n;i++)
{
nums1[m+i]=nums2[i];
}
sort(nums1.begin(),nums1.end());
**/
//法二:双指针
int res[m + n];
int left = 0;
int right = 0;
int tmp;
while(left < m || right < n){
if(left == m){
tmp = nums2[right++];
}
else if(right == n){
tmp = nums1[left++];
}
else if(nums1[left] < nums2[right]){
tmp = nums1[left++];
}
else{
tmp = nums2[right++];
}
res[left + right - 1] = tmp;//每循环一次存入数组
}
for(int i = 0; i < m + n; i++){
nums1[i] = res[i];
}
}
};
Day3 数组
1、两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
//以小遍历大的
if(nums1.size() > nums2.size()){
intersect(nums2, nums1);
}
vector<int> res;
unordered_map<int, int> m;
//向哈希表中存储小的数据
for(int num : nums1){
++m[num];//将m中key为num的个数value加1
}
for(int num : nums2){
if(m.count(num)){//如果m中值为num的个数不为0
res.push_back(num);
--m[num];
if(m[num] == 0){
m.erase(num);//删除m中num的值
}
}
}
return res;
}
};
2、买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
class Solution {
public:
int maxProfit(vector<int>& prices) {
/**法一:形式上,对于每组 i 和 j(其中 j>i)我们需要找出max(prices[j]−prices[i])。
int n = (int)prices.size();
int ans = 0;
for(int i = 0;i<n;++i)
{
for(int j = i+1;j<n;++j)
{
ans = max(ans,prices[j] - prices[i]);
}
}
return ans;
**/
//法二:
int inf = 1e9;
int minprice = inf, maxprofit = 0;
for(int price : prices){
maxprofit = max(maxprofit, price - minprice);
minprice = min(minprice, price);
}
return maxprofit;
}
};
Day4 数组
1、重塑矩阵
在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。
给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。
如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
class Solution {
public:
vector<vector<int>> matrixReshape(vector<vector<int>>& mat, int r, int c) {
int m = mat.size();//m行
int n = mat[0].size();//n列
if(m * n != r * c)
{
return mat;
}
vector<vector<int>> ans(r , vector<int> (c));
for(int x = 0;x < m * n;x++)
{
ans[x / c][x % c] = mat[x / n][x % n];
}
return ans;
}
};
2、杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> ret(numRows);//创建二维向量
for(int i = 0;i<numRows;i++)
{
ret[i].resize(i+1);//这里使用resize函数设置大小,即n行共n+1列
ret[i][0] = ret[i][i] = 1;//第i行的第1个和第i个元素始终为1
for(int j = 1;j<i;j++)
{
ret[i][j] = ret[i-1][j] + ret[i-1][j-1]; //第 n 行的第 i 个数等于第 n−1 行的第 i−1 个数和第 i 个数之和
}
}
return ret;
}
};
Day5 数组
1、有效的数独
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 ‘.’ 表示。
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
/*有效的数独满足以下三个条件:
同一个数字在每一行只能出现一次;
同一个数字在每一列只能出现一次;
同一个数字在每一个小九宫格只能出现一次*/
int rows[9][9];
int cols[9][9];
int boxes[3][3][9];//三维数组
memset(rows, 0, sizeof(rows));
memset(cols, 0, sizeof(cols));
memset(boxes, 0, sizeof(boxes));
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
if(board[i][j] != '.'){
int idx = board[i][j] - '0' - 1;//数独从1开始到9
rows[i][idx]++;
cols[j][idx]++;
boxes[i / 3][j / 3][idx]++;
if(rows[i][idx] > 1 || cols[j][idx] > 1 || boxes[i / 3][j / 3][idx] > 1){
return false;
}
}
}
}
return true;
}
};
2、矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();//行数
int n = matrix[0].size();//列数
vector<int> row(m), cols(n);
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(!matrix[i][j]){
row[i] = cols[j] = true;//如果有0元素,将该行和该列全变为1 标记
}
}
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(row[i] || cols[j]){
matrix[i][j] = 0;
}
}
}
}
};
Day6 字符串
1、字符串中的第一个唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
class Solution {
public:
int firstUniqChar(string s) {
unordered_map<char, int> m;
for(char ch : s){
m[ch]++;
}
for(int i = 0; i < s.length(); i++){
if(m[s[i]] == 1){
return i;
}
}
return -1;
}
};
2、 赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<int, int> hash1;
unordered_map<int, int> hash2;
for(int i = 0; i < ransomNote.size(); i++){
hash1[ransomNote[i]]++;
}
for(int j = 0; j < magazine.size(); j++){
hash2[magazine[j]]++;
}
int flag = 0;
for(int a = 0; a < ransomNote.size(); a++){
//1有的2都有甚至更多
if(hash1[ransomNote[a]] <= hash2[ransomNote[a]]){//如果hash2中元素的个数比hash1中元素的个数多,继续下一次循环
continue;
}
else{
flag++;
}
}
bool n = false;
if(flag == 0){
n = true;
}
return n;
}
};
3、有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
class Solution {
public:
bool isAnagram(string s, string t) {
int record[26] = {0};
for(int i = 0;i < s.size(); i++){
record[s[i] - 'a']++;
}
for(int j = 0; j < t.size(); j++){
record[t[j] - 'a']--;
}
for(int x = 0; x < 26; x++){
if(record[x] != 0){
return false;
}
}
return true;
}
};
Day7 链表
1、环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode *> h;
while(head != NULL){
if(h.count(head)){//该函数返回1或0,因为该集合仅包含唯一元素。如果设置的容器中存在该值,则返回1。如果容器中不存在它,则返回0
return true;
}
h.insert(head);
head = head->next;
}
return false;
}
};
2、合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {} //初始化时使用,将x和next赋值给val和next; //val即为第一个值,next为后面的数
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { //merge函数就是把两个有序的链表合并为一个有序的链表
/**法一:
if(l1 ==nullptr)
{
return l2;
}
else if(l2 == nullptr)
{
return l1;
}
else if(l1->val < l2->val)
{
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l1,l2->next);
return l2;
}
**/
//法二:
ListNode *preHead = new ListNode(-1);
ListNode *prev = preHead;
while(l1 != NULL && l2 != NULL){
if(l1->val < l2->val){
prev->next = l1;
l1 = l1->next;
}
else{
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
//循环完之后,最多有一个为非空,直接拼接在后面
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};
3、移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;//虚拟头节点
ListNode *cur = dummyHead;
while(cur->next != NULL){
if(cur->next->val == val){
ListNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else{
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
Day8 链表
1、反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *tmp;//临时保存cur的下一个节点
ListNode *cur = head;
ListNode *pre = NULL;
while(cur){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
2、删除排序链表中的重复元素
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* cur = head;
ListNode *tmp;
if(!head) //head为空则执行
{
return head;
}
while(cur->next) //满足条件则执行
{
if(cur->val == cur->next->val)
{
tmp = cur->next;
cur->next = cur->next->next;
delete tmp;//删除节点 释放空间
}
else
{
cur = cur->next; //指针移向下一位
}
}
return head;
}
};
Day9 栈/队列
1、有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
class Solution {
public:
bool isValid(string s) {
/**法一:
//如果字符串s中的个数为奇数 返回false
int n = s.length();
if(n%2 == 1)
return false;
//定义字典 前面的为key 后面的为value
unordered_map<char , char> pairs={
{')','('},
{']','['},
{'}','{'}
};
stack<char> stk;
//遍历字符串s
for(char ch:s){
if(pairs.count(ch)) //如果为pairs中的')' ']' '}'
{
if(stk.empty() || stk.top() != pairs[ch]) //pairs[ch]指的是map中的第二列字符
{
return false;
}
stk.pop();
}
else //如果为pairs中的'(' '[' '{'
{
stk.push(ch);
}
}
return stk.empty(); //判断堆栈是否为空
**/
//法二:
stack<int> st;
for(int i = 0; i < s.size(); i++){
if(s[i] == '(') st.push(')');
else if(s[i] == '{') st.push('}');
else if(s[i] == '[') st.push(']');
else if(st.empty() || st.top() != s[i]) return false;
else st.pop();
}
return st.empty();
}
};
2、用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
class MyQueue {
public:
stack<int> stkIn;
stack<int> stkOut;
MyQueue() {
}
void push(int x) {
stkIn.push(x);
}
int pop() {
if(stkOut.empty()){
while(!stkIn.empty()){
stkOut.push(stkIn.top());
stkIn.pop();
}
}
int res = stkOut.top();
stkOut.pop();
return res;
}
int peek() {
int res = this->pop();
stkOut.push(res);//pop使得stkOut少了一个元素,再推进去
return res;
}
bool empty() {
return stkIn.empty() && stkOut.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
Day10 树
1、二叉树的前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
/**
* Definition for a binary tree node.
* 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:
// 前序遍历:根节点,左节点,右节点
//定义 preorder(root) 表示当前遍历到 root 节点的答案。按照定义,我们只要首先将 root 节点的值加入答案,然后递归调用 preorder(root.left) 来遍历 root 节点的左子树,最后递归调用 preorder(root.right) 来遍历 root 节点的右子树即可,递归终止的条件为碰到空节点
void preorder(TreeNode* root,vector<int>& res)
{
if(root == nullptr)
{
return; //不带返回值的return语句
}
//单层逻辑
//将根节点的值加入vector
//将左子树的值加入vector
//将右子树的值加入vector
res.push_back(root->val);
preorder(root->left,res);
preorder(root->right,res);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
//退出参数
preorder(root,res);
//传递参数 输入根节点root,返回vector
return res;
}
};
2、二叉树的中序遍历
给定一个二叉树的根节点 root ,返回它的 中序 遍历。
/**
* Definition for a binary tree node.
* 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:
// 中序遍历:左节点,根节点,右节点
//定义 inorder(root) 表示当前遍历到 root 节点的答案。那么按照定义,我们只要递归调用 inorder(root.left) 来遍历root 节点的左子树,然后将root 节点的值加入答案,再递归调用inorder(root.right) 来遍历root 节点的右子树即可,递归终止的条件为碰到空节点。
void inorder(TreeNode* root,vector<int>& res)
{
if(root == nullptr)
{
return; //不带返回值的return语句
}
//单层逻辑
//将左子树的值加入vector
//将根节点的值加入vector
//将右子树的值加入vector
inorder(root->left,res);
res.push_back(root->val);
inorder(root->right,res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
//退出参数
inorder(root,res);
//传递参数 输入根节点root,返回vector
return res;
}
};
3、二叉树的后序遍历
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
/**
* Definition for a binary tree node.
* 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:
// 后序遍历:左节点,右节点,根节点
//定义 postorder(root) 表示当前遍历到 root 节点的答案。那么按照定义,我们只要递归调用 postorder(root.left) 来遍历root 节点的左子树,再递归调用 postorder(root.right) 来遍历root 节点的右子树,然后将root 节点的值加入答案即可,递归终止的条件为碰到空节点。
void postorder(TreeNode* root,vector<int>& res)
{
if(root == nullptr)
{
return; //不带返回值的return语句
}
//单层逻辑
//将左子树的值加入vector
//将根节点的值加入vector
//将右子树的值加入vector
postorder(root->left,res);
postorder(root->right,res);
res.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
//退出参数
postorder(root,res);
//传递参数 输入根节点root,返回vector
return res;
}
Day11 树
1、二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
/**
* Definition for a binary tree node.
* 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:
//BFS 广度优先搜索
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> res;
if(root != nullptr) que.push(root);
while(!que.empty()){
vector<int> vec;
int size = que.size();
for(int i = 0; i < size; i++){
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
2、二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
/**
* Definition for a binary tree node.
* 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) {
//如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为max(l,r)+1
//而左子树和右子树的最大深度又可以以同样的方式进行计算。 到空节点退出。
if(root == nullptr)
{
return 0;
}
return max(maxDepth(root->left),maxDepth(root->right)) + 1;
}
};
3、对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
/**
* Definition for a binary tree node.
* 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:
//如果同时满足下面的条件,两个树互为镜像:
//它们的两个根结点具有相同的值
//每个树的右子树都与另一个树的左子树镜像对称
/**法一:
//可以实现这样一个递归函数,通过「同步移动」两个指针的方法来遍历这棵树,p 指针和 q 指针一开始都指向这棵树的根,随后 p 右移时,q 左移,p 左移时,q 右移。每次检查当前 p 和 q 节点的值是否相等,如果相等再判断左右子树是否对称。
bool check(TreeNode *p, TreeNode *q)
{
if(!p && !q)
{
return true;
}
if(!p || !q)
{
return false;
}
return p->val == q->val && check(p->left,q->right) && check(p->right,q->left);
}
**/
//法二:
bool check(TreeNode* left, TreeNode* right){
if(left == nullptr && right == nullptr) return true;
else if(left == nullptr && right != nullptr) return false;
else if(left != nullptr && right == nullptr) return false;
else if(left->val != right->val) return false;
else{
bool outOrder = check(left->left, right->right);
bool inOrder = check(left->right, right->left);
bool isSame = outOrder && inOrder;
return isSame;
}
}
bool isSymmetric(TreeNode* root) {
//return check(root,root);
if(root == nullptr) return true;
return check(root->left, root->right);
}
};
Day12 树
1、翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
/**
* Definition for a binary tree node.
* 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* invertTree(TreeNode* root) {
if(root == nullptr) return root;
swap(root->left, root->right);//中
invertTree(root->left);//左
invertTree(root->right);//右
return root;
}
};
2、路径总和
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
/**
* Definition for a binary tree node.
* 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:
bool hasPathSum(TreeNode* root, int targetSum) {
//询问是否存在从当前节点 root 到叶子节点的路径,满足其路径和为 sum。假定从根节点到当前节点的值之和为 val,我们可以将这个大问题转化为一个小问题:是否存在从当前节点的子节点到叶子的路径,满足其路径和为 sum - val。不难发现这满足递归的性质,若当前节点就是叶子节点,那么我们直接判断 sum 是否等于 val 即可(因为路径和已经确定,就是当前节点的值,我们只需要判断该路径和是否满足条件)。若当前节点不是叶子节点,我们只需要递归地询问它的子节点是否能满足条件即可。
if(root == nullptr)
{
return false;
}
if(root->left == nullptr && root->right == nullptr)
{
return targetSum == root->val;
}
return hasPathSum(root->left,targetSum - root->val) || hasPathSum(root->right,targetSum - root->val);
}
};
Day13 树
1、二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
/**
* Definition for a binary tree node.
* 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* searchBST(TreeNode* root, int val) {
if(root == nullptr) return nullptr;
else if(root->val == val) return root;
//要加return
else if(root->val > val) return searchBST(root->left, val);//往左搜索
else if(root->val < val) return searchBST(root->right, val);//往右搜索
return nullptr;
}
};
2、二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
/**
* Definition for a binary tree node.
* 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* insertIntoBST(TreeNode* root, int val) {
if(root == nullptr){
//返回值是一个指针,就要新建一个指针node
TreeNode* node = new TreeNode(val);//构造函数是TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
return node;
}
//遍历找到符合条件的空节点,插入val
//本层用root->left或者root->right将其接住
else if(root->val > val) root->left = insertIntoBST(root->left, val);
else if(root->val < val) root->right = insertIntoBST(root->right, val);
return root;
}
};
Day14 树
1、验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
/**
* Definition for a binary tree node.
* 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* pre = NULL;//记录上一个节点
//采用中序遍历 (左 < 中 < 右)
bool isValidBST(TreeNode* root) {
//二叉搜索树也可以为空
if(root == NULL) return true;
bool left = isValidBST(root->left);//左
//上一个节点的值大于本节点的值返回false
if(pre != NULL && pre->val >= root->val) return false;
pre = root;
bool right = isValidBST(root->right);//右
return left && right;
}
};
2、两数之和 IV - 输入 BST
给定一个二叉搜索树 root 和一个目标结果 k,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
/**
* Definition for a binary tree node.
* 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:
//中序遍历之后使用双指针
vector<int> vec;
void inOrder(TreeNode* root){
if(root == NULL) return;
inOrder(root->left);
vec.push_back(root->val);
inOrder(root->right);
}
bool findTarget(TreeNode* root, int k) {
if(root == NULL) return false;
inOrder(root);//运行结束后返回的是从小到大的中序遍历排序
//双指针
int left = 0, right = vec.size() - 1;
//int mid = (left + right) / 2;
while(left < right){
if(vec[left] + vec[right] > k){
right--;
}
else if(vec[left] + vec[right] < k){
left++;
}
else{
return true;
}
}
return false;
}
};
3、二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == p || root == q || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left == NULL && right != NULL) return right;
else if(left != NULL && right == NULL) return left;
else if(left != NULL && right != NULL) return root;
else{
return NULL;
}
}
};