一些双指针的题

个人笔记,待完善

Ⅰ双指针伪代码

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 删除有序数组的重复项
image-20210527002744601
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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值