LeetCode学习计划——数据结构入门

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;
        }
        
    }
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wrdoct

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值