习惯写左闭右闭,终止条件尽量放最前面,放后面有可能递归再经过一次处理就出不来了
顺序存储查找定位的题目优先想是否为二分查找的变形(二分查找的条件太苛刻了感觉,但是效果也很好,所以如果是顺序存储的话,尽量先想二分查找)
class Solution:
def search(self, nums: List[int], target: int) -> int:
def binary(low,high):
if low>high:
return -1
mid=(low+high)//2
tmp=nums[mid]
if target==tmp:
return mid
elif target>tmp:
return binary(mid+1,high)
else:
return binary(low,mid-1)
return binary(0,len(nums)-1)
1.快慢指针目前见过的有俩种,一种是fast一直动,slow遇到特定条件才动,一种是fast和slow以不同的速度动,日后见过更多的再补充
2.还有一种双指针是左右俩段同时向中间逼近
结果有着数据相邻的要求优先考虑滑动窗口
窗口初始化的逻辑一般与滑动的逻辑相同,所以初始化其实可以就放在滑动的逻辑里面写
满足条件就开滑,滑到不满足条件就再变(初始化就写在这个变里面)
最大滑动窗口模板
while j < len(nums):
判断[i, j]是否满足条件
while 不满足条件:
i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
不断更新结果(注意在while外更新!)
j += 1
最小滑动窗口模板
while j < len(nums):
判断[i, j]是否满足条件
while 满足条件:
不断更新结果(注意在while内更新!)
i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
j += 1
关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。
1.给val删节点
移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。
所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。(利用虚拟头节点就可以不用分开讨论了)
2.删倒数第n个节点
利用数组存储每个节点及其对应的序号然后删除
双指针,fast先跑n下,然后fast到终点时,slow就是要删的
AO+OC+BO=BO+OC+AO
fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
此时已经可以判断链表是否有环了,那么接下来要找这个环的入口了。
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
那么相遇时: slow指针走过的节点数为: x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = n (y + z) - y
,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
这个公式说明什么呢?
先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。
当 n为1的时候,公式就化解为 x = z
,
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。