LeetCode—3.双指针算法—对撞指针与快慢指针及其leetcode题

引言

  双指针算法在数组问题中极为常用。双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。

一、对撞指针

  对撞指针法,顾名思义,就是指针在两边往中间撞。见三

二、快慢指针

  快慢指针可以理解为:一个跑的快的人和一个跑的慢的人在一个圆形的赛道上赛跑,在某个时刻,跑得快的人一定会从后面赶上跑得慢的人。快指针fast一次走两步,慢指针slow一次走一步,最终会相遇在环中的某个位置(最小公倍数)

有以下两种情况:

1 fast走到链表末,返回null
2 fast = slow,两指针第一次相遇,slow指针同时从相遇位置和最开始位置出发,相遇位置即为环入口(不理解就记住)

在这里插入图片描述

证明:
a = 从开始到环入口步数=4
b = 一环步数 = 5
fast = 2 * slow = 2
slow = 1
相遇时,fast走的步数=slow走的步数+n环步数;fast走的步数还等于slow所走步数的2倍。所以,推出slow走的步数与环的步数相同
fast = slow + nb = > slow = nb

三、LeetCode—167

  给定一个有序整型数组和一个整数target,在其中寻找两个元素,使其和为target。返回这两个数的索引。

例:nums=[2,7,11,15],target=9
输出:[1,2]

  • 如果没有解怎么办?保证有解
  • 如果有多个解怎么样?返回任意解

Note:

nums中有3,3+3+3=9算不?
索引问题,从1开始还是从0开始

解法一:
暴力解法,双层遍历,时间度为O(n^2),容易超时!!!

class Solution:
    def findTwosum(self, num, target):
        """
        在有序数组中,找到两数和为标签
        :param num:
        :param target:
        :return:
        """
        return self.violentSolution(num, target)

    def violentSolution(self, num, target):
        length = len(num)
        # 双循环
        for i in range(length):
            for j in range(length):
                if num[i] + num[j] == target:
                    return [i, j]


def main():
    num = [2, 3, 5, 7, 9, 10]
    target = 9
    s = Solution()
    solution = s.findTwosum(num, target)
    print('返回索引为', solution)


if __name__ == '__main__':
    main()

解法二:
暴力解法并没有充分利用原数组的性质(有序),所以我们应该想到要使用二分搜索,其时间复杂度为O(nlogn).

二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。

二分查找的原理:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

class Solution:
    def findTwosum(self,numbers,target):
        """
        二分查找
        :param num: 数组
        :param target: 值
        :return: 返回索引
        """
        return self.binary_search(numbers,target)

    def binary_search(self,numbers,target):
        n = len(numbers)
        for i in range(n):
            low, high = i + 1, n - 1
            while low <= high:
                mid = (low + high) // 2
                if numbers[mid] == target - numbers[i]:
                    return [i + 1, mid + 1]
                elif numbers[mid] > target - numbers[i]:
                    high = mid - 1
                else:
                    low = mid + 1

def main():
    num = [2, 7, 11, 15]
    target = 9
    s = Solution()
    solution = s.findTwosum(num, target)
    print('索引为', solution)


if __name__ == '__main__':
    main()

解法三:
  对撞指针法,顾名思义,就是指针在两边往中间撞。空间复杂度O(1)

class Solution:
    def findTwosum(self, num, target):
        return self.collision_pointer(num, target)

    def collision_pointer(self, num, target):
        """
        对撞指针
        :param num: 有序数组
        :param target: 标签
        :return: 返回索引
        """
        i, j = 0, len(num) - 1
        while i < j:
            if num[i] + num[j] == target:
                break
            elif num[i] + num[j] > target:
                j -= 1
            else:
                i += 1
        if num[i] + num[j] == target:
            return [i + 1, j + 1]
        else:
            print('List is not solution!!!')


def main():
    num = [2, 7, 11, 15]
    target = 9
    s = Solution()
    solution = s.findTwosum(num, target)
    print('索引为', solution)


if __name__ == '__main__':
    main()

其他用到对撞指针的问题

125.判断是否为回文字符串

给定一个字符串,只看其中的数字与字母,忽略大小写,判断这个字符串是否为回文字符串

class Solution:
    def isPalindrome(self, s):
        return self.pointer(s)

    # 对撞指针解回文字符串
    def pointer(self, s):
        s = s.lower()
        # 去掉除字符串与数字外的其他
        s = [i for i in s if i.isalpha() or i.isnumeric()]
        s = "".join(s)
        i, j = 0, len(s) - 1
        while i < j:
            if s[i] == s[j]:
                i += 1
                j -= 1
            else:
                return False
        return True


def main():
    s = "A man, a plan, a canal: Panama"
    ss = Solution()
    solution = ss.isPalindrome(s)
    print(solution)


if __name__ == '__main__':
    main()

344.倒序字符串

给定一个字符串,返回这个字符串的倒序字符串

class Solution:
    def reverseString(self, s) :
        return self.pointer(s)

    def pointer(self,s):
        i,j = 0,len(s)-1
        while i < j:
            s[i],s[j] = s[j],s[i]
            i += 1
            j -= 1
        return s

def main():
    s = ["h","e","l","l","o"]
    ss = Solution()
    solution = ss.reverseString(s)
    print(solution)

if __name__ == '__main__':
    main()

345.将字符串中元音字母翻转

给定一个字符串,将该字符串中的元音字母翻转

# 元音字母有'a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U'
class Solution:
    def reverseVowels(self, s):
        return self.pointer(s)

    def pointer(self, s):
        s = [i for i in s]
        # print(s)
        i, j = 0, len(s) - 1
        while i < j:
            if s[i] not in ['a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U']:
                i += 1
            elif s[j] not in ['a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U']:
                j -= 1
            else:
                s[i], s[j] = s[j], s[i]
                i += 1
                j -= 1
        s = ''.join(s)
        return s


def main():
    s = "hello"
    ss = Solution()
    solution = ss.reverseVowels(s)
    print(solution)


if __name__ == '__main__':
    main()

四、LeetCode—142

142.环形链表II

给定一个链表,返回链表入环的第一个节点。如果链表无环,则返回null。为了表示给定链表中的环,我们使用整数pos来表示链表尾连接到链表中的位置(索引从0开始)。如果pos是-1,则表示该链表中没有环。pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表

在这里插入图片描述

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

  如果我们用一个Set保存已经访问过的节点,我们可以遍历整个链表并返回第一个出现重复的节点。空间复杂度为O(n)

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        record = set()
        while head:
            if head in record:
                return head
            record.add(head)
            head = head.next
        return None

增加条件:空间复杂度为O(1)

链表知识

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast,slow = head,head
        while fast != None:
            fast = fast.next
            if fast == None:
                break
            fast = fast.next
            slow = slow.next
            if slow == fast:
                break
        # 链表中没有环
        if fast == None:
            return None
        p1,p2 = slow,head

        while p1 != p2:
            p1 = p1.next
            p2 = p2.next
        return p1

猜你喜欢

转载自blog.csdn.net/weixin_46649052/article/details/114419116