力扣学习笔记 day7

题054.螺旋矩阵

题意

给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

eg

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


解题

我的方法是按照题意进行遍历,对矩阵进行螺旋式的遍历,然后把遍历到的元素加到list中输出。原本的想法是按上下左右四条直线写四个循环,但看到题解中用更改方向的做法,代码整体看上去会更简洁点。

螺旋式的遍历要考虑两点:

  • 螺旋时,遍历方向的转换
  • 遍历终止的条件

为了解决上面两个问题,可以用一个 状态矩阵 来记录每个点是否被遍历过:

  • 转换方向时,如果原方向的下一个数已被访问过,或者超出了数组的范围,就右转90度,具体做法是定义一个二维矩阵,存储四个方式上横纵坐标的变换(0,+1),(+1,0),(0,-1),(-1,0)。
  • 当遍历次数达到原矩阵元素总数时,遍历结束。

代码

if not matrix or not matrix[0]:
	return list()
direction=[[0,1],[1,0],[0,-1],[-1,0]]
m,n=len(matrix),len(matrix[0])
l=m*n
res=[]

visited=[[False]*n for _ in range(m)]
did=0 #遍历方向
i,j=0,0
for _ in range(l):
	res.append(matrix[i][j])
	visited[i][j]=True
	#判断是否更改方向
	tmpi,tmpj=i+direction[did][0],j+direction[did][1]
	if not(0<=tmpi<m and 0<=tmpj<n and not visited[tmpi][tmpj]):
		did=(did+1)%4
	i+=direction[did][0]
	j+=direction[did][1]
return res

矩阵的每个元素都访问了一次,时间复杂度是O(mn)


题059.螺旋矩阵 II

题意

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

eg

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


解题

本题和上题的螺旋矩阵其实差不多,区别是一个是取值,一个是赋值。
所以参照上例方案,做法如下:

  • 维护一个方向数组,包含四个变量:(0,+1),(+1,0),(0,-1),(-1,0)
  • 由于赋值范围为1~ n 2 n^2 n2 ,即都是正数,所以定义一个n×n的矩阵,初始化所有值为0,这样判断是否转向时只有两种情况:
    • 触碰边界
    • 节点原值不为0
  • 用cur作为当前赋值的数,每次循环赋值后自增,共循环 n 2 n^2 n2

代码

matrix=[[0]*n for _ in range(n)]
direction=[[0,1],[1,0],[0,-1],[-1,0]]

i,j,cur=0,0,1
did=0 #当前方向
for _ in range(n*n):
	matrix[i][j]=cur
	nexti,nextj=i+direction[did][0],j+direction[did][1]
	if not(0<=nexti<n and 0<=nextj<n and not matrix[nexti][nextj]):
		did=(did+1)%4
	i+=direction[did][0]
	j+=direction[did][1]
	cur+=1

return matrix

题061.旋转链表

题意

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

eg

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL


解题

本题解题的关键点就是 找到链表第l-k个节点 (l为链表长度),然后把它的next设为NULL,并且把原来第一个节点接到最后一个节点后面。

我的解法:链表转成list

如果旋转的是数组,只需分成切片再合并即可,但链表的性质决定了操作的麻烦,所以考虑到学习前几天一道题中对于链表的处理方法,将链表放到list中,这样转换顺序就方便多了。

而且k的值可能比链表长度还长,需要对链表长度取余,考虑这点,我觉得数组是个很好的选择。

思路

  1. 判断可直接返回链表的可能:

    • 链表为空
    • 链表只有一项
    • k为0
    • k为链表长度的整数倍
  2. 以上四种的前三种可写在开头,但第四种需要在获得链表长度后再操作

  3. 将链表放到数组中,然后按照原来的顺序连起来,再进行判断:

    1. k取余后为0:直接输出链表
    2. k不为0:把第l-k项的next设为空,把第一项链接到最后一项之后,输出链表

具体的代码如下:

if not head or not head.next or k==0:#前三种情况
	return head
helper=[]
while head:
	helper.append(head)
	head=head.next
l=len(helper)
k=k%l
for i in range(l-1):
	helper[i].next=helper[i+1]
	

if k==0:#第四种情况
	return helper[0]
else:
	helper[l-1].next=helper[0]
	helper[l-k-1].next=None
	return helper[l-k]
	
#对上方的判断部分进行修改,可以在最后输出helper[l-k]时模一下l,就把k为l整数倍的情况也考虑进去了
helper[l-1].next=helper[0]
helper[l-k-1].next=None
return helper[(l-k)%l]

时间复杂度为O(n)


法二:遍历两次

  1. 通过第一次循环获取链表长度,从而得到 l-k 的值
  2. 第二次遍历到第l-k个节点,进行操作
l,cur=1,head
#一次遍历
while cur.next:
	l+=1
	cur=cur.next
cur.next=head

#二次遍历
for i  in range(l-k%l):
	cur=cur.next

head,cur.next=cur.next,Nonne
return head

法三:快慢指针

快慢指针是做链表题时常用的一种方法,这边找的是l-k个节点,也就是从后往前数第k个节点,所以操作如下:

  1. 快指针先走k步
  2. 而后快慢指针同时开始移动

这样当快指针达到尾部的时候,慢指针指向的就是第l-k个节点

BUT,
如果 k 值比 l 小的话可以直接这样做,本题还是要考虑 k 值比 l 大的情况,换句话说,还是要对 k 模 l ,所以依然要先进行一次遍历获取链表长度

#获取长度略

k%=l
if k==0:
return head

slow=fast=head
#先行k步
for _ in range(k):
	fast=fast.next
#携手并进
while fast.next:
	fast=fast.next
	slow=slow.next

fast.next=head
head=slow.next
slow.next=None
return head
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值