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

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=5826个大写字母,26个小写字母,
   还有中间的6个非字母  A~Z[65~90]  非字母[91~96]  a~z[97~122]
3. 滑动窗口的使用:分三种情况来移动窗口:(这里令当前窗口的左右边界分别为l,r,窗口的大小为winSize=r-l+11) 当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题完全相同)

参考

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双针来遍历字符串,一个向字符串的开头,另一个向字符串的结尾。通过比较两个针所向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双针的方法来解决问题。一个向数组的开头,另一个向数组的末尾。通过比较两个针所向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个针来表示滑动窗口的左边界和右边界,通过移动针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双针和滑动窗口算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值