个人笔记,待完善
Ⅰ双指针伪代码
1快慢指针
慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。
l = 0
r = 0
while 没有遍历完:
if ⼀定条件:
l += 1
r += 1
return 合适的值
2左右端点指针
l = 0
r = n - 1
while l < r:
if 找到了:
return 找到的值
if ⼀定条件1:
l += 1
else if ⼀定条件2:
r -= 1
return 没找到
3固定间距指针
l = 0
r = k
while 没有遍历完:
⾃定义逻辑
l += 1
r += 1
return 合适的值
一 快慢指针
关键是明确快慢指针的含义:此题的快指针是检测当前元素,而慢指针是已经确定的,与前面没有重复的元素的指针。
所以,实际上fast所指向的元素,我们是要检测的,而slow指针则是已经确定了的,与前面没有重复的元素的指针。
刻画出快慢指针的含义后,这个题目就可以写了,同时最关键的一点,在slow和fast初始化的时候,明确含义是非常有必要的,有利于写出简洁易懂的代码。
1 移动零
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AK57elKN-1622987966705)(C:\Users\16229\AppData\Roaming\Typora\typora-user-images\image-20210526231644194.png)]
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
i = j =0
while i<len(nums):
if nums[i] !=0:
nums[j], nums[i] = nums[i], nums[j]
j +=1
i +=1
2 奇数在偶数前
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
i,j=0,0
while i<len(nums):
if nums[i]%2!=0:
nums[j],nums[i]=nums[i],nums[j]
j+=1
i+=1
return nums
2 删除重复元素k=2
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素
'''
-初始化快慢指针 slow , fast ,全部指向索引为 0 的元素。
-fast 每次移动一格
-慢指针选择性移动,即只有写入数据之后才移动。是否写入数据取决于 slow-2 对应的数字和 fast 对应的数字是否一致。
-如果一致,我们不应该写。 否则我们就得到了三个相同的数字,不符合题意
-如果不一致,我们需要将 fast 指针的数据写入到 slow 指针。
-重复这个过程,直到 fast 走到头,说明我们已无数字可写。'''
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
i=j=2
while i<len(nums):
if nums[j-2]!=nums[i]:
nums[j]=nums[i]
j+=1
i+=1#fast
return j#slow
3 删除有序数组的重复项
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
i=j=1
while i<len(nums):
if nums[i] != nums[i-1]:
nums[j]=nums[i]
j +=1
i +=1
return j
#开始都在第一个数字,若双指针指向数字相同快指针走一步,不同两个指针都向前走一步,快指针走完整个数组后,慢指针当前的坐标+1就是数组中不同数字的个数
4 链表寻找环入口~
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。输入:head = [3,2,0,-4], pos = 1输出:返回索引为 1 的链表节点解释:链表中有一个环,其尾部连接到第二个节点
注:这题哈希表更简单
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast == slow:
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return slow
5 合并有序链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
pre=ListNode(-1)
dum=pre
while l1 and l2:
if l1.val<=l2.val:#等号可不写
pre.next=l1
l1=l1.next
else:
pre.next=l2
l2=l2.next
pre=pre.next
if not l1:
pre.next=l2
else: pre.next=l1
return dum.next
6 合并有序数组
#额外数组
nums1 = [1,2,3]
nums2 = [2,5,6]
res=[]
m=len(nums1)
n=len(nums2)
i=j=0
while i<m or j<n:
if i==m:
res.append(nums2[j])
j+=1
elif j==n:
res.append(nums1[i])
i+=1
elif nums1[i]<nums2[j]:
res.append(nums1[i])
i+=1
else:
res.append(nums2[j])
j+=1
print(res)
#没有额外空间,只改变nums1
'''输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]'''
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
k=m+n-1
while n>0 and m>0:
if nums1[m-1]>nums2[n-1]:
nums1[k]=nums1[m-1]
m-=1
else:
nums1[k]=nums2[n-1]
n-=1
k-=1
nums1[:n]=nums2[:n]#当n大于0并且m小于0时 此时nums1中的数组所有元素已经排列过了 而nums2中还剩下n个元素 需要对nums1的前n个赋值为nums2的前n个(直接将前n个进行覆盖)
# nums1[m:] = nums2
# nums1.sort()
7 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
示例 1:
输入: “the sky is blue”
输出: “blue is sky the”
class Solution:
def reverseWords(self, s: str) -> str:
# lst=s.strip().split()
# return " ".join(lst[::-1])
lst=s.strip().split()
l = 0
r = len(lst)-1
while l<r:
lst[l],lst[r] = lst[r],lst[l]
l+=1
r-=1
return " ".join(lst)
8 翻转字符串541
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
输入: s = “abcdefg”, k = 2
输出: “bacdfeg”
class Solution:
def reverseStr(self, s: str, k: int) -> str:
a = list(s)
for i in range(0, len(a), 2*k):
a[i:i+k] = reversed(a[i:i+k])
return "".join(a)
9 两个数组的交集
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
length1, length2 = len(nums1), len(nums2)
intersection = list()
index1 = index2 = 0
while index1 < length1 and index2 < length2:
if nums1[index1] < nums2[index2]:
index1 += 1
elif nums1[index1] > nums2[index2]:
index2 += 1
else:
intersection.append(nums1[index1])
index1 += 1
index2 += 1
return intersection
二 左右端点指针
二分查找,暴力由大到小枚举,有序数组(两数和)
1 和为s的两个数字/两数之和
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
#循环搜索: 当双指针相遇时跳出;计算和 s = nums[i] + nums[j]s=nums[i]+nums[j] ;若 s >target ,则指针 j 向左移动,j=j−1;若s<target,则指针 i向右移动,即执行i=i+1 ;若s=target,立即返回数组 [nums[i], nums[j]][nums[i],nums[j]];
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
i, j = 0, len(nums) - 1
while i < j:
s = nums[i] + nums[j]
if s > target: j -= 1
elif s < target: i += 1
else: return nums[i], nums[j]
return []
-
扩展:两数之和:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: dic1 = {} for index,num in enumerate(nums): if x in dic1: return [dic1[target-num],index] dic1[num] = index
三 固定间距指针
固定窗口大小的滑动窗口
~1 链表倒数第k个元素~
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
pre = cur = head
while k:
cur = cur.next
k-=1
while cur:
cur = cur.next
pre = pre.next
return pre
四 二分查找
1 基础二分
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
class Solution:
def search(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums) - 1
while l <= r:
m = l +(r - l)//2
if nums[m] == target: return m
if target < nums[m]: r = m-1
else:l = m +1
return -1#看题目,不然找不到会返回null
2 旋转数组的最小数字(剑11
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
class Solution:
def minArray(self, numbers: List[int]) -> int:
i,j=0,len(numbers)-1
while i<=j:
mid=i+(j-i)//2
if numbers[mid]>numbers[j]:
i=mid+1
elif numbers[mid]<numbers[j]:
j=mid
else:
j-=1
return numbers[mid] #i=j=mid
3 在排序数组中查找数字
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
class Solution:
def search(self, nums: List[int], target: int) -> int:
def findright(x):
i,j=0,len(nums)-1
while i<=j:
m=i+(j-i)//2
if nums[m]<=x:
i =m+1
else: j=m-1
return i
a=findright(target-1)
b=findright(target)
return b-a
补充:在排序数组中查找元素的第一个和最后一个位置
....
if a==b: return [-1,-1]
return [a,b-1]
4 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
class Solution:
def missingNumber(self, nums: List[int]) -> int:
i,j=0,len(nums)-1
while i<=j:
m=(i+j)//2
if nums[m]==m: i=m+1
else:j=m-1
return i