代码随想录算法训练营Day02 | 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

90 阅读3分钟

LeetCode题目

977.有序数组的平方

题目链接:Squares of a Sorted Array - LeetCode

双指针法

  • 数组 A 有序,则平方之后最大值在数组两端,越靠近中间则平方越小
  • 双指针法,设置 left, right 指向 A 两端,设置 index 指向 result 数组末尾
  • if A[left] * A[left] > A[right] * A[right] { result[index--] = A[left] * A[left] }
  • else { result[index--] = A[right] * A[right] }

代码如下:

func sortedSquares(nums []int) []int {
	n := len(nums)
	left, right, index := 0, n-1, n-1
	result := make([]int, n)
	for left <= right {
		if nums[left]*nums[left] > nums[right]*nums[right] {
			result[index] = nums[left] * nums[left]
			left++
		} else {
			result[index] = nums[right] * nums[right]
			right--
		}
		index--
	}
	return result
}

209.长度最小的子数组

题目链接:Minimum Size Subarray Sum - LeetCode

滑动窗口

  • 使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」
  • 增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求
  • 停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求,同时,每次增加 left,都要更新一轮结果

代码如下:

func minSubArrayLen(target int, nums []int) int {
	start := 0
	result := len(nums) + 1
	sum := 0
	for end := 0; end < len(nums); end++ {
		sum += nums[end]
		for sum >= target {
			subLen := end - start + 1
			if subLen < result {
				result = subLen
			}
			sum -= nums[start]
			start++
		}
	}
	if result == len(nums)+1 {
		return 0
	}
	return result
}

59.螺旋矩阵II

题目链接:Spiral Matrix II - LeetCode

模拟法

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

代码如下:

func generateMatrix(n int) [][]int {
	matrix := make([][]int, n)
	for i := 0; i < n; i++ {
		matrix[i] = make([]int, n)
	}
	startx, starty := 0, 0
	loop := n / 2
	mid := n / 2
	count := 1
	offset := 1
	for loop > 0 {
		i, j := startx, starty
		// 上行从左到右(左闭右开)
		for ; j < n-offset; j++ {
			matrix[i][j] = count
			count++
		}
		// 右列从上到下(左闭右开)
		for ; i < n-offset; i++ {
			matrix[i][j] = count
			count++
		}
		// 下行从右到左(左闭右开)
		for ; j > starty; j-- {
			matrix[i][j] = count
			count++
		}
		// 左列从下到上(左闭右开)
		for ; i > startx; i-- {
			matrix[i][j] = count
			count++
		}
		startx++
		starty++
		offset++
		loop--
	}
	if n%2 == 1 {
		matrix[mid][mid] = count
	}
	return matrix
}

向量法

  • 定义对应点的坐标为 (i, j),每次移动的向量为 (di, dj)
  • 先向右移动,也就是此时的向量为 (0, 1)(注意这里使用行列为坐标,而不是笛卡尔坐标)
  • 如果 A[(i + di) % n][(j + dj) % n] != 0,则需要转向
  • 移动向量更改为 (dj, -di)

代码如下:

func generateMatrix(n int) [][]int {
	// 生成二维数组
	matrix := make([][]int, n)
	for i := 0; i < n; i++ {
		matrix[i] = make([]int, n)
	}
	// [i, j]为二维数组坐标,(di, dj)为移动方向向量
	i, j, di, dj := 0, 0, 0, 1
	for k := 1; k <= n*n; k++ {
		matrix[i][j] = k
		// 判断下一步是否需要转向,多加一个n是为了防止负数下标越界
		if matrix[(i+di+n)%n][(j+dj+n)%n] != 0 {
			di, dj = dj, -di
		}
		// 计算下一步填充坐标
		i += di
		j += dj
	}
	return matrix
}

总结

  1. 双指针法在数组相关算法中运用很广泛
  2. 滑动窗口算法好好理解一下
  3. 螺旋矩阵II中使用的向量法很有趣,该思想应该也可以运用到其他算法中