2021年 LeetCode 刷题笔记。

LeetCode 1566. Detect Pattern of Length M Repeated K or More Times

简单题目,在$O(n)$ 时间复杂度。

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    # m 表示重复的 subarry,k 表示重复的次数
    def containsPattern(self, arr: List[int], m: int, k: int) -> bool:
        
        n =len(arr)
        counter =0
        # 按顺序遍历保证了 consecutively 
        for i in range(n-m):
            
            if arr[i] ==arr[i+m]:
                counter +=1
            else:
                # 一旦当前数字不能作为重复的数字,那么之前的记录也都不能用了
                counter =0
        
            if counter == m*(k-1):
                return True
        return False

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    bool containsPattern(vector<int>& arr, int m, int k) {
        int n =arr.size();
        int counter =0;
        for(int i =0; i+m< n; i++)
        {   
            if(arr[i]  ==arr[(i +m )])
                counter +=1;
            else
                counter =0;
            if (counter == m *(k-1))
                return true;         
        }
        return false;
    }
};

1567. Maximum Length of Subarray With Positive Product

时间复杂度是$O(n)$,递推求解(dp,递归)。

python实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    def getMaxLen(self, nums: List[int]) -> int:
        pos =neg =ans =0
        for n in nums:
            if n ==0:
                pos, neg =0, 0 # 递归中有一种情况:重置。
            elif n >0:
                # 从执行顺序上说,等号右边都计算完成之后,然后再重新赋值给左边 (体现了本题中的递推or dp 的思路)
                pos, neg =pos +1, neg +(1 if neg >0 else 0)
                # 注意不能将这一句分开写成两句。
                #pos =pos +1
                #neg =neg +(1 if neg >0 else 0)
            else:
                neg, pos =pos +1 , neg+ (1 if neg >0 else 0)
                #neg =pos +1
                #pos = neg+ (1 if neg>0 else 0)
            ans =max(ans, pos)
        return ans

c++ 代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n =nums.size();
        vector<int> pos(n, 0), neg(n, 0);
        int res =0;
        if(nums[0] >0)
        {
            pos[0] =1;
            res =1;
        }
        else if(nums[0] <0) neg[0] =1;
        for(int i =1; i<n; i++)
        {
            int t =nums[i];
            if (t >0)
            {
                pos[i] =pos[i-1] +1;
                if(neg[i-1] > 0)
                    neg[i] = neg[i-1] +1;
                else
                    neg[i] =neg[i-1];
            }
            else if (t <0)
            {
                neg[i] =pos[i-1] +1;
                if(neg[i-1] >0)
                    pos[i] =neg[i-1] +1;
                else
                    pos[i] =neg[i-1];
                    
            }
            res =max(res, pos[i]);   
        }
        return res;
    }
};

1576. Replace All ?’s to Avoid Consecutive Repeating Characters

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    string modifyString(string s) {
        int n =s.size();
        for(int i =0; i<n; i++)
        {   
            if(s[i] !='?' ) continue;
            // 26字母进行遍历
            for( char ch ='a'; ch<='z'; ch++)
            {
                if(i && s[i-1] ==ch) continue;
                if( i+1<n && s[i+1] ==ch) continue;
                s[i] =ch;
            }
        }
        return s;
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution:
    def modifyString(self, s: str) -> str:
        n =len(s)
        s =list(s)
        alpha ='abcdefghijklmnopqrstuvwxyz'
        for i in range(n):
            if s[i] !='?': 
                continue
            for ch in alpha:
                if i>0 and s[i-1] ==ch: continue
                if i+1 < n and s[i+1] ==ch: continue
                s[i] =ch # 将 s[i] 字母进行替换
        return ''.join(s)
    

1577. Number of Ways Where Square of Number Is Equal to Product of Two Numbers

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    # 数据范围是 10^3, 所以 O(n^2) 的时间复杂度是可以接受的
    from collections import Counter
    def numTriplets(self, nums1: List[int], nums2: List[int]) -> int:
        square1 = [ v*v for v in nums1]
        square2= [v*v for v in nums2]
        
        square1 =Counter(square1)
        square2 =Counter(square2)
        res =0
        n1, n2 =len(nums1), len(nums2)
        
        for i in range(n1-1):
            for j in range(i+1, n1):
                val =nums1[i] * nums1[j]
                if val  in square2:
                    res += square2[val]

        for i in range(n2-1):
            for j in range(i+1, n2):
                val = nums2[i] * nums2[j]
                if val in square1:
                    res += square1[val]
        return res

可以优化的点:nums1nums2 的操作基本上类同的,所以可以写成一个函数,然后调用两次。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution:
    from collections import Counter
    def fun(self, nums1: List[int], nums2: List[int]) -> int:
        nums2 =[val*val for val in nums2]
        dic =Counter(nums2)
        res =0
        n =len(nums1)
        for i in range(n-1):
            for j in range(i+1, n):
                res += dic[nums1[i] *nums1[j]]
        return res
    
    def numTriplets(self, nums1: List[int], nums2: List[int]) -> int:
        
        return self.fun(nums1, nums2) +self.fun(nums2, nums1)
        

(至少从代码的结构上看,这个版本是更加简洁的)

c++ 中如何定义dictionary unordered_map<long long, int> hash 这样进行书写。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    typedef long long LL;
    int fun(vector<int>& nums1, vector<int>& nums2)
    {
        unordered_map<LL, int> dic;
        for(auto num :nums2)
            dic[(LL)num*num] +=1;
        int res =0;
        int n =nums1.size();
        for(int i =0; i< n-1; i++)
            for(int j =i +1; j< n; j++)
                res += dic[(LL)nums1[i] *nums1[j]];
        return res;
    }
    int numTriplets(vector<int>& nums1, vector<int>& nums2) {
        return fun(nums1, nums2) +fun(nums2, nums1);    
    }
};

1578. Minimum Deletion Cost to Avoid Repeating Letters

python求解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution:
    def minCost(self, s: str, cost: List[int]) -> int:
        # 避免重复的最小删除代价
        # 反向思维:统计最大删除代价,然后使用总的删除代价减去最大删除代价
        # python 中没有连等
        res =0
        total , m =0, 0
        
        for i in range(len(s)):    
            if i ==0 or s[i-1] != s[i]:
                res += total -m
                total =m =cost[i]
            else:
                total += cost[i]
                m =max(m, cost[i])
        res += total -m
        return res

c++ 求解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    int minCost(string s, vector<int>& cost) {
        int res =0;
        int total=0, m =0;
        for(int i =0; i< s.size(); i++)
        {
            if(i ==0 || s[i-1] !=s[i])
            {
                res += total -m;
                total =m =cost[i];
            }
            else
            {
                total += cost[i];
                m =max(m, cost[i]);
            }
        }
        res += total -m;
        return res;
    }
};

1582. Special Positions in a Binary Matrix

c++解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    // 如果 [i, j] 是一个special case,那么该行和该列相加起来都是 1
    int numSpecial(vector<vector<int>>& mat) {
        int rows =mat.size(), cols =mat[0].size();
        vector<int> a(rows, 0), b(cols, 0);
        for(int i =0 ; i< rows; i++)
            for(int j =0 ; j< cols ; j++)
            {
                a[i] += mat[i][j];
                b[j] += mat[i][j];
            }
        int res =0;
        for(int i =0; i< rows; i++)
            for(int j =0 ; j<cols ; j++)
                if(mat[i][j] ==1 && mat[i][j] == a[i] && mat[i][j] == b[j])
                    res +=1;
        
        return res;      
    }
};

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def numSpecial(self, mat: List[List[int]]) -> int:
        rows, cols =len(mat), len(mat[0])
        a, b = [0] * rows, [0] * cols
        # a、b 是按照行、列统计所有数字,所以两个是相同的内容,但是不同的计数方式
        for i in range(rows):
            for j in range(cols):
                a[i] += mat[i][j]
                b[j] += mat[i][j]
        res =0
        for i in range(rows):
            for j in range(cols):
                if mat[i][j] ==1 and mat[i][j] ==a[i] and mat[i][j] ==b[j]:
                    res += 1
        return res

378. 有序矩阵中第 K 小的元素

(1) 思路一:暴力解法

python 解法, $n^2log(n)$ 时间复杂度。 这种是最差的解法

1
2
3
4
5
6
7
8
class Solution:
    # 因为这个 n 数据范围是 300,所以
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        lst =[]
        for row in matrix:
            lst.extend(row)        
        lst.sort()
        return lst[k -1]

(2) 大根堆、小根堆解法

数字中第 K 小的元素 对应的是 K 个元素的大根(顶)堆的堆顶元素(这里有点绕,可以多想想)。

遍历整个数组,如果当前元素小于堆顶元素,那么替换掉;如果堆顶的元素不足 k 个,那么加上。 c++ 中使用 priority_queue 默认表示大根堆。

(大根堆的顶层是相对底层要大的元素)

时间复杂度: $n^2logk$,空间复杂度 $k$

该解法没有利用行有序或者列有序的信息

c++ 中默认是大根堆

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n =matrix.size();
        priority_queue<int> q;
        for(int  i =0; i< n; i++)
            for(int j =0 ; j< n; j++)
            {
                if (q.size() < k)
                q.push(matrix[i][j]);
                else
                    if(matrix[i][j] < q.top())
                    {
                        q.pop();
                        q.push(matrix[i][j]);
                    }
            }
        return q.top();
    }
};

python 解法, python 中默认是小根堆, $O(nlogn)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    # 学习记忆 python 中使用小根堆
    # 思路很简单,建立一个小根堆,然后返回第 k 个元素
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        heap =[]
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                heapq.heappush(heap, matrix[i][j])
        for _ in range(k):
            res = heapq.heappop(heap)
        return res

(3)二分解法

二分搜索的题型分为两种,一种是索引二分(在有序数组中的二分查找),一种是值域二分(可行解在一个区间内查找,判断这个解是否成立)。该题目最小结是左上角的 $matrix[0][0]$, 最大解是右下角 $matrix[n-1][n-1]$,那么第

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
    // 二分搜索的题型有两种:一种是索引二分(在有序数组中二分查找);一种是值域二分(可行解在一个区间内查找)。
    // 这个题目是典型的值域二分问题,最小值是左上角,最大值是右下角
    int  search(vector<vector<int>> & matrix, int  target, int n)
    {
        int count =0;
        int row =n -1, col =0;
        while(row >=0 && col <n)
        {
            if(matrix[row][col] <= target)
            {count += row +1;
            col +=1;
            }
            else
            row -= 1;
        }
        return count;
    }

    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n =matrix.size();
        int left =matrix[0][0], right = matrix[n -1][n -1];
        while(left < right)
        {
            int mid =left +right >>1;
            //cout << mid<< endl;
            int count = search(matrix, mid, n);
            if(count >=k)
            right =mid;
            else
            left =mid +1;
        }
        return left;

    }
};

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    def search(self, matrix, target, n):
        row, col = n -1, 0
        count =0
        while row >= 0 and col <n:
            if matrix[row][col] <= target:
                count += row +1
                col += 1
            else:
                row -= 1
        return count

    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        n =len(matrix)
        left, right = matrix[0][0], matrix[n-1][n-1]

        while left< right:
            mid = left + right >>1
            count = self.search(matrix, mid, n)
            if count >= k:
                right =mid
            else:
                left =mid +1
        return left

144. 二叉树的前序遍历

二叉树的非递归遍历

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * 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> preorderTraversal(TreeNode* root) {
        if(root == NULL) return vector<int>{};
        stack<TreeNode*> stk;
        vector<int> res;
        stk.push(root);
        while( !stk.empty())
        {
            TreeNode* t = stk.top();
            if(t)
            {
                res.push_back(t -> val);
                stk.pop();
                if(t ->right)
                stk.push(t-> right);
                if(t ->left)
                stk.push(t-> left);
            }
        }
        return res;
    }
};

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if not root: return []
        stk =[]
        res =[]
        stk.append(root)
        while stk:
            t =stk.pop()
            if t:
                res.append(t.val)
                if t.right:
                    stk.append(t.right)
                if t.left:
                    stk.append(t.left)
        return res

215. 数组中的第K个最大元素

最优的解法是 $O(n)$,使用堆排序是 $O(nlogn)$

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        lst =[]
        # python 中默认是小根堆
        for num in nums:
            heapq.heappush(lst, -1 *num)
   
        while k:
            val = heapq.heappop(lst)
            k -=1
        return -val

这个是python 解法,时间复杂度是$O(n)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution:
    # 可以参考快排的思路,如果 key 值最后的 index 是 k,那么返回该值,以这个index 数值作为 二分的依据
    def quick_sort(self,nums, l, r):
        key = nums[r]
        left, right =l, r
        while l <r:
            while l<r and nums[l] >= key: l +=1
            nums[r] = nums[l]
            while l <r and nums[r] <=key: r -=1
            nums[l] = nums[r]
        nums[r] =key
        return l

    def findKthLargest(self, nums: List[int], k: int) -> int:
        if len(nums) ==1 and k ==1: return nums[0]
        l, r =0, len(nums) -1
        while l <=r:
            index = self.quick_sort(nums, l, r)
            if index == k-1:
                return nums[index]
            elif index > k -1:
                r = index -1
            else:
                l = index +1
        return -1

206. 反转链表

python 解法,这种解法是无法考察指针的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head: return head
        
        lst =[]
        while head:
            lst.append(head.val)
            head = head.next
        
        lst =lst[::-1]
        head =ListNode(lst[0])
        dummy =head
        for i in range(1, len(lst)):
            node =ListNode(lst[i])
            head.next =node
            head =head.next
        return dummy

python 正规的解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre =None
        cur =head
        while cur:
            nex =cur.next
            cur.next =pre
            pre =cur
            cur =nex
        return pre

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 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:
    // 一般 linkedlist 有三种类型:三个指针(这道题);使用 dummy 指针
    ListNode* reverseList(ListNode* head) {
        ListNode* pre =nullptr;
        ListNode* cur =head;
        while(cur)
        {
            ListNode* next = cur ->next;
            cur->next =pre;
            pre =cur, cur =next; 
        }
        return pre;
    }
};

56. 合并区间

python 解法

因为 $n = 10^4$ ,所以时间复杂度 $nlogn$ 是可以满足要求的。 python 中对元组进行排序,那么首先是按照第一个元素有序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    #
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        if not intervals: return []
        intervals.sort()
        #print(intervals) 
        res =[]
        a, b = intervals[0]
        for i in range(1, len(intervals)):
            a1, b1 = intervals[i]
            if a1 <= b:
                b = max(b, b1)
            else:
                res.append([a, b])
                a, b = a1,b1

        res.append([a, b])
        return res

c++ 解法

sort() 函数默认首先按照第一个元素进行增排序,如果第一个相同的话,按照第二个元素进行增排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:

    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if(intervals.empty()) return vector<vector<int>>{};
        sort(intervals.begin(), intervals.end());
        int a = intervals[0][0], b =intervals[0][1];
        vector<vector<int>> res;
        for(int i =1; i< intervals.size(); i ++)
        {
            int a1 =intervals[i][0], b1 =intervals[i][1];
            if(a1 <=b)
                b =max(b, b1);
            else
            {
                res.push_back(vector<int>{a, b});
                a =a1, b =b1;
            }
        }
        res.push_back(vector<int>{a, b});
        return res;
    }
};

57. 插入区间

这个和上面题目的要求是不一样的,上面是合并,这里是说得到不重合的区间的合集。

python 解法

以下时间复杂度是 $O(nlogn)$, 这种解法不是最优的,因为没有用到原先的 interval 是有序的这条信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        if not intervals: return [newInterval]
        intervals.append(newInterval)
        intervals.sort()
        n =len(intervals)
        if n ==1: return intervals
        a, b = intervals[0][0], intervals[0][1]
        res =[]
        for i in range(1, n):
            a1,b1 =intervals[i][0], intervals[i][1]
            if a1 <=b:
                b =max(b, b1)
            else:
                res.append([a, b])
                a, b = a1, b1
        res.append([a, b])
        return res

Python 解法: 时间复杂度 $O(n)$。分为两个步骤,第一步骤是合并数组,第二步骤是合并重叠部分。第一步可以进一步优化成 $logn $的时间复杂度,但是总的时间复杂度还是不变的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        if not intervals: return [newInterval]
        n =len(intervals)
        t =[]
        i =0
        while i <n:
            if intervals[i][0] < newInterval[0] or (intervals[i][0] == newInterval[0] and intervals[i][1] < newInterval[1]):
                t.append(intervals[i])
                i +=1
            else:
                break
        t.append(newInterval)
        while i <n:
            t.append(intervals[i])
            i +=1
        res =[]

        a, b = t[0][0], t[0][1]
        for i in range(1, len(t)):
            a1, b1 = t[i][0], t[i][1]
            if a1 <= b:
                b =max(b, b1)
            else:
                res.append([a, b])
                a, b =a1, b1
        res.append([a, b])
        return res

给出一个从逻辑上处理的问题。在遍历的过程中,将 newInterval 进行插入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int n =intervals.size();
        bool is_in =false;
        vector<vector<int>> res;
        for(auto item : intervals)
        {
            if( newInterval[1] < item[0])
            {
                if(!is_in)
                {
                    res.push_back(newInterval);
                    is_in =true;
                }
                res.push_back(item);
            }
            else if ( item[1] < newInterval[0])
            res.push_back(item);
            else
            {
                newInterval[0] =min(item[0], newInterval[0]);
                newInterval[1] =max(item[1], newInterval[1]);
                //res.push_back(item);
            }
        }
        if(!is_in)
        res.push_back(newInterval);
        return res;
    }
};

152. 乘积最大子数组

c++ 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:
    // 算法核心思想: 需要维护最大值pos,和最小值neg,如果当前 nums[i] >0,那么最大值就是pos * nums[i], 如果 nums[i] <0,那么
    int maxProduct(vector<int>& nums) {
        int maxv =nums[0], minv =nums[0];
        int res =nums[0];
        for(int i =1; i< nums.size() ; i++)
        {
            int t1= maxv, t2 =minv;
            maxv =max(nums[i], max(t1 * nums[i], t2 * nums[i]));
            minv =min(nums[i],  min(t1 * nums[i],  t2* nums[i]));
            res =max(res, maxv);
        }
        return res;
    }
};

python 代码

1
2
3
4
5
6
7
8
9
class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        maxv, minv, res =nums[0], nums[0], nums[0]
        for i in range(1, len(nums)):
            t1, t2 =maxv, minv 
            maxv =max(nums[i], max(t1 * nums[i], t2 * nums[i]))
            minv =min(nums[i], min(t1 * nums[i], t2 * nums[i]))
            res =max(res, maxv)
        return res

160. 相交链表

首先这里指的是单链表。本身对题意就没有把握好。如果单链表相交,那么之后的结点都是相交的,不存在先相交,然后再分离的情况。

然后再想怎么做

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def helper(self, p1, p2, dif):
        while dif:
            p1 =p1.next
            dif -=1
        while p1 and p2:
            if p1 ==p2: return p1
            p1 =p1.next
            p2 =p2.next 
        return None

    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        head =headA
        lena =0
        while head:
            lena +=1
            head =head.next
        lenb =0
        head =headB
        while head:
            lenb +=1
            head =head.next
        if lena> lenb:
            dif = lena -lenb
            return self.helper(headA, headB, dif)
        else:
            dif =lenb -  lena
            return self.helper(headB, headA, dif)
               

322. 零钱兑换

# 需要提供两种解法,一种是递归;一种是动态规划。在解答的时候,优先考虑动态规划
# 下面是递归的解答,但还是有错
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        mincoins =amount
        if amount in coins:
            return 1
        else:
            for i in [c for c in coins if c <= amount]:
                numcoins = 1 + self.coinChange(coins, amount -i)
                mincoins =min(mincoins, numcoins)
        return  mincoins

动态规划

时间复杂度是 $O(mn)$, 其中 $m$ 表示 len(coins) , $n$ 表示 $amount$。

背包问题,相同的体积,使用最少的物品可以装满。注意枚举物体重点的时候,是从小到大进行枚举(j =coins[i]

c++ 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int INF =10000000;
        vector<int> f(amount +1, INF);
      
        f[0] = 0;
        for(int i =0; i< coins.size() ; i++)
        {
            for(int j = coins[i]; j <= amount; j ++)
            f[j] =min(f[j], f[j -coins[i]] +1);
        }
        if(f[amount] ==INF)
        f[amount] =-1;
        return f[amount];
    }
};

python 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        INF =1000000
        # f[j] 表示金额为 j 时候需要使用的最少的数量
        f =[INF] *(amount +1)
        f[0] =0
        for c in coins:
            for j in range(c, amount+1):
                f[j] =min(f[j], f[j - c] +1)
        if f[amount] == INF:
            f[amount] =-1
        return f[amount]

518. 零钱兑换 II

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    # 完全背包问题, amount 可以当做背包的总体积,coins 是单个的体积
    # f[j] 表示面额为 j 时候总的方案数
    def change(self, amount: int, coins: List[int]) -> int:
        f =[0] * (amount +1)
        f[0] =1
        for c in coins:
            for j in range(c, amount+1):
                f[j] += f[j -c]
            
        return f[amount]

剑指 Offer 40. 最小的k个数

当二分排序时候,数组本身就完成了排序,所以直接返回数组就行,不必使用在 quick_sort 的时候返回结果。

时间复杂度是 $O(n)$,证明很繁琐。具体证明可以参考《算法导论》第 9 章第 2 小节。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution:
    def quick_sort(self,arr, l, r):
        key = arr[l]
        left, right =l, r
        while l<r:
            while l <r and arr[r] >=key:
                r -= 1
            arr[l] = arr[r]

            while l <r and arr[l] <=key:
                l +=1
            arr[r] = arr[l]
        arr[l] =key
        return l

    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        l, r =0, len(arr) -1
        while l <r:
            index =self.quick_sort(arr, l, r)
            if index ==k:
                break
            elif index <k:
                l = index +1
            else:
                r =index
        return arr[:k]

还有一种思路:堆。 这个是 $nlogn$ 的算法,可以优化成 $nlogk$ 的时间复杂度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        # 时间复杂度是 nlogk ,
        import heapq
        lst =[] 
        for num in arr:
            heapq.heappush(lst, num)
        res =[]
        while k:
            res.append(heapq.heappop(lst))
            k -=1
        return res

977. 有序数组的平方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        square = [num* num for num in nums]
        if nums[0] >=0: return square
        if nums[-1] <0: return square[::-1]

        index =0
        for (ind, num) in enumerate(nums):
            if num >=0:
                index =ind
                break
        res =[]
        i =index
        j =index -1
        while j >=0 and i <= len(nums) -1:
            if square[j] <= square[i]:
                res.append(square[j])
                j -=1
            else:
                res.append(square[i])
                i +=1
        while j >=0:
            res.append(square[j])
            j -=1
        while i <= len(nums) -1:
            res.append(square[i])
            i +=1
        return res

首先需要找到分界点,按照平方进行排序,[0, ind] 是逆序,[ind, n] 是顺序。只要找到临界点就行。同时学习一下更加简洁的去书写 归并排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 首先找到第一个数值为正的数字
        n  =len(nums)
        ind =n
        for i in range(n):
            if nums[i] >=0:
                ind = i
                break
            else:
                nums[i] = -nums[i]
        # ind 是正序和逆序的分割点
        i, j = ind -1, ind
        res =[]
        while i >=0  or j < n:
            if i<0 :
                res.append(nums[j])
                j +=1
            elif j == n:
                res.append(nums[i])
                i -=1
            else:
                if nums[i] < nums[j]:
                    res.append(nums[i])
                    i -=1
                else:
                    res.append(nums[j])
                    j +=1
        return [ x*x for x in res]

50. Pow(x, n)

c++ 代码。当指数是偶数时候,不断整除 2 最后是1,使用 res= res*p 可以得到最后的结果;是一个快速幂的 结果。注意 c++ 中定义别名 #define LL long long 最后是没有分号的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:
    #define LL long long
    double myPow(double x, int n) {
        double res =1, p =x;
        LL t =abs(n);
        while(t)
        {
            if(t&1 ==1)
            res =res * p;
            p = p *p;
            t >>= 1;
        }
        return n>0? res: 1.0/res;
    }
};

python 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def myPow(self, x: float, n: int) -> float:
        res =1
        p , t =x, abs(n)
        while t:
            if t&1 ==1: res =res *p
            p = p *p
            t  >>= 1
        return res if n >0 else 1.0/ res

1

二分算法有两个版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
// 一般使用的是 l =mid + 1 这个版本
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}

// 在求解平方根的函数中也可以使用 这个版本
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

110. 平衡二叉树

判断是否是一个平衡二叉树。

python 版本,根据定义去书写,dfs 的思路,如果左右子树有不符合条件的,那么 return 为 -1。否则整个树的高度是当前左右子树高度 +1。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root):
            if not root: return 0
            h_left = height(root.left)
            h_right= height(root.right)
            if h_left ==-1 or h_right == -1 or abs(h_left -h_right) >1:
                return -1
            else:
                return max(h_right, h_left) +1
        return height(root) >= 0

c++ 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 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 dfs(TreeNode* root)
    {
        if(root ==nullptr) return 0;
        int h_left = dfs(root -> left), h_right = dfs(root ->right);
        if(h_left == -1 || h_right ==-1 || abs(h_right - h_left) >1)
        return -1;
        return max(h_left, h_right) +1;
    }

    bool isBalanced(TreeNode* root) {
        return dfs(root) >=0;
    }
};

415. 字符串相加

大数相乘,求解的时候,需要先求余数,然后取整数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

class Solution:
    def addStrings(self, a: str, b: str) -> str:
        aa =list(a)[::-1]
        bb = list(b)[::-1]
        len1, len2 =len(aa), len(bb)
        i,j =0, 0
        t=0
        res =[]
        while i <len1 and j < len2:
            t = int(aa[i]) + int(bb[j]) +t
            r  = t%10 # 这两个步骤不能颠倒
            t = t//10 
            res.append(r)
            i +=1
            j +=1
        while i <len1:
            t = int(aa[i]) +t
            r = t %10
            t  = t//10
            res.append(r)
            i += 1
        while j <len2:
            t = int(bb[j]) +t
            r = t %10
            t = t//10
            res.append(r)
            j += 1
        if t >0: res.append(t)
        res = res[::-1]
        res =[str(item) for item in res]
        return ''.join(res)
  1. 1486. XOR Operation in an Array

$O(n)$ 的时间复杂度,如果考察异或操作,那么多和二进制有关。因为数据范围是$10^3$,所以直接暴力求解就ok了。

(也可以归结为模拟题)

python解法

1
2
3
4
5
6
7
class Solution:
    def xorOperation(self, n: int, start: int) -> int:
        res =0
        for i in range(n):
            t = start + 2*i
            res =res ^ (t)
        return res

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int xorOperation(int n, int start) {
        int res =0;
        for(int i =0; i< n; i++)
        {
            int t =i *2 + start;
            res = res^ t;   
        }
        return res;
        
    }
};
  1. 1487. Making File Names Unique

数据范围 $5*10^4$,这意味着需要是$O(n)$ 的做法。使用dictionary

题解:

For while 是非常常见的结构,for 是有序循环,while 是条件循环。

暴力求解(c++)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    vector<string> getFolderNames(vector<string>& names) {
        unordered_set<string> dict;
        vector<string> res;
        
        for(auto name: names)
        {
            int i =0;
            string suf;
            
            while(dict.count(name+suf))
            {
                i ++;
                suf ='('+to_string(i) +')';
            }
            dict.insert(name+suf);
            res.push_back(name+suf);
        }
        return res;
    }
};

时间复杂度分析:$N \times 5\times 10^4$ ,$N$ 是很大的常数,所以可能 time out。

优化的点: 对于同一个文件名,如果之前已经查找过,那么不必从头开始查找。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    vector<string> getFolderNames(vector<string>& names) {
        unordered_set<string> dict;
        unordered_map<string, int> cnt;
        vector<string> res;
        
        for(auto name: names)
        {
            int i =0;
            string suf;
            if (cnt[name] ) i =cnt[name];
            # 这里能够减少时间复杂度
            while(dict.count(name+suf))
            {
                i ++;
                suf ='('+to_string(i) +')';
            }
            cnt[name] =i;
            dict.insert(name+suf);
            res.push_back(name+suf);
        }
        return res;
    }
};

python 版本的code (不仅是算法思路,还需要算法的实现)

下面的解法是正确的,但是 time out (暴力解法)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
    def getFolderNames(self, names: List[str]) -> List[str]:
        from collections import Counter
        dic =Counter()
        dic_ =Counter()
        
        res =[]
        
        for name in names:
            i = 0
            suf =""
            # 这里的 dic 更像是一种 set() 判别是否存在,而非真正dictionary 的功能
            # 每次i 的变化都是从0开始 +1,实际上可以使用dic 保存一个最大值
            while dic.get(name+suf, 0):
                i +=1
                suf ="({})".format(i)
            dic[name+suf] =1
            res.append(name+suf)
        return res

经过上述思路优化的做法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
    def getFolderNames(self, names: List[str]) -> List[str]:
        from collections import Counter
        dic =Counter()
        dic_ =Counter()
        
        res =[]
        
        for name in names:
            i = 0
            suf =""
            # 这里的 dic 更像是一种 set() 判别是否存在,而非真正dictionary 的功能
            # 每次i 的变化都是从0开始 +1,实际上可以使用dic 保存一个最大值
            if dic_.get(name, 0):i =dic_.get(name)
              
            while dic.get(name+suf, 0):
                i +=1
                suf ="({})".format(i)
            dic[name+suf] =1
            dic_[name] =i
            res.append(name+suf)
        return res

将暴力解法写出来,然后去优化,也是一种很基本的解题思路。

1475. Final Prices With a Special Discount in a Shop

时间复杂度是$O(n^2)$

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    // 能想到的算法是 $O(n^2)$,
   //   n =500,所以暴力求解不是最优的,但是能过的 
    vector<int> finalPrices(vector<int>& prices) {
        int n =prices.size();
        vector<int> res(n, 0);
        int j ;
        for(int i =0; i<n-1;i ++ )
        {
            j =i +1;
            while(j <n && prices[j] > prices[i])
                j ++;
            if (j !=n) res[i] =prices[i] -prices[j];
            else
                res[i] =prices[i];
        }
        res[n-1] =prices[n-1];
        return res;
    }
};

python解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution:
    def finalPrices(self, prices: List[int]) -> List[int]:
        # 暴力求解一波
        res =[]
        n =len(prices)
        for i in range(0, n-1): 
            j =i +1
            while j < n and prices[j] > prices[i]:
                j +=1
            if j !=n: 
                res.append(prices[i] -prices[j])
            else:
                res.append(prices[i])
        res.append(prices[-1])
        return res

优化时间复杂度

寻找最近一个比他小的数字,使用单调栈的思想。

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    // 寻找距离最近的比当前数小的数字
    vector<int> finalPrices(vector<int>& prices) {
        
        stack<int> stk;
        vector<int> res;
        int n =prices.size();
        for(int i =n-1; i>=0; i--)
        {
            int t =prices[i];
            while(stk.size() && t < stk.top()) stk.pop();
            if(stk.size() && stk.top() <= t) res.push_back(t -stk.top());
            else
                res.push_back(t);
            while(stk.size() && t ==stk.top()) stk.pop();
            stk.push(t);
        }
        return vector<int>{res.rbegin(), res.rend()};
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution:
    # 距离最近的小数,单调栈思想,最后的时间复杂度是$O(n)$
    def finalPrices(self, prices: List[int]) -> List[int]:
        res =[]
        stk =[]
        n =len(prices)
        for i in range(n-1, -1, -1):
            t =prices[i]
            while stk and stk[-1] > t:
                stk.pop()
            if stk and stk[-1] <= t:
                res.append(t-stk[-1])
            else:
                res.append(t)

            stk.append(t)
        return res[::-1]

1476. Subrectangle Queries

因为数据范围是 $n=100$,那么使用暴力求解就能解决问题。

思路一

python实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class SubrectangleQueries:

    def __init__(self, rectangle: List[List[int]]):
        self.query =rectangle
        
    def updateSubrectangle(self, row1: int, col1: int, row2: int, col2: int, newValue: int) -> None:
        for  i in range(row1, row2+1):
            for j in range(col1, col2+1):
                self.query[i][j] =newValue

    def getValue(self, row: int, col: int) -> int:
        return self.query[row][col]


# Your SubrectangleQueries object will be instantiated and called as such:
# obj = SubrectangleQueries(rectangle)
# obj.updateSubrectangle(row1,col1,row2,col2,newValue)
# param_2 = obj.getValue(row,col)

c++ 实现

多注意一下c++ 中双重数组的声明和初始化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class SubrectangleQueries {
public:
    vector<vector<int>> rect;
    SubrectangleQueries(vector<vector<int>>& rectangle) {
        rect =rectangle;   
    }
    
    void updateSubrectangle(int row1, int col1, int row2, int col2, int newValue) {
        for(int i =row1; i<= row2; i ++)
            for(int j =col1; j<= col2; j++)
                rect[i][j] =newValue;
    }
    int getValue(int row, int col) {
        return rect[row][col];
    }
};

思路一在 updateSubrectangle 比较耗时。还有一种思路:维护一个更新表,将用户的操作以key-value 的方式保存起来。

python实现代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class SubrectangleQueries:
    # 第二种思路:记录update过程,注意 stk中存储的 [(), ()]修改记录,如果修改记录中没有,那么从原来的rectangle中找找
    # python 中如果不再修改,那么是() 要不 [] 更优化
    def __init__(self, rectangle: List[List[int]]):
        self.update = []
        self.query =rectangle

    def updateSubrectangle(self, row1: int, col1: int, row2: int, col2: int, newValue: int) -> None:
        self.stk.update((row1, col1, row2, col2, newValue))
        
    def getValue(self, row: int, col: int) -> int:
        n =len(self.update)
        
        for i in range(n-1, -1, -1):
            if row >= self.update[i][0] and row <= self.update[i][2] and col >= self.update[i][1] and col<= self.update[i][3]:
                return self.update[i][-1]
        return self.query[row][col]

更加简洁的写法

优化了判断函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class SubrectangleQueries:
    # 第二种思路:记录update过程
    def __init__(self, rectangle: List[List[int]]):
        self.update = []
        self.query =rectangle

    def updateSubrectangle(self, row1: int, col1: int, row2: int, col2: int, newValue: int) -> None:
        self.update.append((row1, col1, row2, col2, newValue))
        

    def getValue(self, row: int, col: int) -> int:
        n =len(self.update)
        
        def inside(x, y, R):
            return R[0] <=x <= R[2] and R[1] <= y<=R[3]
        
        for i in range(len(self.update) -1, -1, -1):
            if inside(row, col, self.update[i][:4]):
                return self.update[i][-1]

        return self.query[row][col]

c++ 实现上述思路

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SubrectangleQueries {
public:
    vector<vector<int>> query;
    //stack<int> stk; // 需要使用二维列表实现
    vector<vector<int>> update;
    SubrectangleQueries(vector<vector<int>>& rectangle) {
        query =rectangle;
    }
    
    void updateSubrectangle(int row1, int col1, int row2, int col2, int newValue) {
        update.push_back({row1, col1, row2, col2, newValue}); // c++ 中这属于简化之后的构造函数
    }
    
    int getValue(int row, int col) {
        
        for(int i =update.size()-1; i >=0 ; i--)
        {
            if (row >= update[i][0] && row <= update[i][2] && col >= update[i][1] && col<= update[i][3])
                return udpate[i][4];
        }
        return query[row][col];
    }
};
  1. Path Crossing

判断路线是否相交

分析:数据范围是$10^4$,那么应该是$nlogn$的做法(最多是$n^2$) 的做法?属于模拟题?

正确的解法: 使用dictionary 保存走过的点的坐标,如果发现已经走过,那么 return true否则 return false

c++ 解答

注意set.find()set.count() 对应的判断条件。使用set 就可以表示无修改的dictionary。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    bool isPathCrossing(string path) {
        unordered_set<string> dict;
        dict.insert("00");
        int x=0, y=0;
        for(auto ch: path)
        {
            if (ch=='N') y +=1;
            else if (ch =='E') x +=1;
            else if (ch =='S') y -=1;
            else if (ch =='W') x -=1;
            string t =to_string(x)+to_string(y);
            if (dict.count(t) ) return true;
            //if (dict.find(t)  != dict.end()) return true;
            dict.insert(t);
        }
        return false;
        
    }
};

python 解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def isPathCrossing(self, path: str) -> bool:
        dic =set()
        dic.add("00")
        x, y =0, 0
        for ch in path:
            if ch =='N': y  +=1
            elif ch =='E': x +=1
            elif ch =='S': y -=1
            elif ch =='W': x -=1
            t =str(x)+str(y)
            if t in dic: return True
            dic.add(t)
        return False
    

1497. Check If Array Pairs Are Divisible by k

解题思路:数学题。数字是否可以被k 整除转换为余数问题。如果余数为0,那么可以被其整除;如果两个数字之和可以被整除,那么余数之和相加为k。

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    bool canArrange(vector<int>& arr, int k) {
        vector<int> r(k, 0);
        for(auto num: arr)
            r[(num%k +k)%k] +=1;
        
        if(r[0] %2 ==1) return false;
        
        for(int i =1; i<k ;i++)
            if(r[i] != r[k-i]) return false;
        return true;
        
    }
};

最后的for 循环还可以优化成双指针的形式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
    bool canArrange(vector<int>& arr, int k) {
        vector<int> r(k, 0);
        for(auto num: arr)
            r[(num%k +k)%k] +=1;
        
        if(r[0] %2 ==1) return false;
        
        for(int i =1, j =k-1; i<j; i++, j --)
            if(i ==j) 
            {
                if(r[i] %2 !=0) return false;
            }
            else
            {
                if(r[i] != r[j] ) return false;
            }
        
        return true;
        
    }
};

python 解法 (按照第二种写法)

1
2
3
4
5
6
7
8
class Solution:
    def canArrange(self, arr: List[int], k: int) -> bool:
        
        r = [0] *k
        for num in arr:
            r[(num%k +k) %k] +=1
        
        return r[0]%2 ==0 and all( r[i] ==r[k-i] for i in range(1,k//2+1))

1498. Number of Subsequences That Satisfy the Given Sum Condition

主要是思路问题,查看最后给出的例子可以发现,当序列中任一两数之和满足条件,那么是可以使用数学公式的。所以可以先排序,然后使用二分去查找合适的区间。最后的时间复杂度是 $O(nlogn)$。

任意序列(sequence) 和任意区间的区别:前者可以是非连续,后者是连续的。

python 求解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def numSubseq(self, nums: List[int], target: int) -> int:
        # 如果是 subarry 可以使用预处理找到每个区间的 最大最小值? 但是这个是 subsequence,没有什么思路
        # n =10^5,算法复杂度一般在nlogn 或者 O(n) ,但还是没有思路
        MOD = 1000000000 +7
        nums.sort()
        res =0
        l, r = 0, len(nums) -1
        while l <= r:
            if nums[l] +nums[r] > target: r -=1
            else:
                res =(res+ pow(2, r-l, MOD) ) % MOD
                l +=1
        return res

python 中的一个函数 pow(x, y, z),其中 x is the base, y is the exponent, z (optional) is used for modulus.

(x**y) % z

c++解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
    int numSubseq(vector<int>& nums, int target) {
        int res =0, n =nums.size();
        vector<int> pow(n,1); // 这个就是一个预处理
        int MOD =1000000000 +7;
        
        for(int i =1; i <n; i++)
            pow[i] =(pow[i-1] *2) %MOD;
        
        int l =0, r =n-1;
        sort(nums.begin(), nums.end());
        
        while (l <= r)
        {
            if ( nums[l] + nums[r] > target) 
                r -=1;
            else
            {
                
                res  =( res + pow[r-l] %MOD )% MOD;
                l +=1;
            }
                
        }
        return res;
    }
};

1491. Average Salary Excluding the Minimum and Maximum Salary

题目很简单,简单的遍历一下就可以

c++ 实现代码

(需要记住的是INT_MAXINT_MIN 两个常量)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    double average(vector<int>& salary) {
        double res =0.0;
        int min_v =INT_MAX, max_v =INT_MIN;
        for(auto num : salary)
        {
            res += num;
            min_v = min(min_v, num);
            max_v =max(max_v, num);
        }
        return (res -min_v -max_v )/(salary.size() -2);
    }
};

python实现代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def average(self, salary: List[int]) -> float:
        min_v, max_v = float("inf"), -float("inf")
        res =0
        for num in salary:
            res += num
            min_v =min(min_v, num)
            max_v =max(max_v, num)
        return (res -min_v -max_v)*1.0/(len(salary) -2)
    

1492. The kth Factor of n

数据量是 1000,那么使用暴力枚举就可以: 先找出所有的除数,然后得到。时间复杂度是$O(n)$。

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def kthFactor(self, n: int, k: int) -> int:
        res=[]
        for i in range(1, n+1):
            if n%i ==0:
                res.append(i)
        if k <= len(res):
            return res[k-1];
        else:
            return -1

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    int kthFactor(int n, int k) {
        vector<int> res;
        for(int i =1; i<=n; i++)
        {
            if( n%i ==0) res.push_back(i);
        }
        if (res.size() >=k)
            return res[k-1];
        else
            return -1;   
    }
};

上述解法并没有利用到左右两个数字是“对称”的信息。所以可以从枚举每个数字到枚举每个整除数。优化一下时间复杂度 $O(\sqrt(n))$。

c++ 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    int kthFactor(int n, int k) {
        vector<int> beg, end;
        
        int i =1;
        while (i *i <=n)
        {
            if (n%i ==0)
            {
                beg.push_back(i);
                if (i *i !=n)
                    end.push_back(n/i);
            }
                
            i +=1;
        }
        if (k <= beg.size())
            return beg[k-1];
        else if ( k<= beg.size() +end.size())
            return end[end.size() +beg.size() -k];
        return -1;
        
    }
};

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution:
    # 数据量是 1000,那么使用暴力枚举就可以
    # 先找出所有的除数,然后得到
    def kthFactor(self, n: int, k: int) -> int:
        beg, end =[], []
        i =1
        while i *i <= n:
            if n%i ==0:
                beg.append(i)
                if i *i != n:
                    end.append(n//i)
            i +=1
            
        if k <= len(beg):
            return beg[k-1]
        elif k <= len(beg) +len(end):
            return end[len(beg) +len(end) -k]
        return -1

1503. Last Moment Before All Ants Fall Out of a Plank

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    # 这个比较难的抽象出来模型,没有什么思路
    # 是模拟题。对于向左走的 ant,所需要的时间是其初始位置;对于向右走的ant,所需要的时间是其当前的位置。然后求解所有可能的最大值
    # 算法时间复杂度是 O(n)
    def getLastMoment(self, n: int, left: List[int], right: List[int]) -> int:
        res = -1
        for item in left:
            res =max(res, item)
        for item in right:
            res =max(res, n -item)
        return res
    

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int getLastMoment(int n, vector<int>& left, vector<int>& right) {
        
        int res =-1;
        for(auto item: left)
            res =max(res, item);
        for(auto item: right)
            res =max(res, n -item);
        return res;
            
    }
};

1504. Count Submatrices With All Ones

因为数据范围是 150,那么可以使用 $n^3$ 的做法;当然是可以使用单调栈进行优化,然后时间复杂度是 $n^2$。判断是否可以使用单调栈,问题中是否包含这寻找左侧最近的最小元素(左侧意味着已经处理过,遍历过)。

以下是单调栈优化过的。

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int rows =mat.size(), cols =mat[0].size();
        // 二维vector 的书写
        vector<vector<int>> f(rows, vector<int>(cols));
        
        for(int i =0; i< rows; i++)
            for(int j =0; j< cols; j++)
                if(mat[i][j])
                {
                    f[i][j] =1;
                    if(i) f[i][j] += f[i-1][j];
                }
                else
                    f[i][j] =0;
        
        int res =0;
        for(int i =0; i < rows; i++)
        {
            stack<pair<int, int>> stk;
            for(int j =0; j< cols; j++)
            {
                int s =0;
                
                while(stk.size() && f[i][stk.top().first] >= f[i][j]) stk.pop();
                
                if(stk.size())
                {
                    s += stk.top().second;
                    s += f[i][j] *(j - stk.top().first);
                }
                else
                    s += f[i][j] *(j+1);
                stk.push({j, s});
                res += s;
                    
            }
        }
        return res;
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        
        res =0
        rows, cols =len(mat), len(mat[0])
        f = [ [0] * cols for _ in range(rows)]
    
        for i in range(rows):
            for j in range(cols):
                if mat[i][j]:
                    f[i][j] =1
                    if i : f[i][j] += f[i-1][j]
        
        for i in range(rows):        
            stk =[]
            for j in range(cols):
                s =0
                
                while stk and f[i][stk[-1][0]] >= f[i][j]: stk.pop()
        
                if stk:
                    s += stk[-1][1] # 累加之前的结果
                    s += (j- stk[-1][0]) * f[i][j]
                else:
                    s += (j+1) *f[i][j]
                stk.append([j, s])
                res +=s
        return res

可以写得更加紧凑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution:
    def numSubmat(self, mat: List[List[int]]) -> int:
        
        res =0
        rows, cols =len(mat), len(mat[0])
        f = [ [0] * cols for _ in range(rows)]

        for i in range(rows):        
            stk =[]
            for j in range(cols):
                if mat[i][j]:
                    f[i][j] =1
                    if i : f[i][j] += f[i-1][j]
                s =0
                
                while stk and f[i][stk[-1][0]] >= f[i][j]: stk.pop()
        
                if stk:
                    s += stk[-1][1] # 累加之前的结果
                    s += (j- stk[-1][0]) * f[i][j]
                else:
                    s += (j+1) *f[i][j]
                stk.append([j, s])
                res +=s
        return res
        

1507. Reformat Date

python 比较擅长字符串处理,下面的解法的缺点是使用了int() 转换函数;优点 是使用$ a<= char <= z$ 进行字符的判断和处理。

python解法

(下面是使用了一种更加优化的方法,没有调用python 自带的int 函数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution:
    def reformatDate(self, date: str) -> str:
        mon_dic={"Jan":"01", 
                "Feb": "02", 
                "Mar": "03",
                "Apr":"04", 
                "May" :"05",
                "Jun": "06",
                "Jul" : "07", 
                "Aug" :"08", 
                "Sep" : "09", 
                "Oct" :"10", 
                "Nov" : "11", 
                "Dec" :"12"}
        # 因为字符串是符合某种规范,所以按照长度分为两类
        if len(date) ==13:
            return date[-4:] + '-' + mon_dic[date[-8:-5]] + '-' + date[:2]
        else:
            return date[-4:] + '-' +mon_dic[date[-8:-5]] + '-0'+date[0]

c++ 实现的时候,多使用substr() 函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 class Solution {
public:
    // c++ 中处理字符串: 合理使用substr 
    string reformatDate(string date) {
        unordered_map<string, string> mp;
        
        mp["Jan"] ="01";
        mp["Feb"] ="02";
        mp["Mar"] ="03";
        mp["Apr"] ="04";
        mp["May"] ="05" ;
        mp["Jun"] ="06";
        mp["Jul"] ="07";
        mp["Aug"] ="08";
        mp["Sep"] ="09";
        mp["Oct"] ="10";
        mp["Nov"] ="11";
        mp["Dec"] ="12";
        
        string day;
        int offset ;
        if(isdigit(date[1]))
        {
            day = date.substr(0, 2);
            offset =1;
        }
        else
        {
            day = '0' +date.substr(0, 1);
            offset =0;
        }
        string month =mp[date.substr(offset+4, 3)];
        string year = date.substr(8+offset);
        return year+"-" +month +"-" +day;
    }
};
  1. 1508. Range Sum of Sorted Subarray Sums

python 实现(这种方式会timeout),尽管分析是可以在 $O(n^2)$ 完成,但实际上不行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
    # 暴力求解:1. 得到 subarry sum (n^2) 2. sort() nlogn  3. 选择 [left, right],是O(n) 算法。 N =10^3 ,所以是可以满足条件的。
    def sum_with_modu(self, arr: List[int], MOD :int) -> int:
        res =0
        for item in arr:
            res += (item %MOD)
        return res
    def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:
        MOD =1000000000 +7
        sub_sum =[]
        n =len(nums)
        for i in range(n+1):
            for j in range(i+1, n+1):
                tmp =nums[i:j]
                sub_sum.append(self.sum_with_modu(tmp, MOD))
        
        sub_sum.sort()
        #print(sub_sum)
        res =0
        for i in range(left-1, right):
            res += (sub_sum[i] %MOD)
        return res
        

不要使用内置的 sum() 去计算,使用以下的代码是可以通过的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution:
    def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:
        
        MOD =1000000000 +7
        sums =[]
        n =len(nums)
        for i in range(n):
            s =0
            for j in range(i, n):
                s += nums[j]
                sums.append(s)
        sums.sort()
        #print(sums)
        ans =0
        for i in range(left-1, right):
            ans += sums[i]
        return ans %MOD;

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
    int rangeSum(vector<int>& nums, int n, int left, int right) {
        const int mode =1000000007;
        vector<int> sums;
        
        for(int i =0; i< n; i ++)
        {
            int t =0;
            for(int j =i ; j< n; j++)
            {
                t += nums[j];
                sums.push_back(t);
            }
        }
        
        sort(sums.begin(), sums.end());
        long long res =0;
        for(int i =left -1 ; i <= right -1; i ++)
        {
            res += sums[i];
        }
        return res %mode;
    }
};

1509. Minimum Difference Between Largest and Smallest Value in Three Moves

其实可以在线性时间内解决,分别找出一个list 中最大的三个数字和最小的三个数字,但实现起来很复杂。

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
    // 时间复杂度 nlogn,使用排序。求解最大数和最小数之间的最小差值。一般来说可以按照贪心的思路求解,修改最大或者最小的三个数字。
    // 那么有四种组合:修改最大的三个数字,修改最大两个和最小的一个,修改最大一个 和最小两个,修改最小三个,然后比较四种方案的最小值
    int minDifference(vector<int>& nums) {
        if (nums.size() <=4 ) return 0;
        int n =nums.size();
        sort(nums.begin(), nums.end());
        return min( min(nums[n-4] -nums[0], nums[n-3] -nums[1]), min(nums[n-2] -nums[2], nums[n-1] -nums[3]));
    }
};

python 实现(可以简化min 函数的书写)

1
2
3
4
5
6
class Solution:
    def minDifference(self, nums: List[int]) -> int:
        if len(nums) <= 4: return 0
        n =len(nums)
        nums.sort()
        return min( nums[n-4] -nums[0],  nums[n-3] -nums[1], nums[n-2] -nums[2], nums[n-1] -nums[3])

1510. Stone Game IV

LeetCode 最后一题一般都是一个模板题。(所以多积累一些)

经典的博弈论问题。必胜或者必败,不存在平局。

必胜态:能走到某一个必败态;必败态:只能走到必胜态。定义 f[i] 的状态,可能是必胜态也可能是必败态。

c++实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:
    bool winnerSquareGame(int n) {
        vector<bool> st(n+1);
        st[0] =false;
        
        for(int i =1; i<=n ; i++)
            for(int j=1; j *j <=i; j++)
                if ( st[i -j *j] ==false)
                {
                    st[i] =true;
                    break;
                }
        return st[n];
    }
};

python 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def winnerSquareGame(self, n: int) -> bool:
        st =[ False] *(n +1)
        for i in range(1,n+1):
            j =1
            while j *j <=i:
                if st[i -j *j] == False:
                    st[i] =True
                    break
                j +=1
        return st[n]
        

1512. Number of Good Pairs

数据范围是 100,那么暴力求解就行。那么便是 $n^2$ 的时间复杂度。

python 解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        res =0
        n =len(nums)
        for i in range(0, n):
            j =i +1
            while  j < n:  
                if nums[i] ==nums[j]:
                    res +=1
                j +=1 
        return res

优化成 $nlogn$ 时间复杂度 (python 实现)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        # 先排序,然后双指针。最后的时间复杂是 nlogn
        nums.sort()
        res =0
        n =len(nums)      
        for i in range(n):
            j =i +1
            while j < n and nums[i] ==nums[j]:
                res +=1
                j += 1
        return res

c++ 实现第二种算法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    int numIdenticalPairs(vector<int>& nums) {
        int res =0;
        sort(nums.begin(), nums.end());
        for(int i =0; i< nums.size(); i++)
        {
            int j =i +1;
            while (j < nums.size() && nums[i] ==nums[j])
            {
                res +=1;
                j ++;
            }          
        }
        return res;
    }
};

1513. Number of Substrings With Only 1s

数据量 $O(10^5)$ ,所以可以确定是 $O(n)$ 或者 $nlogn$ 的算法。根据最后一个case 可以知道如果都是1,那么可以使用数学的方式处理,所以就是一个 for-while 循环。空间上可以不必要使用 list 保存。

python解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution:
    def numSub(self, s: str) -> int:
        i =0
        substr =[]
        MOD =1000000007
        while i < len(s):
            if s[i] =='1':
                j =i 
                while j < len(s) and s[j] =='1':
                    j +=1
                substr.append(s[i:j])
                i =j+1
            else:
                i +=1
        res =0
        for sub in substr:
            len_s =len(sub) 
            res +=( (1 +len_s) *len_s //2  %MOD )
        return res

c++ 写法

(在Python中的 substring 也是没有必要的,因为最后参与计算的是 len(substr)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    // 将string 分割成单个的 substring,这些 substring全部都是1
    int numSub(string s) {
        const int MOD =1000000007;
        int res =0;
        for(int i =0; i< s.size(); i ++)
        {
            if (s[i] =='1')
            {
                int j =i ;
                while (j< s.size() & s[j]=='1') j +=1;
                
                long long n =j -i;
                res +=( (n +1)* n/2 %MOD );
                i =j;
            }
        }
        return res;
    }
};

1518. Water Bottles

模拟题的思路,需要使用到取整和取余两个知识点。

c++ 解法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    // 这个是模拟题,每次都是取其商,然后余数是累加到下一次的结果中
    int numWaterBottles(int numBottles, int numExchange) {
        int res =0;
        int t1, t2;
        res += numBottles;
        while (numBottles >= numExchange)
        {   
            t1= (numBottles / numExchange);
            t2 = numBottles % numExchange;
            res += t1;
            numBottles =t1 + t2;
            //cout << t1<< " "<< t2<<" " << numBottles<< endl;
        }
        return res;
    }
};

python 解法

(相同的思路,使用python 进行实现)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution:
    def numWaterBottles(self, numBottles: int, numExchange: int) -> int:
        
        res =0
        res += numBottles
        
        while numBottles >= numExchange:
            
            res += (numBottles // numExchange)
            numBottles = (numBottles //  numExchange) + (numBottles % numExchange)
        
        return res

1519. Number of Nodes in the Sub-Tree With the Same Label

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
    // 数据范围是 10^5, 所以最好是 O(n) 或者 O(nlogn) 的做法
    //  可以在O(n) 时间内完成,从底部开始层序遍历
    // 可以复习一下 树的遍历(dfs,bfs 和层序遍历)
    // 从上到下的层序遍历 可以使用 stack 实现;但是从下往上的层序遍历不会写 (这道题目并不是按照指针的方式给出了 树,而是给出了边,无向图的思路)
    // 树形dp 问题
    vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
        
    }
};

视频讲解:https://www.acwing.com/video/1507/

c++ 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
    
private:
    
    vector<vector<int>> f;
    
    void dfs(int u, int fa, vector<vector<int>> &g)
    {
        for(auto v: g[u])
        {
            if (v!= fa)
            {
                dfs(v, u, g);
                for(int i =0 ; i< 26; i++)
                    f[u][i] += f[v][i];
            }
        }
    }
public:
    // 这个是树形dp 问题
    vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
        
        vector<vector<int>> g(n);
        for(const auto e: edges)
        {
            int a =e[0], b =e[1];
            g[a].push_back(b), g[b].push_back(a);
        }
        f.resize(n, vector<int>(26, 0));
        // 状态表示需要初始化
        for(int i =0; i< n; i++)
            f[i][labels[i]-'a'] ++;
        
        dfs(0, -1, g);
        vector<int> res(n);
        for(int i =0 ; i< n; i++)
            res[i] += f[i][labels[i] -'a'];
        return res;
    }
};

python 实现, python 中使用嵌套函数(inner function) 是很好用的,直接共享了变量。ord() 小函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution:
    # python 实现的时候有 嵌套函数的概念,所以可以好好利用一下
    def countSubTrees(self, n: int, edges: List[List[int]], labels: str) -> List[int]:
        
        
        g =defaultdict(list)
        
        for e in edges:
            a, b =e[0], e[1]
            g[a].append(b)
            g[b].append(a)
            
        
        # 状态转移的表示
        f =[[0] *26 for _ in range(n)]
        
        for i in range(n):
            f[i][ord(labels[i]) -97] +=1
            
        def dfs(cur, pa):
            for v in g[cur]:
                if v != pa:
                    dfs(v, cur)
                    for i in range(26):
                        f[cur][i] += f[v][i]
        
        dfs(0, -1)
        
        res =[0] *n
        for i in range(n):
            res[i] = f[i][ord(labels[i]) -97]

        return res

215. 数组中的第K个最大元素

中轴值 pivot

时间复杂度

递归时,每层时间复杂度为 O(n) ,但并不是都进入左右两部分递归。 仅进入一侧递归在平均情况下数组长度会减半,故时间复杂度为 n+n/2+n/4+…+1=O(n)

注意的点:

partition 函数名; r =ind -1 这个边界值是需要注意的。k 值和 index 之间 相差1. 都是细节。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
    def partition(self,arr, l, r):
        if l >r: return
        key = arr[r]
        while l <r:
            while l <r and arr[l] >= key: l +=1
            arr[r] = arr[l]
            while l < r and arr[r] <= key: r -=1
            arr[l] = arr[r]
        arr[r] =key
        return l
    
    def findKthLargest(self, nums: List[int], k: int) -> int:
        if not  nums: return 
        l, r = 0, len(nums) -1
        while l <r:
            ind = self.partition(nums, l, r)
            if ind < k-1:
                l  =ind +1
            elif ind ==k-1:
                break
            else:
                r =ind -1
        return nums[k-1]

395. 至少有 K 个重复字符的最长子串

如果处理的是字符串问题,并且出现至少 关键词,那么可以考虑 滑动窗口cnt 表示满足出现数量为 k 的字母的个数, dif_cnt 不同字母出现的个数。基本的思路:枚举字符串中出现不同字母的个数,时间复杂度是$26n$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
    def longestSubstring(self, s: str, k: int) -> int:
        res =0
        for i in range(1, len(set(s)) +1):
            l =r = cnt = dif_cnt =0
            times =[0] *26
            while r < len(s):
                ind = ord(s[r]) - ord('a')
                times[ind] +=1
                if times[ind] ==1:
                    dif_cnt +=1
                if times[ind] ==k:
                    cnt +=1
                r += 1

                while l <r and dif_cnt >i:
                    ind = ord(s[l]) - ord('a')
                    if times[ind] ==1:
                        dif_cnt -= 1
                    if times[ind] ==k:
                        cnt -=1
                    times[ind] -=1
                    l +=1
                    
                if i ==dif_cnt ==cnt:
                    res =max(res, r -l)
        return res