• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

代码随想录算法训练营第二天 | 滑动窗口系列(209、904、76)<>螺旋矩阵系列(59、54、剑指Offer 29)

武飞扬头像
我爱py数据分析
帮助1

977 有序数组的平方

小记:这道题昨天做了,没有什么思路,还是对双指针的理解太局限了,这里的双指针就是一个指向数组开头、一个指向数组结尾的指针,在通过加和的正负判断二者大小,最后用一个新数组储存值。

下面贴一下代码随想录给出的代码。

代码随想录的代码

(版本一)双指针法
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l, r, i = 0, len(nums)-1, len(nums)-1
        res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
        while l <= r:
            if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
                res[i] = nums[r] ** 2
                r -= 1 # 右指针往左移动
            else:
                res[i] = nums[l] ** 2
                l  = 1 # 左指针往右移动
            i -= 1 # 存放结果的指针需要往前平移一位
        return res


(版本二)暴力排序法
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        for i in range(len(nums)):
            nums[i] *= nums[i]
        nums.sort()
        return nums


(版本三)暴力排序法 列表推导法
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(x*x for x in nums)


学新通

滑动窗口注意点

实现滑动窗口,主要确定如下三点:

窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
学新通

209 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl 1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例2:
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

没思路

在没看任何解析的情况下思考,只能想到先排序后加和的办法,但这明显不是题目想要考察的。自己写的一个半部分代码(无法执行),没有体现出代码随想录中所教授“滑动窗口”的精髓。

我的错误代码

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        start = 0
        n = len(nums)
        count = 0
        for end in range(n):
            count = count   nums[end]
            if count >= target :
                while count >= target:
                    count = count - nums[start]
                    start = start   1
                count = count   nums[start]
                start = start - 1

代码随想录的代码

(版本一)滑动窗口法
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        l = len(nums)
        left = 0
        right = 0
        min_len = float('inf')
        cur_sum = 0 #当前的累加值
        
        while right < l:
            cur_sum  = nums[right]
            
            while cur_sum >= s: # 当前累加值大于目标值
                min_len = min(min_len, right - left   1)
                cur_sum -= nums[left]
                left  = 1
            
            right  = 1
        
        return min_len if min_len != float('inf') else 0



(版本二)暴力法
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        l = len(nums)
        min_len = float('inf')
        
        for i in range(l):
            cur_sum = 0
            for j in range(i, l):
                cur_sum  = nums[j]
                if cur_sum >= s:
                    min_len = min(min_len, j - i   1)
                    break
        
        return min_len if min_len != float('inf') else 0
学新通

我的代码

简单看了视频后,自己写出的代码,也debug了好久,根据报错处理了很多特殊情况,比如返回0的情况,比如数组目前大于target,但是按照while内操作语句,减去第一个元素后,就小于target的情况。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        start = 0
        n = len(nums)
        count = 0
        result = n
        resl = []
        for end in range(n):
            count = count   nums[end] 
            while count >= target:
                subl = end - start   1
                if subl < result :
                    resl = nums[start:end 1]
                result = min(subl,result)
                if count - nums[start] >= target:
                    count = count - nums[start]
                    start = start   1
                else:
                    break
        if result == n and count < target:
            return 0
        else:   
            return result
学新通

我的代码2

看了随想录的代码后,发现是我的上个版本给的result的初始值为n,会造成一些矛盾,才导致多了if判断,改正后如下。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        start = 0
        n = len(nums)
        count = 0
        result = 2*n
        resl = []
        for end in range(n):
            count = count   nums[end] 
            while count >= target:
                subl = end - start   1
                if subl < result :
                    resl = nums[start:end 1]
                result = min(subl,result)
                
                count = count - nums[start]
                start = start   1
        if result == 2*n :
            return 0
        else :
            return result     
学新通

力扣的示例代码

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        left, right, s, res = 0, -1, 0, float("inf")
        for _ in range(len(nums)):
            right  = 1
            s  = nums[right]
            while s >= target:
                res = min(res, right - left   1)
                if res == 1:
                    return 1
                s -= nums[left]
                left  = 1
        return res if res != float("inf") else 0

904 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的最大数目。

示例1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

不会做

可以明确知道用滑动窗口方法,但是由于此题考虑变成了树的ID,而不是一个简单元素的求和sum,不知道如何对ID进行限制。

我的错误代码

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        start = 0
        n = len(fruits)
        count = 0
        result = 0
        resl = []
        tree_id = []
        for end in range(n):
            if fruits[end] is not in tree_id:
                count = count   1 
                tree_id.append(fruits[end])
            if count > 2:
                while len(tree_id) > 2 :
                    start
                subl = end - start   1
                if subl > result :
                    resl = fruits[start:end 1]
                result = max(subl,result)
                
                count = countfruits[start]
                start = start   1
学新通

力扣的示例代码

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        baskets = []
        num = 0   # 连续的当前水果数量
        name = -1 # 当前判断水果类别
        maxFruits = 1
        count = 0
        for fruit in fruits:
            if fruit in baskets:
                if fruit == name:
                    num  = 1
                else:
                    name = fruit
                    num = 1
                count  = 1
            else:
                if len(baskets) != 2:
                    baskets.append(fruit)
                    name = fruit
                    num = 1
                    count  = 1
                else:
                    if maxFruits < count:
                        maxFruits = count
                    baskets = [name]
                    baskets.append(fruit)
                    name = fruit
                    count = num   1
                    num = 1
        return max(maxFruits, count)

学新通

录友的代码

滑动窗口,维护left和right,每次取两个数作为基准,right向右滑动 ··如果fruits[right]不是其中的就更新新的数为基准将left直接移动到right前面一格,然后再向前找left相等的最前面的定位left。 如果fruits[right]是其中的就更新ans

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int left = 0,right = 0,ans = 0;
        int ln = fruits[left],rn = fruits[right];
        while(right < fruits.size()){
            if(fruits[right] == rn || fruits[right] == ln){
                ans = max(ans,right   1 - left);
                right  ;
            }else{
                left = right - 1;
                ln = fruits[left];
                while(left >= 1 && fruits[left - 1] == ln) left--;
                rn = fruits[right];
                ans = max(ans,right   1 - left);
            }
        }
        return ans;
    }
};
学新通

感悟

因为题目中给了只有2个篮子,所以没必要去维护一个元组来记录当前有哪些种类的水果,反而给自己增加难度,就用两个数去记录并判断就好了。

76 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。

注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。

示例 2:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

示例 3:
输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。

不会不会不会!不知道怎么移动start指针

我的错误代码

class Solution:
    def minWindow(self, s: str, t: str) -> str:
       
        start = 0
        end = 0
        n = len(s)
        count = 2*n
        lt = list(t)
        for end in range(n):
            if s[end] in lt:
                lt.remove(s[end]) 
            if lt == []:
                if count < end-start 1:
                    count = end - start   1
                    res = s[start,end 1]
                
学新通

录友的代码1

思路:

采用类似滑动窗口的思路,即用两个指针表示窗口左端left和右端right。 向右移动right,保证left与right之间的字符串足够包含需要包含的所有字符, 而在保证字符串能够包含所有需要的字符条件下,向右移动left,保证left的位置对应为需要的字符,这样的 窗口才有可能最短,此时只需要判断当期窗口的长度是不是目前来说最短的,决定要不要更新minL和minR(这两个 变量用于记录可能的最短窗口的端点)

搞清楚指针移动的规则之后,我们需要解决几个问题,就是怎么确定当前窗口包含所有需要的字符,以及怎么确定left的 位置对应的是需要的字符。 这里我们用一个字典mem保存目标字符串t中所含字符及其对应的频数。比如t=“ABAc”,那么字典mem={“A”:2,“B”:1,“c”:1}, 只要我们在向右移动right的时候,碰到t中的一个字符,对应字典的计数就减一,那么当字典这些元素的值都不大于0的时候, 我们的窗口里面就包含了所有需要的字符;但判断字典这些元素的值都不大于0并不能在O(1)时间内实现,因此我们要用一个变量 来记录我们遍历过字符数目,记为t_len,当我们遍历s的时候,碰到字典中存在的字符且对应频数大于0,就说明我们还没有找到 足够的字符,那么就要继续向右移动right,此时t_len-=1;直到t_len变为0,就说明此时已经找到足够的字符保证窗口符合要求了。

所以接下来就是移动left。我们需要移动left,直到找到目标字符串中的字符,同时又希望窗口尽可能短,因此我们就希望找到的 left使得窗口的开头就是要求的字符串中的字符,同时整个窗口含有所有需要的字符数量。注意到,前面我们更新字典的时候, 比如字符"A",如果我们窗口里面有10个A,而目标字符串中有5个A,那此时字典中A对应的计数就是-5,那么我要收缩窗口又要保证 窗口能够包含所需的字符,那么我们就要在收缩窗口的时候增加对应字符在字典的计数,直到我们找到某个位置的字符A,此时字典中 的计数为0,就不可以再收缩了(如果此时继续移动left,那么之后的窗口就不可能含有A这个字符了),此时窗口为可能的最小窗口,比较 更新记录即可。

from collections import defaultdict
class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        mem = defaultdict(int)
        for char in t:
        	mem[char] =1
        t_len = len(t)

        minLeft, minRight = 0,len(s)
        left = 0

        for right,char in enumerate(s):
        	if mem[char]>0:
        		t_len-=1
        	mem[char]-=1

        	if t_len==0:
        		while mem[s[left]]<0:
        			mem[s[left]] =1
        			left =1

        		if right-left<minRight-minLeft:
        			minLeft,minRight = left,right

        		mem[s[left]] =1
        		t_len =1
        		left =1
        return '' if minRight==len(s) else s[minLeft:minRight 1]
学新通

录友的代码2

/**时间复杂度O(N),空间复杂度O(1);  技术:滑动窗口 计数索引(不知道是不是这样叫,可以理解为简单的Hash表实现)
这道题有一定难度,leetcode438 号题也可使用类似的思路,不过稍微简单一些。

1. 注意到题目的关键:"所有字母的最小子串",也就是说两个串都只能是字母。
2. 于是,可以开辟一个大小为64的数组,来存放数组中字母的频率(Frequency)。准确的说,
   通过字母的ASCII码作为数组的索引,开辟空间的大小为26 6 26=58:26个大写字母,26个小写字母,
   还有中间的6个非字母  A~Z[65~90]  非字母[91~96]  a~z[97~122]
3. 滑动窗口的使用:分三种情况来移动窗口:(这里令当前窗口的左右边界分别为l,r,窗口的大小为winSize=r-l 1)
   1) 当winSize < t.size()  r  ;  也就是窗口右边界向右移动
   2) 当winSize == t.size() :
	   2.1) 当窗口中的字符已经符合要求了,直接返回return,已经找到了
	   2.2) 否则r  ,窗口右边界向右移动
   3) 当winSize > t.size()
	   3.1) 当窗口中的字符已经符合要求了,l  ,窗口左边界向右移动
	   3.2) 否则r  ,窗口右边界向右移动

4. 上面是滑动窗口的使用思路,具体实现上有一定的不同,下面是需要考虑到的要点:
   1) 啥叫作窗口中的字符已经符合要求了?
   2) 窗口滑动时的操作是关键
   3) 要考虑到数组越界的问题

学新通
string minWindow(string s, string t) {
	if (s.size() < t.size()) return "";

	int sFreq[64] = { 0 }, tFreq[64] = { 0 };  // frequency数组
	for (int i = 0; i < t.size(); i  ) tFreq[t[i] - 'A']  ;

	int l = 0, r = -1, edge[2] = { -1, s.size()   1 };  //edge数组表示要求的串的左右边界
	while (l <= s.size() - t.size()) {

		// < t.size()  直接窗口右边界右移,循环continue
		if (r - l   1 < t.size()) {
			if (r   1 < s.size()) {      // 这里注意到数组越界
				sFreq[s[  r] - 'A']  ; continue;
			}
			else break;
		}

		// >= t.size() 先判断当前窗口中的字符是否满足“题目要求”
		int i = 0;
		while (i < 64) {
			if (sFreq[i] < tFreq[i]) break;
			i  ;
		}
		if (i < 64) {
                        // 这里注意到数组越界
			if (r   1 < s.size()) sFreq[s[  r] - 'A']  ;
			else sFreq[s[l  ] - 'A']--;
		}
		else {
			if (r - l   1 == t.size()) return string(s.begin()   l, s.begin()   r   1); 
			else {
				if (r - l < edge[1] - edge[0]) {
					edge[0] = l;
					edge[1] = r;
				}
				sFreq[s[l  ] - 'A']--;
			}
		}
	}
	return edge[0] == -1 ? "" : string(s.begin()   edge[0], s.begin()   edge[1]   1);
}
学新通

录友的代码3

class Solution {
    public String minWindow(String s, String t) {
        /*
        滑窗问题 数组统计
        1.这道题和LC3.最长无重复子串长度 类似,不过这一题更难一点,因为要判断窗口内是否能凑得t
        2.这种窗口内的元素能否凑成t的问题要关注数量,因此需要用HashMap或者数组来维护(A65 a97 z122)
        3.我们不妨固定一个左边界,然后右边界一直向右扫描直至首个满足窗口的元素,此时必定是该左边界为起点最短长度
        4.其他的长度必定比这个更长,因此变动左边界的情况下维护这个最短长度,并且要实时记录左右边界
        时间复杂度:O(m n) 空间复杂度:O(1)
         */
        int lenS = s.length(), lenT = t.length();
        if (lenS < lenT) return "";
        int minL = 0, minR = -1;
        int l = 0, r = -1;
        int[] cntMap1 = new int[58], cntMap2 = new int[58];
        // 统计t的数据
        for (int i = 0; i < lenT; i  ) {
            cntMap1[t.charAt(i) - 'A']  ;
        }
        // 统计窗口有效元素个数
        int cnt = 0;
        while (l < lenS) {
            // 移动r指针直至满足条件
            while (r < lenS && cnt < lenT) {
                // 此时r指针加入窗口
                  r;
                // 当且仅当数目小于目标值才是有效数目
                if (r < lenS &&   cntMap2[s.charAt(r) - 'A'] <= cntMap1[s.charAt(r) - 'A']) cnt  ;
            }
            // 此时的窗口符合条件,若长度更小则可以更新
            if (cnt == lenT && (minR == -1 || r - l < minR - minL)) {
                minL = l;
                minR = r;
            }
            // l指针每一轮都右移一位退出窗口
            if (--cntMap2[s.charAt(l) - 'A'] < cntMap1[s.charAt(l) - 'A']) cnt--;
              l;
        }
        // s[minL,minR]就是答案
        return s.substring(minL, minR   1);
    }
}
学新通

力扣的示例代码

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need = collections.defaultdict(int)
        for c in t:
            need[c]  = 1
        need_count = len(t)

        l = 0; res = (0, float('inf'))

        for r,c in enumerate(s):
            if need[c] > 0:
                need_count -= 1
            need[c] -= 1
            if need_count == 0:
                while True:
                    if need[s[l]] == 0:
                        break
                    need[s[l]]  = 1
                    l  = 1
                if r-l < res[1]-res[0]:
                    res = (l,r)
                need[s[l]]  = 1
                need_count  = 1
                l  = 1
        return '' if res[1]>len(s) else s[res[0]:res[1] 1]
学新通

螺旋矩阵注意点

小记:坚持循环不变量,每一条边都用左闭右开去遍历即可,最后判断,若n为奇数,单独填充中心元素。

59 螺旋矩阵

你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

第一眼看上去没思路

看了代码随想录的解答,坚持循环不变量,每一条边都用左闭右开去遍历即可,最后判断,若n为奇数,单独填充中心元素。

代码随想录的代码

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]
        startx, starty = 0, 0               # 起始点
        loop, mid = n // 2, n // 2          # 迭代次数、n为奇数时,矩阵的中心点
        count = 1                           # 计数

        for offset in range(1, loop   1) :      # 每循环一层偏移量加1,偏移量从1开始
            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums[startx][i] = count
                count  = 1
            for i in range(startx, n - offset) :    # 从上至下
                nums[i][n - offset] = count
                count  = 1
            for i in range(n - offset, starty, -1) : # 从右至左
                nums[n - offset][i] = count
                count  = 1
            for i in range(n - offset, startx, -1) : # 从下至上
                nums[i][starty] = count
                count  = 1                
            startx  = 1         # 更新起始点
            starty  = 1

        if n % 2 != 0 :			# n为奇数时,填充中心点
            nums[mid][mid] = count 
        return nums
学新通

力扣的示例代码

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        # matrix = [[0]*n  for _ in range(n)]
        # dirs = [(0,1),(1,0),(0,-1),(-1,0)]
        # row,col,idx = 0,0,0
        # for num in range(1,n**2 1):
        #     matrix[row][col] = num
        #     dx,dy = dirs[idx]
        #     r,c = row dx,col dy
        #     if r<0 or r>=n or c<0 or c>=n or matrix[r][c]>0:
        #         idx = (idx 1) %4
        #         dx,dy = dirs[idx]
        #     row,col = row dx,col dy
        # return matrix

        #模拟法二
        matrix = [[0]*n for _ in range(n)]
        top,bottom,left,right = 0,n-1,0,n-1
        num = 1
        while top<=bottom and left<=right:
            for col in range(left,right 1):
                matrix[top][col] = num
                num =1
            for row in range(top 1,bottom 1):
                matrix[row][right]= num
                num =1
            if left<right and top<bottom:
                for tp in range(right-1,left,-1):
                    matrix[bottom][tp] = num
                    num =1
                for bt in range(bottom,top,-1):
                    matrix[bt][left] = num
                    num =1
            top =1
            bottom-=1
            left =1
            right-=1
        return matrix

学新通

54 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

我的代码

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        
        m = len(matrix)
        n = len(matrix[0])
        if m == 1:
            return matrix[0]
        elif n == 1:
            return [matrix[i][0] for i in range(m)]
        nums = []
        startx, starty = 0, 0    
        loop = min(m,n)//2           # 起始点
       

        for offset in range(1, loop   1) :      # 每循环一层偏移量加1,偏移量从1开始
            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums.append(matrix[startx][i]) 
                
            for i in range(startx, m - offset) :    # 从上至下
                nums.append(matrix[i][n - offset])
                
            for i in range(n - offset, starty, -1) : # 从右至左
                nums.append(matrix[m - offset][i]) 
                
            for i in range(m - offset, startx, -1) : # 从下至上
                nums.append(matrix[i][starty])
                              
            startx  = 1         # 更新起始点
            starty  = 1
        if m > n :
            if n % 2 != 0:
                first = n // 2
                end = m - 2 * first
                for j in range(first,end first):
                    nums.append(matrix[j][first])
        elif m < n :
            if m % 2 != 0:
                first = m // 2
                end = n - 2 * first
                for j in range(first,end first):
                    nums.append(matrix[first][j])
        else :
            mid = n // 2
            if n % 2 != 0 :			# n为奇数时,填充中心点
                nums.append(matrix[mid][mid])
        return nums
学新通

小记:没看任何参考自己写出来的,根据无数次debug添加了很多判断条件,简介代码还需学习。

力扣的示例代码

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        rowlen = len(matrix)
        columnlen = len(matrix[0])
        cl = 0
        rt = 0
        total = rowlen*columnlen
        i = 0
        t = "right"
        x = 0
        y = 0
        out = []
        while i < total:
            if t == "right":
                x = rt
                for u in range(cl,columnlen):
                    out.append(matrix[x][u])
                    i  = 1
                rt  = 1
                t = "down"
            elif t == "down":
                y = columnlen - 1
                for u in range(rt, rowlen):
                    out.append(matrix[u][y])
                    i  = 1
                columnlen -= 1
                t = "left"
            elif t == "left":
                x = rowlen -1
                for u in range(columnlen -1, cl -1, -1):
                    out.append(matrix[x][u])
                    i  = 1
                rowlen -= 1
                t = "up"
            elif t == "up":
                y = cl
                for u in range(rowlen -1, rt -1, -1):
                    out.append(matrix[u][y])
                    i  = 1
                cl  = 1
                t = "right"
        return out
学新通

根据力扣给出的解答,自己又改进的一版代码:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        
        m = len(matrix)
        n = len(matrix[0])
        nums = []
        startx, starty = 0, 0   
        count = 0
        total = m*n 
        offset = 1
        while count < total:

            if starty == n - offset and startx == m - offset :
                mid = n // 2
                nums.append(matrix[mid][mid])
                count = count   1
                break
            elif starty == n - offset :
                for i in range(startx, m - offset 1):
                    nums.append(matrix[i][n - offset])
                    count = count   1
                break
            elif startx == m - offset :
                for i in range(starty, n - offset 1) :    # 从左至右,左闭右开
                    nums.append(matrix[startx][i]) 
                    count = count   1
                break
       

            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums.append(matrix[startx][i]) 
                count = count   1
                
            for i in range(startx, m - offset) :    # 从上至下
                nums.append(matrix[i][n - offset])
                count = count   1
                
            for i in range(n - offset, starty, -1) : # 从右至左
                nums.append(matrix[m - offset][i]) 
                count = count   1
                
            for i in range(m - offset, startx, -1) : # 从下至上
                nums.append(matrix[i][starty])
                count = count   1

                              
            startx  = 1         # 更新起始点
            starty  = 1
            offset = offset   1

           
       
        return nums
学新通

看下来,还是力扣给出的代码简洁,但是处理逻辑上比代码随想录的坚持左闭右开要复杂一点。

一位录友的思路:

对于这种螺旋遍历的方法,重要的是要确定上下左右四条边的位置,那么初始化的时候,上边up就是0,下边down就是m-1,左边left是0,右边right是n-1。然后我们进行while循环,先遍历上边,将所有元素加入结果res,然后上边下移一位,如果此时上边大于下边,说明此时已经遍历完成了,直接break。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(matrix.empty() || matrix[0].empty()) return {};
        vector<int> res;
        int m = matrix.size(), n = matrix[0].size();
        // 确定上下左右四条边的位置
        int up = 0, down = m - 1, left = 0, right = n - 1;
        while (true)
        { 
            for (int i = left; i <= right; i  ) res.push_back(matrix[up][i]);
            if (  up > down) break;
            for (int i = up; i <= down; i  ) res.push_back(matrix[i][right]);
            if (--right < left) break;
            for (int i = right; i >= left; i--) res.push_back(matrix[down][i]);
            if (--down < up) break;
            for (int i = down; i >= up; i--) res.push_back(matrix[i][left]);
            if (  left > right) break;
        }
        return res;
    }
};
学新通

经录友启发的有一个版本:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        
        m = len(matrix)
        n = len(matrix[0])
        nums = []
        startx, starty = 0, 0   
        count = 0
        total = m*n 
        offset = 1
        while count < total:

            
            if count == total - 1 and m == n and m % 2 != 0:
                nums.append(matrix[startx][startx]) 
                break
            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums.append(matrix[startx][i]) 
                count = count   1
            if count == total - 1 :
                nums.append(matrix[startx][i 1]) 
                break
            for i in range(startx, m - offset) :    # 从上至下
                nums.append(matrix[i][n - offset])
                count = count   1
            if count == total - 1 :
                nums.append(matrix[i 1][n - offset]) 
                break 
            for i in range(n - offset, starty, -1) : # 从右至左
                nums.append(matrix[m - offset][i]) 
                count = count   1
                
            for i in range(m - offset, startx, -1) : # 从下至上
                nums.append(matrix[i][starty])
                count = count   1

                              
            startx  = 1         # 更新起始点
            starty  = 1
            offset = offset   1

           
       
        return nums
学新通

剑指Offer 29 (与54题完全相同)

参考

部分内容参考自:力扣官网、代码随想录。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgekfcc
系列文章
更多 icon
同类精品
更多 icon
继续加载