Leetcode训练营笔记(DataWhale开源社区)(2022春)

Leetcode训练营笔记(DataWhale开源社区)

文章目录

  • Leetcode训练营笔记(DataWhale开源社区)
    • 1.线性结构
      • 1.1数组
        • 1.1.1课堂内容
        • 1.1.2课堂例题
          • a.T1两数之和
          • b.T15最接近三数之和
        • 1.1.3课后题
          • a.T26移除元素
          • b.T25删除有序数组中的重复项
          • c.T14三数之和
      • 1.2链表
        • 1.2.1课堂内容
        • 1.2.2课堂例题
          • a.T148移除链表元素
          • b.T61旋转链表
        • 1.2.3课后题
          • a.T160相交链表
          • b.T21合并两个有序链表
          • c.*T82删除排序列表中的重复元素2
      • 1.3栈
        • 1.3.1课后题
          • a.T130最小栈
          • b.T844比较含退格的字符串
          • c.T227基本计算器2
      • 1.4字符串
        • 1.4.1课后题
          • a.T680验证回文字符串2
          • b.T168Excel表列名称
          • c.T394 字符串解码
    • 2.非线性结构
      • 2.1树
        • 2.1.1课后题
          • a.T111 二叉树的最小深度
          • b.T112路径总和
          • c.T173 二叉搜索树迭代器
    • 3.技巧
      • 3.1位运算
        • 3.1.1课后题
          • a.T171 2的幂
          • b.T137 只出现一次的数字2
          • c.T78 子集
      • 3.2 双指针
        • 3.2.1 课后题
          • a.T71 删除排序链表中的重复元素
          • b.T118 环形链表
          • c.T125排序链表
    • 4.算法
      • 4.1排序
        • 4.1.1课后题
          • a.T966有序数组的平方
          • b.T1122数组的相对排序
          • c.T147 对链表进行插入排序

1.线性结构

1.1数组

1.1.1课堂内容

a.一二三维数组公式:
一 维 数 组 : L o c ( a [ i ] ) = L o c ( a [ 0 ] ) + i ∗ c 二 维 数 组 : L o c ( a [ i , j ] ) = L o c ( a [ 0 ] [ 0 ] ) + ( i ∗ n + j ) ∗ c 三 维 数 组 : L o c ( a [ i , j , k ] ) = L o c ( a [ 0 ] [ 0 ] [ 0 ] ) + ( i ∗ n ∗ l + j ∗ l + k ) ∗ c 一维数组:Loc(a[i])=Loc(a[0])+i*c\\ 二维数组:Loc(a[i,j])=Loc(a[0][0])+(i*n+j)*c\\ 三维数组:Loc(a[i,j,k])=Loc(a[0][0][0])+(i*n*l+j*l+k)*c Loc(a[i])=Loc(a[0])+icLoc(a[i,j])=Loc(a[0][0])+(in+j)cLoc(a[i,j,k])=Loc(a[0][0][0])+(inl+jl+k)c

1.1.2课堂例题
a.T1两数之和

题目:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

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

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

**进阶:**你可以想出一个时间复杂度小于 O(n2) 的算法吗?

解:

暴力

class Solution:
 def twoSum(self, nums: list[int], target: int) -> list[int]:
     for i in range(len(nums) - 1):
         j = i + 1
         while j < len(nums):
             if nums[j] + nums[i] == target:
                 res = [j, i]
                 return res
             else:
                 j += 1
     return []
b.T15最接近三数之和

题目:

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

示例 2:

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

提示:

  • 3 <= nums.length <= 1000
  • -1000 <= nums[i] <= 1000
  • -104 <= target <= 104

解:

O( n 3 n^3 n3)解法:

嵌套三层循环,寻找和的绝对值最接近的数

O( n 2 n^2 n2)解法:

def threeSumClosest(self, nums: List[int], target: int) -> int:
        nums.sort()
        result = nums[0] + nums[1] + nums[2] #用result记录最接近的值
        for i in range(len(nums)-2):
            start = i + 1
            end = len(nums) - 1
            while(start<end):
                summ = nums[i] + nums[start] + nums[end] #用summ记录一次循环的总和
                if(abs(target-summ)<abs(target-result)): #如果summ更接近target,则将result更新为新的summ
                    result = summ
                if(summ>target): #summ大了就将最后一个元素往前移,反之将中间的元素往后移
            	    end-=1
                elif(summ<target): 
                    start+=1
                else: #result与target相等,直接返回result
                    return result
        return result
1.1.3课后题
a.T26移除元素

题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
 print(nums[i]);
}

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

解:

前后双指针解决,删除方式采用删除一个就往前移以为而不是前后交换

def removeElement(nums: list, val: int):
    start = 0 #双指针
    end = len(nums) - 1
    res = len(nums)
    while start <= end: #start与end重合时停止
        if nums[start] == val:
            temp = start #temp用于删除元素时临时指针
            res -= 1
            while temp != end: #temp与end重合时停止删除
                nums[temp] = nums[temp+1]
                temp+=1
            end -= 1
        if(nums[start] == val): #更新后元素仍为val,则重复循环
            continue
        else:
            start += 1
    return res

numsTest = [0,1,2,2,3,0,4,2]
print(removeElement(numsTest, 2))
b.T25删除有序数组中的重复项

给你一个有序数组 nums ,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按升序排列

解:

一个指针遍历,将不重复的提到前面,逐渐组成不重复序列

def removeRepeat(nums: list):
    if(len(nums)==0):
        return 0
    res = 0
    for i in range(1,len(nums)):
        if nums[i] == nums[i-1]:
            res += 1
        else:
            nums[i-res] = nums[i] #与前面一个不重复,往前挪
    return len(nums)-res
c.T14三数之和

题目:

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 0 <= nums.length <= 3000
  • `-105 <= nums[i] <= 105

解:

三指针操作

class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        nums.sort()
        result = []
        for i in range(len(nums)):
            if nums[i] > 0:
                return result
            if i > 0 and nums[i - 1] == nums[i]:  # 对元素a做去重
                continue

            left = i + 1  # 左右双指针
            right = len(nums) - 1

            while left < right:
                if nums[i] + nums[left] + nums[right] > 0:  # 大了right左移
                    right -= 1
                elif nums[i] + nums[left] + nums[right] < 0:  # 小了left右移
                    left += 1
                else:
                    temp = [nums[i], nums[left], nums[right]]
                    result.append(temp)
                    while left < right and nums[right] == nums[right - 1]:  # 对元素c进行去重
                        right -= 1
                    while left < right and nums[left] == nums[left + 1]:  # 对元素b进行去重
                        left += 1
                    right -= 1
                    left += 1
        return result

1.2链表

1.2.1课堂内容

旋转链表:

1->2->3->4->5 向右移动两位

操作方式:5后接1,第3位后断(length-m)

1.2.2课堂例题
a.T148移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

#include

using namespace std;

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) {

        if(head==nullptr) return head;//老坑了

        ListNode* pre = head;
        ListNode* p = head->next;
        while(p!=nullptr){
            if(p->val==val){
                ListNode* temp = p;
                p = p->next;
                pre->next = p;
                delete temp;
                continue;
            }
            p = p->next;
            pre = pre->next;
        }

        //排除第一个元素是要排除的元素
        if(head->val == val) {
            head = head->next;
        }

        return head;
    }
};
b.T61旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

使用上面的思路,先连尾巴再断

#include

using namespace std;

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 k) {

        if(head== nullptr) return head;

        ListNode* p = head;

        //统计长度
        int length = 1;
        while(p->next!=nullptr){
            ++length;
            p = p->next;
        }
        p->next = head;//顺便让尾节点指向头节点

        k = k%length;

        //找到要断掉尾巴的节点(length-val)
        p = head;
        for(int i=0;inext){}
        head = p->next;
        p->next = nullptr;

        return head;

    }
};
1.2.3课后题
a.T160相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

O(n^2)的循环暴力法就不写了,用乐扣上面大佬的方法,将两个指针交替指一遍,以弥补长度差:

Leetcode训练营笔记(DataWhale开源社区)(2022春)_第1张图片

//Definition for singly-linked list.
using namespace std;
#include
struct ListNode {
    int val;
    ListNode *next;

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

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA==NULL||headB==NULL) return NULL;
        ListNode* pA = headA;ListNode* pB = headB;
        while(pA!=pB){
            pA = pA == NULL ? headB : pA->next;
            pB = pB == NULL ? headA : pB->next;
        }
        return pA;
    }
};

参考链接:图解相交链表 - 相交链表 - 力扣(LeetCode) (leetcode-cn.com)

b.T21合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

相继插入新的链表中,谁空了就直接接剩下的部分,错误点在直接返回了res而没有使用temp存储链表头节点

#include

using namespace std;

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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* res = nullptr;
        ListNode* temp = nullptr;
        if(list1==nullptr) return list2;
        if(list2==nullptr) return list1;

        if(list1->val<list2->val){
            res = new ListNode(list1->val);
            temp = res;
            list1 = list1->next;
        }else{
            res = new ListNode(list2->val);
            temp = res;
            list2 = list2->next;
        }

        while(list1!=nullptr||list2!=nullptr){
            if(list1==nullptr){
                res->next = list2;
                return temp;
            }
            if(list2==nullptr){
                res->next = list1;
                return temp;
            }
            if(list1->val<list2->val){
                res->next = new ListNode(list1->val);
                res = res->next;
                list1 = list1->next;
            }else{
                res->next = new ListNode(list2->val);
                res = res->next;
                list2 = list2->next;
            }
        }
        return temp;
    }
};
c.*T82删除排序列表中的重复元素2

给定一个已排序的链表的头 head删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表

运用到dummy哑指针(一直在头前一个,保证头可以被删除),用cur遍历,pre跳

//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) {
        if (!head || !head->next) return head;
        ListNode* preHead = new ListNode(0);
        preHead->next = head;
        ListNode* pre = preHead;//头前面
        ListNode* cur = head;
        while (cur) {
            //跳过当前的重复节点,使得cur指向当前重复元素的最后一个位置
            while (cur->next && cur->val == cur->next->val) {
                cur = cur->next;
            }
            if (pre->next == cur) {
                //pre和cur之间没有重复节点,pre后移
                pre = pre->next;
            } else {
                //pre->next指向cur的下一个位置(相当于跳过了当前的重复元素)
                //但是pre不移动,仍然指向已经遍历的链表结尾
                pre->next = cur->next;
            }
            cur = cur->next;
        }
        return preHead->next;
    }
};

1.3栈

1.3.1课后题
a.T130最小栈

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) —— 将元素 x 推入栈中。
  • pop() —— 删除栈顶的元素。
  • top() —— 获取栈顶元素。
  • getMin() —— 检索栈中的最小元素。
#include
#include 

using namespace std;

class MinStack {
public:
    MinStack() {

    }

    void push(int val) {
        if(st.size() == 0){
            st.push({val,val});
        }else{
            st.push({val,min(val,st.top().second)});//和未加新元素的栈顶的第二个(之前的最小值)相比,比它小就替换
        }
    }

    void pop() {
        st.pop();
    }

    int top() {
        return st.top().first;
    }

    int getMin() {
        return st.top().second;
    }

private:
    stack<pair<int,int>> st;//前一个用来存数,后一个用来存最小值
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */
b.T844比较含退格的字符串

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

准备两个指针 ii, jj 分别指向 SS,TT 的末位字符,再准备两个变量 skipSskipS,skipTskipT 来分别存放 SS,TT 字符串中的 # 数量。
从后往前遍历 SS,所遇情况有三,如下所示:
2.1 若当前字符是 #,则 skipSskipS 自增 11;
2.2 若当前字符不是 #,且 skipSskipS 不为 00,则 skipSskipS 自减 11;
2.3 若当前字符不是 #,且 skipSskipS 为 00,则代表当前字符不会被消除,我们可以用来和 TT 中的当前字符作比较。

#include
#include
using namespace std;

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int i = s.length()-1,j=t.length()-1;
        int skipS=0,skipT=0;

        while(i>=0||j>=0){
            while(i>=0){
                if(s[i]=='#'){
                    ++skipS;
                    --i;
                }else if(skipS>0){
                    --skipS;
                    --i;
                }else{
                    break;//结束循环,抛出字符
                }
            }

            while(j>=0){
                if(t[j]=='#'){
                    ++skipT;
                    --j;
                }else if(skipT>0){
                    --skipT;
                    --j;
                }else{
                    break;//结束循环,抛出字符
                }
            }

            if(i>=0&&j>=0){
                if(s[i]!=t[j]) return false;
            }else if(i>=0||j>=0){
                return false;
            }

            --i;--j;
        }
        return true;
    }
};
c.T227基本计算器2

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

#include
#include
using namespace std;

class Solution {
public:
    int calculate(string s) {
        int n = s.length();
        stack<int> stk;
        char sign = '+';
        long num = 0;
        long ans = 0;
        int i = 0;
        while (i < n){
            if (s[i] == ' ') i ++;
            else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
                sign = s[i];
                i ++;
            }
            else {
                while (i < n && s[i] >= '0' && s[i] <= '9'){
                    num = num * 10 + s[i] - '0';
                    i ++;
                }
                if (sign == '-') {
                    num = - num;
                }
                else if (sign == '*') {
                    num = num * stk.top();
                    stk.pop();
                }
                else if (sign == '/') {
                    num = stk.top() / num;
                    stk.pop();
                }
                stk.push(num);
                num = 0;
            }

        }

        while (!stk.empty()) {
            ans += stk.top();
            stk.pop();
        }

        return ans;
    }
};

1.4字符串

1.4.1课后题
a.T680验证回文字符串2

给定一个非空字符串 s最多删除一个字符。判断是否能成为回文字符串。

#include

using namespace std;

class Solution {
public:

    bool checkPalindrome(const string& s,int low,int high){//字符串出现不一样的字符处理函数
        while(low<high){
            if(s[low]!=s[high]) return false;
            ++low;--high;
        }
        return true;
    }
    bool validPalindrome(string s) {
        int low = 0,high = s.length()-1;
        while(low<high){
            if(s[low]!=s[high]){//如果出现不一样的元素,那么删除low+1元素或者high-1元素,倘若有一种方法可以实现回文,那么就return true,反之则返回false
                return checkPalindrome(s,low,high-1)|| checkPalindrome(s,low+1,high);//只要有一种方法是正确的那就return true
            }
            ++low;
            --high;
        }
        return true;
    }
};
b.T168Excel表列名称

给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。

例如:

A -> 1
B -> 2
C -> 3

Z -> 26
AA -> 27
AB -> 28

#include
#include
using namespace std;

class Solution {
public:
    string convertToTitle(int columnNumber) {
        string res;
        while(columnNumber){//Z特殊情况,若不处理循环不停止
            if(columnNumber%26==0){
                res.push_back('Z');
                columnNumber-=26;
            }else{
                res.push_back(char('A'+columnNumber%26-1));
            }
            columnNumber/=26;
        }
        reverse(res.begin(),res.end());
        return res;
    }
};
c.T394 字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

#include
#include
#include
using namespace std;

class Solution {
public:
    string decodeString(string s) {
        string res = "";
        stack <int> nums;
        stack <string> strs;
        int num = 0;
        int len = s.size();
        for(int i = 0; i < len; ++ i)
        {
            if(s[i] >= '0' && s[i] <= '9')
            {
                num = num * 10 + s[i] - '0';
            }
            else if((s[i] >= 'a' && s[i] <= 'z') ||(s[i] >= 'A' && s[i] <= 'Z'))
            {
                res = res + s[i];
            }
            else if(s[i] == '[') //将‘[’前的数字压入nums栈内, 字母字符串压入strs栈内
            {
                nums.push(num);
                num = 0;
                strs.push(res);
                res = "";
            }
            else //遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律
            {
                int times = nums.top();
                nums.pop();
                for(int j = 0; j < times; ++ j)
                    strs.top() += res;
                res = strs.top(); //之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
                //若是左括号,res会被压入strs栈,作为上一层的运算
                strs.pop();
            }
        }
        return res;
    }
};

2.非线性结构

2.1树

2.1.1课后题
a.T111 二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

使用递归,官方题解:

#include
using namespace std;

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 minDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        if(root->left==nullptr&&root->right==nullptr) return 1;
        int min_depth = INT_MAX;
        if(root->left!=nullptr) min_depth = min(min_depth, minDepth(root->left));
        if(root->right!=nullptr) min_depth = min(min_depth, minDepth(root->right));
        return min_depth+1;
    }
};
b.T112路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

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) {
        if(root==nullptr) return false;
        if(root->left==nullptr&&root->right==nullptr) return root->val==targetSum;
        return hasPathSum(root->left,targetSum-root->val)||
        hasPathSum(root->right,targetSum-root->val);//有一边成功就返回成功,然后成功会一直往上返回,直到最后一层
    }
};

c.T173 二叉搜索树迭代器

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

/**
 * 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 BSTIterator {
private:
    void inorder(TreeNode* root,vector<int>& res){
        if(!root) return;
        inorder(root->left,res);
        res.push_back(root->val);
        inorder(root->right,res);
    }
    vector<int> inorderTraversal(TreeNode* root){
        vector<int> res;
        inorder(root,res);
        return res;
    }
    vector<int> arr;
    int idx;

public:
    BSTIterator(TreeNode* root):idx(0),arr(inorderTraversal(root)) {}
    
    int next() {
        return arr[idx++];
    }
    
    bool hasNext() {
        return (idx<arr.size());
    }
};

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator* obj = new BSTIterator(root);
 * int param_1 = obj->next();
 * bool param_2 = obj->hasNext();
 */

3.技巧

3.1位运算

3.1.1课后题
a.T171 2的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

利用2的幂次与减一与运算为0进行解题,注意点为符号运算顺序,==恰恰好大于&,所以不能忘了加括号

class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n>0&&(n&(n-1))==0;
    }
};
b.T137 只出现一次的数字2

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

看到题解中有使用数字电路中真值公式的解法,确实很妙

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a=0,b=0;
        for(int num:nums){
            a = a ^ num & ~ b;
            b = b ^ num & ~ a;
        }
        return a;
    }
};
c.T78 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;

    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        for (int mask = 0; mask < (1 << n); ++mask) {//1<
            t.clear();
            for (int i = 0; i < n; ++i) {
                if (mask & (1 << i)) {//与运算表示判断每一位是否包含在其中
                    t.push_back(nums[i]);
                }
            }
            ans.push_back(t);
        }
        return ans;
    }

};

3.2 双指针

3.2.1 课后题
a.T71 删除排序链表中的重复元素

给定一个已排序的链表的头 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) {
        if(head==nullptr) return nullptr;
        ListNode* dummy = head;
        while(head->next!=nullptr){
            if(head->val == head->next->val){
                ListNode* temp = head->next;
                head->next = head->next->next;
                delete temp;
                continue;
            }
            head = head->next;
        }
        return dummy;
    }
};
b.T118 环形链表

给你一个链表的头节点 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) {
        if(head==nullptr||head->next==nullptr) return false;
        ListNode* fast = head->next;
        ListNode* slow = head;
        while(slow!=fast){
            if(fast==nullptr||fast->next==nullptr) return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
        
    }
};
c.T125排序链表

给你链表的头结点 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* sortList(ListNode* head) {
        vector<int> v;
        ListNode* n = head;
        while(n!=nullptr){
            v.push_back(n->val);
            n = n->next;
        }
        sort(begin(v),end(v));
        n = head;
        for(int i : v){
            n->val = i;
            n = n->next;
        }
        return head;
    }
};

4.算法

4.1排序

4.1.1课后题
a.T966有序数组的平方

暴力

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(num*num for num in nums);
b.T1122数组的相对排序

给你两个数组,arr1 和 arr2,arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。

对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

用python可以非常优雅的解决,注意python中的remove函数是移除列表中符合条件的第一项

class Solution:
    def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
        res = []
        for i in arr2:
            while i in arr1:#取出arr1中所有i
                res.append(i)
                arr1.remove(i)
        return res+sorted(arr1)
c.T147 对链表进行插入排序

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

class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        dummy = ListNode(float("-inf"))
        pre = dummy#拿来找插入位置
        tail = dummy#最后一个排好序的
        cur = head
        while cur:
            if tail.val<cur.val:
                tail.next = cur
                tail = cur
                cur = cur.next
            else:
                tmp = cur.next
                tail.next = tmp
                #寻找插入位置
                while pre.next and pre.next.val<cur.val:
                    pre = pre.next
                #找到应该插入的位置,在pre后面插入
                cur.next = pre.next
                pre.next = cur
                pre = dummy
                cur = tmp
        return dummy.next

你可能感兴趣的:(数据结构与算法,leetcode,算法,职场和发展)