leetcode 51~60 学习经历
- 51. N 皇后
 - 52. N 皇后 II
 - 53. 最大子数组和
 - 54. 螺旋矩阵
 - 55. 跳跃游戏
 - 56. 合并区间
 - 57. 插入区间
 - 58. 最后一个单词的长度
 - 59. 螺旋矩阵 II
 - 60. 排列序列
 - 小结
 
51. N 皇后
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额额。。。这咋弄,暴力尝试吧
class Solution:
    def solveNQueens(self, n: int) -> list[list[str]]:
        r = []
        def findAnswer(n,m,arr,u):
            for i in range(n):
                dp = [[x for x in row] for row in arr]
                ready = u
                if dp[m][i] == ' ':
                    dp[m][i] = 'Q'
                    ready += 1
                    if ready == n:
                        r.append([''.join([x if x == 'Q' else '.' for x in row]) for row in dp])
                    for j in range(1,n - m):
                        dp[m + j][i] = 'X'
                        if i + j < n:
                            dp[m + j][i + j] = 'X'
                        if i - j >= 0:
                            dp[m + j][i - j] = 'X'
                    if m < n - 1:
                        findAnswer(n,m + 1,dp,ready)
        findAnswer(n,0,[[' ' for _ in range(n)] for _ in range(n)],0)
        return r
 

 这次是这么个路子,每行放1个,放下之后,剩余行部分地方就不能放了,我用X来占领,并记录已经放成功几个,当放成功的数量和 n 相等,记录这个放法
然后调整了两个判断条件,不符合就提前跳出
class Solution:
    def solveNQueens(self, n: int) -> list[list[str]]:
        r = []
        def findAnswer(n,m,arr,u):
            if arr[m].count('') == 0:
                return
            for i in range(n):
                if arr[m][i] != '':
                    continue
                dp = [[x for x in row] for row in arr]
                dp[m][i] = 'Q'
                ready = u + 1
                if ready == n:
                    r.append([''.join([x if x == 'Q' else '.' for x in row]) for row in dp])
                for j in range(1,n - m):
                    dp[m + j][i] = '.'
                    if i + j < n:
                        dp[m + j][i + j] = '.'
                    if i - j >= 0:
                        dp[m + j][i - j] = '.'
                if m < n - 1:
                    findAnswer(n,m + 1,dp,ready)
        findAnswer(n,0,[['' for _ in range(n)] for _ in range(n)],0)
        return r
 

 然后就抓瞎了,才刚及格的排名,抄答案去
然后就是蚊香眼。。。。大佬们都是用数字表示状态,根本没用矩阵、字符之类的状态。。。厉害了
试试能不能修改下自己的代码
class Solution:
    def solveNQueens(self, n: int) -> list[list[str]]:
        r = []
        def findAnswer(n,arr,s):
            row = len(arr)
            if row == n:
                r.append(['.' * arr[x] + 'Q' + (n - arr[x] - 1) * '.' for x in range(n)])
                return
            for i in range(n):
                if i in arr or row * 10 + i in s:
                    continue
                u = set()
                for x in range(1,n - row):
                    if i + x < n:
                        u.add((row+x) * 10 + i + x)
                    if i - x >= 0:
                        u.add((row+x) * 10 + i - x)
                findAnswer(n, arr + [i], s.union(u))
        findAnswer(n,[],set())
        return r
 

 大佬关于判断位置可用的部分没看懂,还是保留了自己的不可用判定,效率还是没达到大佬的程度啊
52. N 皇后 II
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:1
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/n-queens-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
和51一样的题目,就是一个输出方案详情,一个输出方案数量,拿来改改先用着
class Solution:
    def totalNQueens(self, n: int) -> int:
        def findAnswer(n,arr,s):
            r = 0
            row = len(arr)
            if row == n:
                return 1
            for i in range(n):
                if i in arr or row * 10 + i in s:
                    continue
                u = set()
                for x in range(1,n - row):
                    if i + x < n:
                        u.add((row+x) * 10 + i + x)
                    if i - x >= 0:
                        u.add((row+x) * 10 + i - x)
                r += findAnswer(n, arr + [i], s.union(u))
            return r
        
        return findAnswer(n,[],set())
 

 然后又看了看大佬们的答案,40ms的和天书一样了。。。至于更前面的,are you kidding me?
 
 看来这种得出单一数字答案的题目,除非用例够多,否则就这么几个十几个用例,直接答案给你。。。
53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
https://blog.csdn.net/superwfei/article/details/128983274?spm=1001.2014.3001.5502,这是之前老顾用二维数组的做法,结果把这个办法放到 leetcode,直接超时了。。。
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        l = len(nums)
        mx = nums[0]
        dp = [[0]*(l+1) for _ in range(l)]
        dp[0][0] = nums[0]
        for i in range(1,l):
            dp[i][0] = nums[i]
            for j in range(i):
                dp[i][j+1] = nums[i] + dp[i-1][j]
                mx = max(mx,dp[i][j+1],dp[i][0])
        return mx
 

 因为这次不求坐标,所以这个办法估计是真不适用了
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        mx = 0
        mxi = nums[0]
        s = 0
        for i in range(len(nums)):
            mxi = max(mxi,nums[i])
            s += nums[i]
            if s < 0:
                s = 0
            if mxi > 0:
                mx = max(mx,s)
        return mx if mxi > 0 else mxi
 

换了个顺序加法的,结果成绩不理想,另外,分治法是啥?先不管,再删一个变量,提前预判一下结果
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        mx,s = max(nums),0
        if mx <= 0:
            return mx
        for i in range(len(nums)):
            s += nums[i]
            if s < 0:
                s = 0
                continue
            mx = max(mx,s)
        return mx
 

 现在,可以去抄答案了
 

 有看没有懂,有了方针没有方案。。。。。抄错地方了
# 以下内容抄自 leetcode 第53题 python 68ms 答案
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max = nums[0]
        sum = 0
        for i in nums:
            if sum <= 0 :
                sum = i
            else :
                sum += i
            if sum > max:
                max = sum
        return max
 
也没有分治法啊?啥情况?这个是运算中判断比我少一些,看代码看不出来,逻辑上少了一些。。。
54. 螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
老顾天真了,开始以为都是 m * n 的连续自然数,结果中间踩坑了,甚至矩阵里还有相同的数字。。。。
哎。。。。提交了三次才正确
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        arr = []
        t = 0
        while len(matrix) > 0:
            if t % 4 == 0:
                arr += matrix[0]
                matrix.pop(0)
            elif t % 4 == 1:
                for j in range(len(matrix)):
                    arr.append(matrix[j][-1])
                    matrix[j].pop(-1)
            elif t % 4 == 2:
                arr += matrix[-1][::-1]
                matrix.pop(-1)
            else:
                for j in range(len(matrix),0,-1):
                    arr.append(matrix[j - 1][0])
                    matrix[j - 1].pop(0)
            if len(matrix) == 0 or len(matrix[0]) == 0:
                break
            t += 1
        return arr
 

#以下内容抄自 leetcode 54题 python 16ms 答案
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        up,down,left,right = 0,len(matrix)-1,0,len(matrix[0])-1
        ans = []
        while len(ans) < len(matrix)*len(matrix[0]):
            if up <= down:
                for i in range(left,right+1):
                    ans.append(matrix[up][i])
                up = up + 1
            if left <= right:
                for i in range(up,down+1):
                    ans.append(matrix[i][right])
                right = right - 1
            if up <= down:
                for i in range(right,left-1,-1):
                    ans.append(matrix[down][i])
                down = down - 1
            if left <= right:
                for i in range(down,up-1,-1):
                    ans.append(matrix[i][left])
                left = left + 1
        return ans
 
嗯,也是记录方向,不过没有对原数组操作,相当于4指针。。。。巧妙啊
55. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个题好熟悉,好像做过!第45题!这就和 N皇后,N皇后2是同一个内容,返回不同结果一样,没啥说的,调整下就提交
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        if n < 2:
            return True
        pos = 0
        while True:
            if pos + nums[pos] >= n - 1:
                return True
            if nums[pos] == 0:
                return False
            mx,nxt = 0,0
            for i in range(nums[pos],-1,-1):
                if i + nums[pos + i] > mx:
                    mx = i + nums[pos + i]
                    nxt = i
            pos += nxt
 

56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/merge-intervals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这个就没必要用交集了吧,排序后合并就好
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        if len(intervals) == 0:
            return []
        intervals.sort()
        r = [intervals[0]]
        for i in range(1,len(intervals)):
            if intervals[i][0] <= r[-1][1]:
                r[-1][1] = max(intervals[i][1],r[-1][1])
            else:
                r.append(intervals[i])
        return r
 

# 以下内容抄自 leetcode第56题 python 32ms答案
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals = sorted(intervals, key=lambda x: x[0])
        ans = [intervals[0]]
        for i in range(1, len(intervals)):
            tmp = ans[-1]
            if tmp[1] < intervals[i][0]:
                ans.append(intervals[i])
            elif tmp[1] < intervals[i][1]:
                tmp[1] = intervals[i][1]
        return ans
 
嗯,减少了一次判断赋值。。。。
57. 插入区间
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]
示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。
示例 3:
输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]
示例 4:
输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]
示例 5:
输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/insert-interval
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
额。。。无重叠的是重点,不过先忽略他,按上一题做法做一次
class Solution:
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        n = len(intervals)
        if n == 0:
            return [newInterval]
        intervals = sorted(intervals + [newInterval])
        r = [intervals[0]]
        for i in range(1,n + 1):
            if intervals[i][0] > r[-1][1]:
                r.append(intervals[i])
            elif intervals[i][1] > r[-1][1]:
                r[-1][1] = intervals[i][1]
        return r
 

 然后,无重叠,写了个自行插入的。。。效率有点不尽如人意,还是再去抄一抄吧。。。
然后,被24ms大佬用类似指针的操作秀了一脸。。。
58. 最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/length-of-last-word
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
嗯。。随便用 split 测了一下,他中间应该没有出现 school-bag 这样的然后要求返回 bag 长度的情况
class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        return len(s.split()[-1])
 
就算是用循环,从后向前读字符,也不会浪费多少时间
class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        r = ''
        for i in range(len(s) - 1,-1,-1):
            if len(r) > 0 and s[i] == ' ':
                break
            if s[i] != ' ':
                r += s[i]
        return len(r)
 

 没什么好说的
59. 螺旋矩阵 II
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/spiral-matrix-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这次是我自己控制矩阵了吧,不用再考虑什么重复啦,方向啦
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        dp = [[0] * n for _ in range(n)]
        x,y,row,col = 0,0,0,1
        for i in range(n ** 2):
            dp[x][y] = i + 1
            x += row
            y += col
            if col == 1 and (y + col >= n or dp[x][y + col] != 0):
                row,col = 1,0
            if col == -1 and (y + col < 0 or dp[x][y + col] != 0):
                row,col = -1,0
            if row == 1 and (x + row >= n or dp[x + row][y] != 0):
                row,col = 0,-1
            if row == -1 and (x + row < 0 or dp[x + row][y] != 0):
                row,col = 0,1
        return dp
 

 
 四个指针的办法,就不用了,咱这个就很暴力,很顺滑了
60. 排列序列
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。
示例 1:
输入:n = 3, k = 3
输出:“213”
示例 2:
输入:n = 4, k = 9
输出:“2314”
示例 3:
输入:n = 3, k = 1
输出:“123”
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/permutation-sequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
啊, 啊啊,想起来第 31 题,下一个排列,当时就把我弄的有点蒙,嗯这次更好,不问下一个了,直接问,从第一个开始,下边第n个是啥排列,就不抄自己以前额答案了,从新写个看看有没有进步
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        r = [_ + 1 for _ in range(n)]
        def move(arr):
            n = len(arr)
            for i in range(n - 1,0,-1):
                l = arr[i - 1]
                c = arr[i]
                r = arr[i + 1:]
                if c > l:
                    if len(r) == 0:
                        arr[i],arr[i - 1] = arr[i - 1],arr[i]
                        return arr[:i] + sorted(arr[i:])
                    k = [k for k in r if k > l]
                    if len(k) == 0:
                        arr[i],arr[i - 1] = arr[i - 1],arr[i]
                        return arr[:i] + sorted(arr[i:])
                    if len(k) > 0:
                        mn = min(k)
                        return arr[:i - 1] + [mn] + sorted([n for n in arr[i - 1:] if n != mn])
            arr.sort()
            return arr
        for i in range(1,k):
            r = move(r)
        return ''.join([str(i) for i in r])
 

 我去。。。。正确率倒是有了,效率这么低?
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        #n,k=4,9
        r = [_ + 1 for _ in range(n)]
        def P(n):
            if n < 2:
                return 1
            return n * P(n - 1)
        def move(arr):
            n = len(arr)
            for i in range(n - 1,0,-1):
                l = arr[i - 1]
                c = arr[i]
                r = arr[i + 1:]
                if c > l:
                    if len(r) == 0:
                        arr[i],arr[i - 1] = arr[i - 1],arr[i]
                        return arr[:i] + sorted(arr[i:])
                    k = [k for k in r if k > l]
                    if len(k) == 0:
                        arr[i],arr[i - 1] = arr[i - 1],arr[i]
                        return arr[:i] + sorted(arr[i:])
                    if len(k) > 0:
                        mn = min(k)
                        return arr[:i - 1] + [mn] + sorted([n for n in arr[i - 1:] if n != mn])
            arr.sort()
            return arr
        k = k % P(n) if k % P(n) != 0 else k
        res = 0
        for i in range(1,n):
            if P(i + 1) <= k:
                res = i + 1
            else:
                break
        print(res,P(res))
        k -= P(res) - 1
        r = r[:n - res] + r[n - res:][::-1]
        for i in range(1,k):
            r = move(r)
        return ''.join([str(i) for i in r])
 

 加了一个阶乘后,效率好了一点,那么,就把可以用递归的全部用递归完成
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        if n == 1:
            return '1'
        if n == 2:
            return '12' if k % 2 == 1 else '21'
        k -= 1
        r = [_ + 1 for _ in range(n)]
        def P(n):
            if n < 2:
                return 1
            return n * P(n - 1)
        for i in range(n,0,-1):
            if k >= P(i):
                p = k // P(i)
                r[-i - 1],r[-i - 1 + p] = r[-i - 1 + p],r[-i - 1]
                r = r[:-i] + sorted(r[-i:])
                k = k % P(i)
        return ''.join([str(i) for i in r])
 

 这里用阶乘代替了数字移动。。。比如4个数,初始是1234,这个对应的是1,然后移动2次,就是2的阶乘1次,得到的就是1324,如果是移动4次,就是2的阶乘两次,那就是1423
1234 0 k == 1
1243 1 k == 2
1324 2
1342 3
1423 4
2134 6
3124 12
4123 18
可以发现规律,那就是阶乘次数表示多少个数字移动,2的阶乘移动3个数字,5的阶乘移动6个数字,至于移动哪个数字,看当前移动次数整除阶乘后的结果,所以。。。阶乘用来计算这个太方便了
# 以下内容抄自 leetcode 第60题 python 20ms 答案
class Solution:
    def getPermutation(self, n: int, k: int) -> str:
        l = [str(i) for i in range(1, n + 1)]
        d = ''
        for i in range(n):
            nn = factorial((n - 1))
            d += l.pop(ceil(k / nn) - 1)
            k = k % nn
            print(k)
            n -= 1
        return d
 
额。。。factorial 是阶乘的意思。。。在哪个包里?这个大佬更巧妙的根据阶乘 pop 掉某个位置的数字,用来拼接成字符串
小结
这次这10题不算难,第60题需要考虑到最大组合就是阶乘数量,根据这个就可以方便的得到结果了,如果想不到。。。那就真抓瞎了。
 
 慢慢刷题,点点积累,日日进步







![[数据结构]:07-二叉树(无头结点)(C语言实现)](https://img-blog.csdnimg.cn/c03810c0ee5a4796b7c06d9dd88cdce8.png)















