leetcode2021年度刷题分类型总结(九)双指针法 (python)

例一.27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

class Solution(object):
    def removeElement(self, nums, val):
        """
        :type nums: List[int]
        :type val: int
        :rtype: int
        """
        #双指针法(快慢指针)
        #
        slowinx=0
        fastinx=0
        n=len(nums)

        while fastinx<=n-1:
            if nums[fastinx]!=val:
                nums[slowinx]=nums[fastinx]
                slowinx+=1
            fastinx+=1

        return slowinx


        #暴力解法
        #此解法的难点在于循环次数是个变量,即数组的长度在变
        #每次移动数组都要把尾部的冗余数剔除
        #python:for循环range中不允许有变量
        n=len(nums)

        i=0
        while i<=n-1:
            if nums[i]==val:
                for j in range(i+1,n):
                    nums[j-1]=nums[j]
                i-=1 #因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                n-=1 # 此时数组的大小-1
            i+=1
        return n
        

例二.977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        #暴力
        n=len(nums)
        tmp=[]
        for i in range(n):
            tmp.append(nums[i]*nums[i])
        tmp.sort()
        return tmp

        #双指针
        #首位双指针
        n=len(nums)
        tmp=[]
        left,right=0,n-1
        while left<=right:
            if nums[left]<0: #当数组中存在负数
                if -nums[left]>nums[right]:
                    tmp.append(nums[left]*nums[left])
                    left+=1
                else: #当数组中都是正数
                    tmp.append(nums[right]*nums[right])
                    right-=1
            else:
                tmp.append(nums[right]*nums[right])
                right-=1
        # print(tmp)
        tmp.reverse() #也可以先声明一个n长的tmp一维数组,再从后往前填数,就不用reverse了

    
        return tmp

例三.209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        #滑动窗口
        #重点:需要弄清滑动窗口的机制,什么时候左边指针动,什么时候右边指针动
        #while addnums>=target: left+=1
        #当走出while addnums>=target: 右指针动
        n=len(nums)
        left=0
        res=[]

        addnums=0
        cnt=0

        for right in range(n):
            addnums+=nums[right]
            # right+=1
            cnt+=1
            while addnums>=target:
                res.append(cnt)
                # print(res)
                cnt-=1
                addnums-=nums[left]
                left+=1  
           
        if res:
            return min(res)
        return 0

        #暴力解法(超出时间限制)
        n=len(nums)
        res=[]
        for i in range(n):
            addnums=0
            cnt=0
            for j in range(i,n):
                cnt+=1
                addnums+=nums[j]
                if addnums>=target:
                    res.append(cnt)
                    break
        if res:
            return min(res)

        return 0

例四.344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]

class Solution(object):
    def reverseString(self, s):
        """
        :type s: List[str]
        :rtype: None Do not return anything, modify s in-place instead.
        """
        #双指针法
        n=len(s)
        left,right=0,n-1
        tmp=""

        while left<right:
            tmp=s[left]
            s[left]=s[right]
            s[right]=tmp
            left+=1
            right-=1

        return s


        #调库函数 考试时不推荐使用
        # s.reverse()
        # return s

例五.541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
输入:s = “abcdefg”, k = 2
输出:“bacdfeg”

class Solution(object):
    def reverseStr(self, s, k):
        """
        :type s: str
        :type k: int
        :rtype: str
        """
        # s="abcdefg"
        # k=8
        # 双指针法
        s=list(s)
        n=len(s)

        left0,right0=0,k-1 #left 和right各自的初始位置
        while left0<n-1:
            left,right=left0,right0 #初始化left,right
            leng=len(s[left0:n]) #单独处理剩余部分 
            if leng<k: #剩余字符少于 k 个,剩余全部反转
                right=n-1
            #剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,正常程序处理即可
            tmp=""
            
            while left<right:
                tmp=s[left]
                s[left]=s[right]
                s[right]=tmp
                left+=1
                right-=1

            left0+=2*k  #按要求翻转后初始位置+=2*k
            right0+=2*k

        return "".join(s)

        #同样的方法,但把翻转部分写成函数
        def reverse_substring(text):
            left, right = 0, len(text) - 1
            while left < right:
                text[left], text[right] = text[right], text[left]
                left += 1
                right -= 1
            return text
        
        res = list(s)

        for cur in range(0, len(s), 2 * k): #从头到尾步长2k
            res[cur: cur + k] = reverse_substring(res[cur: cur + k]) #前k个翻转
        
        return ''.join(res)

例六.206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        #递归法
        def dfs(pre,curNode):
            if not curNode:
                return pre #curNode=None时,进行最后的返回
            nextNode=curNode.next #保存下一节点
            curNode.next=pre #翻转
            return dfs(curNode,nextNode) #进行下一步
             
        return dfs(None,head) #返回的pre是最终结果


        #双指针法
        #别忘记尾部的None
        tail=None
        if not head:
            return None
        if not head.next:
            return head
        
        curNode=head.next
        nextNode=head.next.next
        head.next=tail
        while curNode and nextNode:
            #翻转
            curNode.next=head
            head=curNode
            curNode=nextNode
            nextNode=curNode.next

        if head and curNode and not nextNode: #处理两个节点
            curNode.next=head

        return curNode

        ##简化版双指针代码
        #(虽然都是双指针,这里pre = None cur = head的方式显然更简单,可以省去很多情况讨论)
        cur = head   
        pre = None
        while(cur!=None):
            temp = cur.next # 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur.next = pre #反转
            #更新pre、cur指针
            pre = cur
            cur = temp
        return pre

例七.24. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

输入:head = [1,2,3,4]
输出:[2,1,4,3]

思路:通过pre\cur\tmp三个指针的移动来改变链表,同时要注意当节点个数是单数时的情况,还有到了链表尾部的None时特别注意。

class Solution(object):
    def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return None
        if not head.next:
            return head
        
        pre=head
        cur=pre.next
        res=cur

        while cur:
            tmp=cur.next
            cur.next=pre
            if not tmp or not tmp.next:
                pre.next=tmp
                break
            pre.next=tmp.next #当tmp.next为None
            pre=tmp
            cur=tmp.next #当tmp为None


        return res

例八.19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        #双指针+虚拟头节点
        #设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了;还便于返回结果
        #思路:fast先抢跑n+1步,之后slow和fast同步移动,这样slow和fast间就能有n+1的间隔
        #fast到达链表尾部时,slow指向数的下一个数就是要删除的数
        head_dummy=ListNode()
        head_dummy.next=head

        slow,fast=head_dummy,head_dummy
        n+=1
        while n:
            fast=fast.next
            n-=1

        while fast:
            fast=fast.next
            slow=slow.next
        
        slow.next=slow.next.next
        return head_dummy.next


        #直接暴力
        #缺点:走了两遍链表
        leng=1
        head_copy=head
        res=head
        while head_copy.next:
            leng+=1
            head_copy=head_copy.next

        cnt=leng-n #删除第cnt+1个节点
        if cnt==0:
            head=head.next
            return head

        while cnt>1:
            head=head.next
            cnt-=1
        #当cnt==1 或者 cnt>1
        if head.next:
            head.next=head.next.next

        return res

例九.面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        #快慢指针求解链表相交
        #走你走过的路
        headA_copy=headA
        headB_copy=headB

        while headA or headB:
            if headA!=headB:
                if  headA:
                    headA=headA.next
                else:
                    headA=headB_copy
                if  headB:
                    headB=headB.next
                else:
                    headB=headA_copy  
            else:
                return headA

        if not headA and not headB:
            return None

例十.142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
思路:

  1. 判断链表是否环:分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
  2. 如果有环,如何找到这个环的入口:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """

        slow,fast=head,head

        if fast==None:
            return None
        
        if fast.next==None:
            return None

        fast=fast.next.next
        slow=slow.next

        while slow!=fast and fast and fast.next:
            fast=fast.next.next
            slow=slow.next

        if slow==fast:
            while slow!=head:
                head=head.next
                slow=slow.next
            return slow


        if fast==None or fast.next==None:
            return None

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值