LeetCode 刷题记录,以 array专题开刷。

AC

LeetCode 977. Squares of a Sorted Array  

归并思想,然后0(如果有)分成左右两边,右边是平方的正序,左边是平方的倒序。时间复杂度是$O(n)$, 空间是$O(n),$$n$ 是数组的长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution:
    def sortedSquares(self, A: List[int]) -> List[int]:
        l, r =0, len(A)-1
        res =[]
        
        while l<=r:
            if A[r] **2 >=A[l] **2:
                res.append(A[r] **2)
                r -=1
            else:
                res.append(A[l] **2)
                l +=1
        return res[::-1]

1304. Find N Unique Integers Sum up to Zero

题目要求只需要找到一组,那么可以构造一组解。如果是奇数,那么最后一个放0,其他的位置放上相反数。时间复杂度是$O(n)$, 空间是$O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:

    vector<int> sumZero(int n) {
        vector<int> res(n);
        if(n &1) res[n-1] =0;
        
        for(int i =0; i< n /2; i++)
        {
            res[i] =i +1;
            res[i +n/2] =-i -1;
        }
        return res;      
    }
};

LeetCode 1295. Find Numbers with Even Number of Digits

数的性质,对一个数字 $\log 10$并且进行下取整,如果结果是奇数,那么说明是偶数位数。所以这个是可以在$O(n)$,假设log 运算是常数的话。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
    int findNumbers(vector<int>& nums) {
        int counts =0;
        for(int num  : nums)
        {
            if(int(double(log10(num))) &1) counts +=1;
        }      
        return counts ;
    }
};

1266. Minimum Time Visiting All Points

模拟题,最少的步数是 max(x-point[i][0], y-point[i][1]),因为不管是横着走还是竖着走,还是斜着走,长度都是1。所示时间复杂度是$O(n)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:

    int minTimeToVisitAllPoints(vector<vector<int>>& points) {
        
        int ans =0;
        int x =points[0][0], y =points[0][1];
        int n =points.size();
        for(int i =1; i< n; i++)
        {
            ans += max(abs(x -points[i][0]), abs(y -points[i][1]));
            x =points[i][0];
            y =points[i][1];
        }
        return ans;
    }
};

NAC

581. Shortest Unsorted Continuous Subarray

有两种思路。一种是先排序,然后排序前后进行比较,去掉首尾相同的,剩下的长度就是结果,时间复杂度是$O(nlogn)$。第二种思路是将数字划分成三个部分:起始单调递增中间乱序尾部单调递增,时间复杂度是$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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int n =nums.size();
        int st =n;
        for(int i =0; i< n -1; i++) // 前半部分的逆序的index
            if(nums[i] > nums[i+1])
            {
                st= i+1;
                break;
            }
        int ed =-1;
        
        for(int i =n -1; i>=1; i--) // 后半部分的逆序的index
            if(nums[i -1] > nums[i])
            {
                ed =i -1;
                break;
            }
        
        int max_num =INT_MIN, min_num =INT_MAX;
        for(int i =0; i<= ed; i++) // 前中 部分的最大值
            max_num =max(max_num, nums[i]);
        
        for(int i =st; i<n; i++) //中后 部分的最小值
            min_num =min(min_num, nums[i]);
        
        for(int i =0; i< st; i++)
            if(min_num < nums[i])
            {
                st =i;
                break;
            }
        
        for(int i =n -1; i> ed; i--)
            if(max_num > nums[i])
            {
                ed =i;
                break;
            }
        return max(0, ed -st +1);
    }
};

1089. Duplicate Zeros

重复一次 0,随后的数字是依次往后推延。双指针问题, i 表示慢指针, j 表示快指针, 当快指针走到头的时候,开始向回走,时间复杂度是$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
26
class Solution {
public:
    void duplicateZeros(vector<int>& arr) { 
        int n =arr.size();
        int i, j;
        i =j =0;
        while(j <n)
        {
            if(arr[i] ==0)
                j ++;
            i ++;
            j ++;
        }
        i --;
        j --;
        while(i >=0)
        {
            if(j <n)
                arr[j] =arr[i];
            if(arr[i] ==0)
                arr[--j] =0;
            i --;
            j --;
        }      
    }
};

1260. Shift 2D Grid

时间复杂度,总的时间复杂度是$O(mn)$;求解最大公约数是 $O(\log (mn))$ 空间复杂度:in-place 算法

 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
class Solution {
public:
    int gcd(int x, int y) {
        while (y) {
            int t = x % y;
            x = y;
            y = t;
        }
        return x;
    }
    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int n = grid.size(), m = grid[0].size();
        int g = gcd(k, n * m);
        for (int s = 0; s < g; s++) {
            int x = s / m, y = s % m;
            int tmp = grid[x][y];

            while (1) {
                int nx = x, ny = y - k;
                while (ny < 0) {
                    nx = (nx - 1 + n) % n;
                    ny += m;
                }
                grid[x][y] = grid[nx][ny];
                if (nx == s / m && ny == s % m) {
                    grid[x][y] = tmp;
                    break;
                }
                x = nx; y = ny;
            }
        }
        return grid;
    }
};

1252. Cells with Odd Values in a Matrix

cnt_r, cnt_c 表示多少行经过了奇数次更新, 表示多少列经过了奇数次更新,如果最后单元格变成奇数,那么奇数行经过偶次更新。时间复杂度是$O(n)$, 需要申请一个字典,所以空间复杂度是$O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:

    int oddCells(int n, int m, vector<vector<int>>& indices) {
        unordered_map<int, int> r, c;
        int cnt_r =0, cnt_c =0;
        for(auto &v : indices)
        {
            int &vr =r[v[0]], &vc =c[v[1]];
            vr ^= 1; vc ^= 1;
            cnt_r += (vr &1) ? 1: -1;
            cnt_c += (vc &1) ? 1: -1;   
        }
        return cnt_r * (m -cnt_c) + cnt_c *(n -cnt_r);
    }
};

1266. Minimum Time Visiting All Points

模拟题,最少的步数是 max(x-point[i][0], y-point[i][1]),因为不管是横着走还是竖着走,还是斜着走,长度都是1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:

    int minTimeToVisitAllPoints(vector<vector<int>>& points) {
        int counts  =0;
        int x =points[0][0], y =points[0][1];
        int n =points.size();
        for(int i =1; i< n; i++)
        {
            counts += max(abs(points[i][0]- x), abs(points[i][1] -y));
            x =points[i][0];
            y =points[i][1];
        }
        return counts;
    }
};

1217. Play with Chips

思路比较重要:最终的位置只分为奇数和偶数两种.对于奇数位置,所有偶数位置的代价为1;对于偶数位置,所有奇数位置的代价为1。时间复杂度$O(n)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution {
public:
    int minCostToMoveChips(vector<int>& chips) {
        int n =chips.size();
        int x =0;
        for(int i =0; i< n; i++)
            x += chips[i] %2;
        return min(x, n -x);
    }
};

1200. Minimum Absolute Difference

读懂题意之后还是比较好写的,首先是要找出最小的绝对差值,然后根据差值然后再去选择 满足条件的对数。时间复杂度是$nlogn$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    vector<vector<int>> minimumAbsDifference(vector<int>& arr) {
        int n =arr.size();
        sort(arr.begin(), arr.end());
        int min_diff =arr[1] -arr[0];
        for(int i =1; i< n; i++)
        {
            min_diff=min(min_diff, arr[i]- arr[i-1]);
        }      
        vector<vector<int>> res;
        for(int i =1; i< n; i++)
        {
            if(arr[i] -arr[i-1] ==min_diff) 
            res.push_back({arr[i-1], arr[i]}); # 这里补不能直接使用 emplace_back(),因为该函数是以现有的 vector<> 进行后面添加的,所以不能使用构造函数的写法 {}
        }
        return res;                
    }
};

1185. Day of the Week

想说的这样逻辑的计算是令人佩服,从1970 开始,当2100 ,不超过 5000天,所以这个是可以枚举的,枚举的。是可以在 1s 内运算出结果的

 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
43
44
45
46
47
48
49
50
51
52
53
class Solution {
public:

    string dayOfTheWeek(int day, int month, int year) {
        vector<string> days(8);
        days[1] ="Monday";
        days[2] ="Tuesday";
        days[3] ="Wednesday";
        days[4] ="Thursday";
        days[5] ="Friday";
        days[6] ="Saturday";
        days[7] ="Sunday";
        vector<int> md={0,
                            31, 28, 31, 30,
                            31, 30, 31, 31,
                            30, 31, 30, 31};
        int cur_days =5;
        int y =1971, m =1, d =1;
        while( !(y ==year && m ==month && d ==day))
        {
            d ++;
            cur_days ++;
            if(cur_days > 7)
                cur_days =1;
            bool t =false;
            if(y %4 ==0 && y%100 !=0 || y%400 ==0)
                t =true;
            // 是不是闰年的问题
            if(t && m ==2)
            {
                if(d >29)
                {
                    m ++;
                    d =1;
                }
            }
            else{
                if(d >md[m])
                {
                    m ++;
                    d =1;
                }
            }
            if (m > 12)
            {
                m =1;
                y ++;
            }
            
        }
        return days[cur_days];
    }
};

1184. Distance Between Bus Stops

这个是环形公路,给定起点和终点, 返回最短路径,其实路径只有两条,一条是顺时针,一条是逆时针。所以 while 循环就可以搞定,如果不是终点,那么就一直循环。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Solution {
public:
    int distanceBetweenBusStops(vector<int>& distance, int start, int destination) {
        int n =distance.size();
        int tot =0;
        for(int i =0; i< n; i++)
            tot += distance[i];
        int ans =0;
        while(start != destination)
        {
            ans += distance[start];
            start ++;
            if(start ==n)
                start =0;
        }
        return min(ans, tot -ans);
    }
};

905. Sort Array By Parity

是按照快排split() 进行解题。时间复杂度最优是$O(n)$,空间复杂度是$O(1)$, in-place 的做法。

 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> sortArrayByParity(vector<int>& A) {
        if( A.empty()) return A;
        int n = A.size();
        int l=0,  r =n-1;
        // 有点类似快排的思路, 偶数在前面;奇数在后面
        while(l <r)
        {
            while (l<r && (A[r] & 1) ==1) r -=1;
            while(l <r &&(A[l] &1 )==0 ) l ++;
            if(l<r) 
            {
                swap(A[l], A[r]);
                l ++;
                r --;
            }
        }
        return A;   
    }
};

922. Sort Array By Parity II

在上一道题的基础上,进行交换操作。时间复杂度是$O(n)$,空间是 $O(1)$。

 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> sortArrayByParityII(vector<int>& A) {
        int n =A.size();
        int l =0, r =n -1;
        while(l<r)
        {
            while(l <r && (A[r] &1) ==1) r -=1;  // 注意 & 或者 && 的优先级是小于 == 运算符的
            while(l< r && (A[l] & 1) ==0) l ++;
            if(l<r) 
            {
                swap(A[l], A[r]);
                l ++;
                r --;
            }
        }
        for(int i =1, j=n -2; i< j; i +=2, j -=2)
            swap(A[i], A[j]);
        return A;
    }
};

941. Valid Mountain Array

所谓的山峰就是前面是严格递增, 后面是是严格递减;第一个循环找出分界点、

 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 {
public:
    bool validMountainArray(vector<int>& A) {
        int n =A.size();
        if(n <3) return false;
        int pivot =-1;
        for(int i =1; i< n; i++)
        {
            if(A[i-1] ==A[i])
                return false;
            else if(A[i-1] > A[i])
            {
                if(i ==1) return false;
                pivot =i -1;
                break;
            }
        }
        if(pivot ==-1)
            return false;
        for(int i =pivot +1; i<n; i++)
            if(A[i-1] <= A[i]) return false;
        return true;
    }
};

1122. Relative Sort Array

题解:按照 arr2的顺序对 arr1 进行排序,结果是 arr1 相对于 arr2 的顺序。时间复杂度是$O(nlogn)$,实现了自定义的排序方式。如果出现过那么按照index 排序,如果没有出现过,那么按照value 排序。 自定义排序很精髓呀。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution(object):
    def relativeSortArray(self, arr1, arr2):
        def custom_sort(val):
            try:
                index = arr2.index(val)
                return (0, index)
            except ValueError:
                return (1, val)
        return sorted(arr1, key = custom_sort)
    

1287. Element Appearing More Than 25% In Sorted Array

该题思路 和统计出现次数超过一半的数字 是一个思路,时间复杂度是 $O(n)$,空间复杂度是$O(1)$。

 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:
    int findSpecialInteger(vector<int>& arr) {
        int n =arr.size();
        int cnt =1, num =arr[0];
        for(int i =1; i< n; i++)
        {
            if(arr[i] == arr[i-1])
            {
                cnt ++;
            }
            else{
                if(cnt *4 > n) return num;
                else
                {
                    cnt =1;
                    num =arr[i];
                }           
            }
        }
        return num;
    }
};

1299. Replace Elements with Greatest Element on Right Side

好多类似的题目了,求解右边最大的元素。模拟题。倒序遍历,查找最大的元素。时间复杂度是$O(n)$.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    vector<int> replaceElements(vector<int>& arr) {      
        int n =arr.size() , m =-1;
        for(int i =n -1; i>=0; i--)
        {
            int t =arr[i];
            arr[i] =m;
            m =max(m , t);
        }
        return arr;
    }
};

1002. Find Common Characters

实现的时候注意 Counter() 和可以进行集合操(&), 但是 dicitonary 是不能的。时间复杂度分析,$O(mn)$,其中 $m$ 是list 的长度, $n$ 是单个字符串的平均长度。也可以理解为所有的字母,因为每个单词的每个字母都是需要遍历一遍的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution(object):
    def commonChars(self, A):
        """
        :type A: List[str]
        :rtype: List[str]
        """
        if len(A) ==1:
            return list(A[0])
        c =collections.Counter(A[0])
        for i in range(1, len(A)):
            t =collections.Counter(A[i])
            c =c & t
        ans =[]
        for i in c:
            ans += i *c[i]
        return ans
 

1013. Partition Array Into Three Parts With Equal Sum

题目: 如果能够把array 进行三等分,那么return true;否则的话return false。时间复杂度是$O(n)$,当正向找到了 $sum /3$,这个时候从反向查找,如果能够找到那么就返回true,佛则返回 false。

 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:
    bool canThreePartsEqualSum(vector<int>& A) {
        int n = A.size(), sum =0;
        for(int i =0; i< n; i++)
            sum += A[i];
        if(sum %3 != 0) return false;
        int s =sum /3;
        sum =0;
        for(int i =0; i< n; i++)
        {
            sum += A[i];
            
            if(sum ==s)
            {
                sum =0;
                for(int j =n -1; j> i+1; j --)
                {
                    sum += A[j];
                    if(sum ==s)
                        return true;
                }
                return false;
            }
        }
        return false;
    }
};

1018. Binary Prefix Divisible By 5

这个思路还是比较简单的, 因为是二进制的数字,所以返回成 整数之后,然后维护一个 % 5的结果,时间复杂度是$O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    vector<bool> prefixesDivBy5(vector<int>& A) {
        int n =A.size(), tot =0;
        vector<bool> ans;
        for(int i =0; i< n; i++)
        {
            tot = tot*2 + A[i];   
            tot %=5;
            ans.push_back(tot ==0);
        }
        return ans;
    }
};

1160. Find Words That Can Be Formed by Characters

首先说的是这种逻辑结构是非常清楚的,需要对每个单词进行单独的处理,得到的是部分的结果,然后将全部的结果相加。时间复杂度分析$O(nL +m)$,其中 $n$表示 words 的长度,$L$ 表示单词的平均长度, $m$表示chars 的长度。空间复杂度 $O(1)$, 因为这里保存了26 个字母, 是不随着题目中的变量而变化的。

 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:
    int countCharacters(vector<string>& words, string chars) {
        int ans =0;
        vector<int> seen(26, 0);
        for(char & ch : chars)
            seee[ch -'a'] ++;
        for(auto & word : words)
        {

            vector<int> cur(26, 0);
            for(char & ch : word)
                cur[ch -'a'] ++;
            bool flag =true;
            for(int i =0; i< 26; i++)
                if(cur[i] > seen[i])
                    flag =false;   
            if(flag)
                ans += word.length();
        }
        return ans;
    }
};

1051. Height Checker

思路超级简单, 排序之后,统计不在正确位置的个数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int heightChecker(vector<int>& heights) {
        int n =heights.size();
        vector<int> h(heights);
        sort(h.begin(), h.end());
        int tot =0;
        for(int i =0; i< n; i++)
            if(heights[i] != h[i])
                tot ++;
        return tot;
    }
};

665. Non-decreasing Array

时间复杂度是$O(n)$,一次遍历就行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
    bool checkPossibility(vector<int>& nums) {
        int n =nums.size();
        bool chance =true;
        for(int i =1; i< n; i++)
        {
            if(nums[i] < nums[i-1])
            {
                if(!chance ) return false; // 如果是第二次,那么 return false
                // 题目要求最多 改变一个元素的情况下,变成一个非递减,所以
                if(i >1 && i < n -1 && nums[i-2] > nums[i] && nums[i-1]  > nums[i+1])
                    return false;
                chance =false;
            }
        }
        return true;
    }
};

不能说先排序,然后比较index 上的值,因为 4 2 3 是一个bad case。

605. Can Place Flowers

贪心的思想(如果能够插入花,那么就直接插入),时间复杂度是$O(n)$, if( (i ==0 || flowerbed[i-1] ==0) && (i ==flowerbed.size()-1 || flowerbed[i+1] ==0)) 这个判断非常的巧妙,将首尾边界情况和数组中间普通的情况一块考虑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
public:
    bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        for(int i =0; i< flowerbed.size(); i++)
        {
            if(flowerbed[i] ==0)
            {
                if( (i ==0 || flowerbed[i-1] ==0) && (i ==flowerbed.size()-1 || flowerbed[i+1] ==0))
                {
                    flowerbed[i] =1;
                    n --;
                }
            }
        }
        return n <=0;
    }
};

989. Add to Array-Form of Integer

时间复杂度是$O(n)$, 其中的 reverse() 操作是非常的nice:处理了从小到大的数字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    vector<int> addToArrayForm(vector<int>& A, int K) {
        int n =A.size();
        reverse(A.begin(), A.end());
        A[0] +=K;
        int i =0;
        while(A[i] >=10)
        {
            if(i >=n -1)
                A.push_back(0); // 如果超过了位数,那么就补一位
            
            A[i+1]  += A[i] /10;
            A[i] %= 10;
            i ++;
        }
        reverse(A.begin(), A.end());
        return A;      
    }
};

628. Maximum Product of Three Numbers

这个定理用得好:三个数的最大乘积, 必然是是哪个最大的数的乘积或者两个最小的数字(负数)和最大的数字的乘积,所以算法复杂度是 $O(n)$ 或者 $O(nlogn)$。$O(n) $就是写多个for 循环,找出上述的三个数字。$O(nlogn) $排序之后直接进行判断。

1
2
3
4
5
6
7
8
9
class Solution {
public:

    int maximumProduct(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n =nums.size();
        return max(nums[n-1] * nums[n-2] *nums[n-3], nums[0] *nums[1]  * nums[n-1]);
    }
};

566. Reshape the Matrix

实习的是 reshape 函数,时间复杂度是$O(mn)$。

 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<vector<int>> matrixReshape(vector<vector<int>>& nums, int r, int c) {
        int n =nums.size(), m =nums[0].size();
        if(n *m != r *c) return nums;
        vector<vector<int>> res(r, vector<int>(c));
        int a =0, b =0;
        for(int i =0; i< n; i++)
            for(int j =0; j< m ; j++)
            {
                res[a][b] =nums[i][j];
                b ++;
                // 这种代码是非常的简洁
                if(b == c)
                {
                    a ++;
                    b =0;
                }
            }
        return res;
    }
};

832. Flipping an Image

水平旋转是对称交换位置,从 0到1或者从1 到0的转换,使用异或操作^ 就可以完成。时间复杂度是$O(mn)$, 需要遍历每个元素;空间复杂度是$O(1)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    vector<vector<int>> flipAndInvertImage(vector<vector<int>>& A) {      
        int n =A[0].size();
        for(int i =0; i< A.size(); i++)
            for(int j =0; j< (n+1) /2; j ++)
            {
                int tmp = A[i][j] ^1;
                A[i][j] = A[i][n -1-j] ^1;
                A[i][n -1-j] =tmp;
            }
        return A;
    }
};

840. Magic Squares In Grid

合法的矩阵每行每列的和一定是15。枚举所有可行的枚举点,然后以该点为左上角构建矩阵,判断矩阵是否为可行解。判断单个矩阵用常数时间,总共的时间复杂度是 $O(mn)$。

 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
43
44
45
46
class Solution {
public:
    bool check(int x, int y, const vector<vector<int>> & grid)
    {
        vector<int> vis(10, false);
        int row =0, col =0;
        // 每一行的
        for(int i = x; i< x+3; i++)
        {
            int tmp =0;
            for(int j =y; j< y+3; j++){
                if(grid[i][j] >= 10 || vis[grid[i][j]])
                    return false;
                tmp += grid[i][j];
                vis[grid[i][j]] =true;
            }
            if(tmp != 15) return false;
        }
        //每一列的
        for(int j =y; j< y +3; j++)
        {
            int tmp =0;
            for(int i =x; i< x+3; i++)
                tmp += grid[i][j];
            if(tmp != 15)
                return false;
        }
        //两个对角线的
        if(grid[x][y] + grid[x+1][y+1] + grid[x+2][y+2] != 15)
            return false;
        
        if(grid[x][y+2] +grid[x+1][y+1] + grid[x+2][y] != 15)
            return false;
        return true;
    }
    
    int numMagicSquaresInside(vector<vector<int>>& grid) {
        int n =grid.size(), m =grid[0].size();
        int ans =0;
        for(int i =0; i<= n -3; i++)
            for(int j =0; j<= m -3; j ++)
                if(check(i, j, grid))
                    ans ++;    
        return ans;
    }
};

849. Maximize Distance to Closest Person

时间复杂度是$O(n)$,暴力枚举。last 保存是上一个1的位置,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
    // 并没有说求出所在的位置,实际上是求解连续0的个数长度
    int maxDistToClosest(vector<int>& seats) {
        int n =seats.size();
        int last =-1, ans  =-1;      
        for(int i =0; i<n; i++)
        {
            if(seats[i] ==1)
            {
                if(ans ==-1)
                    ans =i;
                else
                    ans=max(ans, (i-last) /2);
                last =i;
            }
        }
        ans =max(ans, n -1- last); #  主要是为了处理这样的bad case [1,0,0,0]
        return ans;
    }
};

867. Transpose Matrix

Transpose(转置)是行数据和列数据互换。这个从理论上也是非常简单的 [i,j] 转化成[j, i]。所以时间和空间复杂度是 $O(mn)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
    vector<vector<int>> transpose(vector<vector<int>>& A) {
        int n =A.size(), m =A[0].size();
        vector<vector<int>> B(m, vector<int>(n));
        for(int i =0; i< n; i++)
            for (int j =0; j< m; j++)
                B[j][i] =A[i][j];
        return B;
    }
};

LeetCode 888. Fair Candy Swap

hash表的用途是提高了查找速度,从之前的查找速度转化成$O(1)$的查找速度。交换两个数字$a$和 $b$,那么两者数组之间的差值会变化 $2|a-b|$,所以如果某个值能变化$|a-b|$,那么是其中的一个解。时间复杂度是$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
class Solution {
public:
    vector<int> fairCandySwap(vector<int>& A, vector<int>& B) {
        int n =A.size(), m =B.size();
        unordered_set<int> set_a(A.begin(), A.end());
        // 这个使用的是构造方法,还可以这样写
        int sum1 =0, sum2 =0;
        for(int & t : A)
            sum1 +=t;
        for(int & t : B)
            sum2 += t;
        int delta =(sum1 -sum2) /2;
        for(int i =0; i< m; i++)
        {
            int t =B[i] + delta;
            if(set_a.count(t))
            {
                return vector<int>{t, B[i]};
            }
        }
        return vector<int>{};
    }
};

896. Monotonic Array

时间复杂度是$O(n)$,判断是否是单调数组。

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

661. Image Smoother

定义两个偏移数组来简化代码,是按照逆时针遍历。这个就是暴力枚举,每个格子都是遍历常数次,时间复杂度是$O(mn)$。

 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 {
public:
    vector<vector<int>> imageSmoother(vector<vector<int>>& M) {
        int n=M.size(), m =M[0].size();
        vector<vector<int>> ans(M);
        int dx[9] ={-1, -1, -1, 0, 1, 1,1, 0, 0}; // 逆时针的顺序
        int dy[9] ={-1, 0, 1, 1, 1, 0, -1 , -1, 0};
        for(int i =0; i< n; i++)
            for(int j =0; j<m; j++)
            {
                int tot =0, cnt =0;
                for(int k =0; k <9; k++)
                {
                    int x =i +dx[k], y = j +dy[k];
                    
                    if(x >=0 && x <n && y>=0 && y<m)
                    {
                        tot += M[x][y];
                        cnt ++;
                    }
                }
                ans[i][j] =tot/ cnt;
            }
        return ans;
    }
};

LeetCode 376. Wiggle Subsequence

从数学表达上还是非常好理解的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        //c++经典的去重的操作
        nums.erase(unique(nums.begin(), nums.end()), nums.end());
        if(nums.size() <= 2) return nums.size();
        int res =2;      
        for(int i =1; i+1 < nums.size() ; i++)
            if(nums[i-1] < nums[i] && nums[i] > nums[i+1] || nums[i-1] > nums[i] && nums[i] < nums[i+1])
                res ++;
        return res;
    }
};

674. Longest Continuous Increasing Subsequence

线性扫描数组,当发现(不合法) $nums[i] <= nums[i -1] $的时候, 则使用 $i-st$ 更新长度,其中 $st$ 表示上一个合法的位置的起点, $i$ 表示当前的位置。时间复杂度是$O(n)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int n =nums.size(), st =0, maxl =0;
        for(int i =1; i<n; i++)
        {
            if(nums[i] <= nums[i-1])// 不是递增的时候才会发生变化
            {
                maxl =max(maxl , i  -st);
                st =i;
            }
        }
        //return maxl;
        return max(maxl, n -st);      
    }
};

643. Maximum Average Subarray I

这种固定了长度的子数组是比较巧妙的, 开始的时候是无脑加入,当长度为 $k$ 的时候,这个是后开始判断子数组长度的问题,最后返回平均值。时间复杂度是 $O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        int n =nums.size(), ans =INT_MIN, tmp =0;
        for(int i =0; i<n; i++)
        {
            tmp += nums[i];
            if(i >= k-1)
            {
                ans =max(ans, tmp);
                tmp -= nums[i -k +1];
            }
        }
        return 1.0* ans / k;
    }
};

LeetCode 561. Array Partition I

贪心的做法:两个比较小的放在一起比 一个大一个小的数字 放在一起最后的结果要大, 一旦排序,那么时间复杂度是 O(nlogn)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n =nums.size();
        int ans =0;
        for(int i =0; i<  n; i+= 2)
            ans += nums[i];
        return ans;
    }
};

485. Max Consecutive Ones

(上面有类似的题目)last 存储上一个符合条件序列的头, i 表示当前遍历的index。时间复杂度是$O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        int n =nums.size();
        int last =0, ans =0;
        for(int i =0; i< n; i++)
            if(nums[i] != 1)
            {
                ans = max(ans, i -last);
                last = i +1;
            }
        ans =max(ans, n -last);
        return ans;
    }
};

217. Contains Duplicate

简单题目,时间复杂度和空间复杂度都是$ O(n)$。注意 unordered_set 的操作, inset() 用于添加元素, find() 用于查找元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> hash;
        int n =nums.size();
        for(int i =0; i< n; i++)
        {
            if(hash.find(nums[i]) != hash.end())
                return true;
            hash.insert(nums[i]);
        }
        return false;
    }
};

219. Contains Duplicate II

对于hash 的维护,并不需要一开始就是将所有的nums 放到hash 映射,在访问判断的过程中可以慢慢放。使用hash 表,其中key 是值,val 是index。所以时间和空间复杂度都是 $O(n)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int, int> hash;
        for(int i =0; i< nums.size() ; i++)
        {
            if(hash.find(nums[i]) != hash.end())
                if(i -hash[nums[i]] <= k)
                    return true;
            hash[nums[i]] =i;
        }
        return false;
    }
};

需要使用两种语言实现,python 中的multiset 是如何实现的?

LeetCode 220. Contains Duplicate III

set(multiset) 是基于平衡树实现的,所以查找的时间效率是$O(logn)$。如果能把 $nums[i] -t <= x$的数字构建成一棵平衡树,找到其中最小的数字 $x$,如果该 $x <= t +nums[i]$,那么就是答案。总共的时间复杂度是$nlogk$, $n$表示结点的个数, $k$是multiset 的大小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#define LL long long
class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        multiset<LL> hash;
        multiset<LL>::iterator it;
        for (int i = 0; i < nums.size(); i++) {
            it = hash.lower_bound((LL)(nums[i]) - t);
            if (it != hash.end() && *it <= (LL)(nums[i]) + t)
                return true;
            hash.insert(nums[i]);
            if (i >= k)
                hash.erase(hash.find(nums[i - k]));
        }
        return false;
    }
};