三月刷题记录-part2
此博文为个人学习笔记,记录2021年3月Leetcode每日一题刷题记录
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]
提示
- m == matrix.length
- n == matrix[i].length
- 1 <= m, n <= 10
- -100 <= matrix [i][j] <= 100
notes
算法题一定要留意提示信息
像这题,m和n的范围都给定的,就不需要做matrix的非空判断,m,n的有效性检查等无效判断了
另外,每个值的范围给定了也是有用的。我的方法就可以利用到这一个条件
分析
螺旋矩阵其实是蛇形矩阵系列题之一,他的遍历不像常规的正序遍历,涉及到"转弯"的一个问题。
- 那么就以这个"转弯"作为切入点,设置四个方向坐标
right: 表示向右,其他类推
right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}- 遍历过的坐标设置值为101,利用提示给定的信息 范围-100—100
那么,后续就可以在遍历时判断坐标是否走过了
能够理解这个方向变化,剩下的看代码注释就好
代码
func spiralOrder(matrix [][]int) []int {
row := len(matrix)
col := len(matrix[0])
// 返回结果
ret := make([]int, row*col)
// 定义方向
// right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}
// directs 是上述的方向集
directs := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
// 初始向右,定义方向索引p 为0
p := 0
direct := directs[p]
// m, n为遍历matrix的行列指针
m := 0
n := 0
// 行列范围提示已经给定了,我们这里可以直接对第一个进行赋值
ret[0] = matrix[0][0]
matrix[0][0] = 101
// flag用来判断转方向后是否发生了移动
// 每转一次方向,flag变为false,移动一次,flag变为true
// 如果连续转了两次方向,那么说明遍历结束了
flag := true
for i := 1; i < len(ret); {
// m, n移动
m += direct[0]
n += direct[1]
// 如果当前值为101,说明遍历过了,==row,==col 说明到达边界了,转方向
if m == row || n == col || m < 0 || n < 0 || matrix[m][n] == 101 {
// 当前位置不对,退一步
m -= direct[0]
n -= direct[1]
// 退一步之后判断flag,为true那么转方向继续
if flag {
p++
p = p % 4
direct = directs[p]
flag = false
continue
}
// 如果flag 为false,那么就是连续发生了两次转向,未移动,那么说明遍历结束了
return ret
}
// 说明是有效移动,将值放入ret,i++,继续循环遍历
ret[i] = matrix[m][n]
i++
matrix[m][n] = 101
// flag = true,表示有移动
flag = true
}
return ret
}
执行结果
由于定义了方向矩阵,存在部分空间消耗
59. 螺旋矩阵Ⅱ
描述
给你一个正整数
n
,生成一个包含1
到n2
所有元素,且元素按顺时针顺序螺旋排列的n x n
正方形矩阵matrix
。
示例
示例1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
示例2:
输入:n = 1 输出:[[1]]
提示
- 1 <= n <= 20
分析
- 首先,看提示:n的范围给定了1到20,所以不必在进行有效性的判定
- 此题和第54题是类似题,都是蛇形矩阵相关题
- 区别在于54题是取值,本题是放值,直接将54的代码进行修改就能满足需求
代码
func generateMatrix(n int) [][]int {
// 初始化
ret := make([][]int, n)
for i := 0; i < n; i++ {
ret[i] = make([]int, n)
}
// flag用来判断转方向后是否发生了移动,如果连续转了两次方向,那么说明遍历结束了
flag := true
// 定义方向
// right = {0,1},down = {1,0}, left = {0,-1}, up = {-1, 0}
// directs 是上述的方向集
directs := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
// aim表示要放的目标个数
aim := n * n
// 初始向右,定义方向索引p 为0
p := 0
direct := directs[p]
// x, y为遍历matrix的行列指针
x := 0
y := 0
ret[0][0] = 1
for i := 1; i < aim; {
// m, n移动
x += direct[0]
y += direct[1]
// 如果当前值为101,说明遍历过了,==row,==col 说明到达边界了,转方向
if x == n || y == n || x < 0 || y < 0 || ret[x][y] != 0 {
// 当前位置不对,退一步
x -= direct[0]
y -= direct[1]
// 退一步之后判断flag,为true那么转方向继续
if flag {
p++
p = p % 4
direct = directs[p]
flag = false
continue
}
// 如果flag 为false,那么就是连续发生了两次转向,未移动,那么说明遍历结束了
return ret
}
ret[x][y] = i+1
i++
// flag = true,表示有移动
flag = true
}
return ret
}
提交结果
蛇形矩阵可以使用不断缩小循环范围的方式去遍历,不设置方向,这样可以在空间上也得到节省。
190.颠倒二进制位
题目链接
分析见代码注释
代码
func reverseBits(num uint32) uint32 {
// 定义返回值类型,赋初值为0
var ret uint32
ret = 0
// uint32,循环32次拿到每一位
for i := 0; i < 32; i++ {
// 将ret左移1位
ret <<= 1
// 如果当前num末位为1,那么ret+1
if num&1 == 1 {
ret += 1
}
// num右移1位
num >>= 1
}
return ret
}
运行结果
61.旋转链表
题解看代码注释
方式一
不借助额外空间,对k取模,移动有限次 核心在于首尾相连+断链
代码
func rotateRight(head *ListNode, k int) *ListNode {
if head == nil {
return head
}
p := head
// 节点总数
size := 1
for p.Next != nil {
p = p.Next
size++
}
// 得到最终要移动的次数
k %= size
// 链首尾相连
p.Next = head
// 记录当前的p: 最后一个位置
last := p
p = head
// 移动值 k次
for i := 0; i < k; i++ {
cur := p.Val
nextVal := p.Next.Val
for j := 0; j < size; j++ {
p.Next.Val = cur
p = p.Next
cur = nextVal
nextVal = p.Next.Val
}
}
// 断链
last.Next = nil
return head
}
运行结果
方式二
借助额外的切片存储所有的节点值,核心在于记录初始位置,可以直接得到移动k步后的值,性能理论上来说或许更快
代码
func rotateRight(head *ListNode, k int) *ListNode {
if head == nil {
return head
}
p := head
// 存储各位置的初始节点
vals := []int{p.Val}
for p.Next != nil {
p = p.Next
vals = append(vals, p.Val)
}
// 得到最终要移动的次数
k %= len(vals)
p = head
for i := 0; i < len(vals); i++ {
// 当前值来源于当前值-k
p.Val = vals[(len(vals)-k+i) %len(vals)]
p = p.Next
}
return head
}
运行结果
82.删除排序链表中的重复元素
func deleteDuplicates(head *ListNode) *ListNode {
first := &ListNode{
-1, head,
}
p := head
pre := first
// 判断当前值是不是连续相同值, true表示不连续
var flag bool
for p != nil {
flag = true
for p.Next != nil && p.Val == p.Next.Val {
// 如果连续相同,那么flag变为false
flag = false
p = p.Next
}
// 如果是不同值
if flag {
// true表示非连续相同,那么值需要保留,追加到pre后
pre.Next = p
pre = pre.Next
}
p = p.Next
}
pre.Next = nil
return first.Next
}
456.132模式
代码
// 简易stack
type stack struct {
values []int
top int
}
func find132pattern(nums []int) bool {
/*
解题分析:
nums需满足索引:
i < j < k 时, ai < ak < aj
分析:
1. 有效性:若nums.length<3,一定不满足
2. 由最后一个元素开始进行判断:
首先寻找 j < k时, ak < aj的关系
可以想到通过栈结构来维护, 不断将 nums[i] 入栈,如果存在一个 ak<aj的关系,那么存储ak,再将aj入栈
*/
// 有效性判断
if len(nums) < 3 {
return false
}
// 初始化ak, stack
// ak初始 = min,只有当存在 之前出现的值 < 之后出现的值即 ak < aj时, 才给ak赋上元素值
ak, stack := math.MinInt32, stack{make([]int, len(nums)), -1}
// 循环判断
for i := len(nums) - 1; i >= 0; i-- {
// 往前遍历发现nums[i] < ak时,说明 存在 i < j < k, ai < ak < aj了
if nums[i] < ak {
return true
}
// 如果栈不为空, 且当前值>栈顶值,那么就说明 ak<aj存在了 栈里面的值是之前出现过的
// 循环给ak赋值,保证 ak是当前满足关系的 ak最大值
for stack.top > -1 && nums[i] > stack.values[stack.top]{
ak = stack.values[stack.top]
stack.top--
}
// 循环结束以后,将当前值作为 aj入栈
stack.top++
stack.values[stack.top] = nums[i]
}
// 如果遍历完不满足关系,那么返回false
return false
}