LeetCode(一):数组与链表

15 三数之和

链接: https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

方法一:先对数组进行排序,然后通过两个指针求结果

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        res = []

        for i, num in enumerate(nums[:-2]): # 这里枚举到倒数第二个元素是为了防止后面下标越界
            if len(nums) < 3:  # 如果数组本身元素个数小于3,那么返回的结果为空列表
                break 
            if nums[0] > 0: # 如果排序后的数组首元素大于0,那么数组所有元素都大于0,不存在符合要求的三元组,返回空列表
                break
            if nums[0] + nums[1] + nums[2] > 0: # 最小的三个元素相加大于0,不存在符合要求的三元组,返回空列表
                break
            if i > 0 and nums[i] == nums[i - 1]: # 循环过程中遇到与上个元素相同的值,直接进入下一次循环
                continue

            left = i + 1 # 左边的指针,用来定位从哪个位置开始求解
            right = len(nums) - 1 # 右边的指针从数组末尾开始逐步往left靠拢,通过nums[i]+nums[left]+nums[right]判断
            while left < right: 
                if num + nums[left] + nums[right] < 0: #说明left指向的数比较小,需要往下一个数移
                    left += 1
                elif num + nums[left] + nums[right] > 0: #说明right指向的数让结果过大,需要让right指向小一点的值
                    right -= 1
                else:
                    res.append([num, nums[left], nums[right]]) #满足a+b+c=0,将结果以列表的形式放入res中
                    while left < right and nums[left] == nums[left + 1]: 
                        left += 1  #为了去掉重复元素,让left指针移动指向下一个不重复的值
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1  #为了去掉重复元素,让right指针移动指向上一个不重复的值
                    left, right = left + 1, right - 1  # 当left的下一个与right的上一个都与他们本身指向的值不重复时,分别指向下一个和上一个
        return res

方法二:用Python内置的二分查找函数bisect,这个方法是LeetCode上大神的,用Python内置的二分查找函数,方法很巧妙,放在这里供大家参考。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        count = {}

        for i in nums:
            count[i] = count.get(i, 0) + 1

        nums = sorted(count)

        for i, num in enumerate(nums):
            if count[num] > 1:
                if num == 0:
                    if count[num] > 2:
                        res.append([0,0,0])
                else:
                    if -num * 2 in count:
                        res.append([num, num, -num * 2])
            if num < 0:
                twoSum = -num
                left = bisect.bisect_left(nums, twoSum - nums[-1], i + 1)
                for i in nums[left: bisect.bisect_right(nums, twoSum // 2, left)]:
                    j = twoSum - i
                    if j in count and j != i:
                        res.append([num, i, j])

        return res

169 多数元素

链接:https://leetcode-cn.com/problems/majority-element/

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [3,2,3]
输出: 3

示例 2:

输入: [2,2,1,1,1,2,2]
输出: 2

方法一:将数组排序后取中间的值,这里利用了题目中多数元素在数组中出现次数大于 ⌊ n/2 ⌋ 这一点,在这个基础上,排序后的数组位于中间的元素值总是多数元素

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        nums.sort()
        return nums[len(nums)//2]

方法二:用两个变量num和count,num存储元素值,count用于计数,遇到等于num的元素时,num不变,count加一;遇到不等于num的元素时,若count不等于0,那么count减一;当遇到不等于num的元素且count等于0时,num等于新的元素值,count重新加一一直到数组循环结束

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        count = 0
        num = 0
        for i in nums:
            if num != i:
                if count == 0:
                    num = i
                    count += 1
                else:
                    count -= 1
            else:
                count += 1
        return num

206 反转链表

链接:https://leetcode-cn.com/problems/reverse-linked-list/

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

方法一:迭代法反转链表

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

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or head.next == None:
            return head
        curr = head
        prev = None
        Next = head
        while curr:
            Next = curr.next
            curr.next = prev
            prev = curr
            curr = Next
        return prev

方法二:递归反转链表(这个的运行时间和内存消耗都远超迭代法的)

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

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or head.next == None:
            return head
        
        newHead = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return newHead

24 两两交换链表中的节点

链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs/

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3。

方法一:设置一个哑结点在头部,然后用三个指针进行交换,需要搞清楚交换后各指针指向的位置。

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

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        newHead = ListNode()
        newHead.next = head
        curr = head
        prev = newHead

        while curr:
            if not curr.next:
                break
            Next = curr.next
            curr.next = Next.next
            Next.next = curr
            prev.next = Next
            prev = curr
            curr = prev.next
        
        return newHead.next

方法二:递归法

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

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        curr = head.next
        prev = head

        prev.next = self.swapPairs(curr.next)
        curr.next = prev
        return curr

141 环形链表

链接:https://leetcode-cn.com/problems/linked-list-cycle/

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDapwP5K-1601476010823)(G:\数据结构与算法总结\总结图片\LeetCode题目\141-1.png)]

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9rQr6y3U-1601476010826)(G:\数据结构与算法总结\总结图片\LeetCode题目\141-2.png)]

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F3XpuOCb-1601476010827)(G:\数据结构与算法总结\总结图片\LeetCode题目\141-3.png)]

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

方法一:用一个集合存储已经遍历过的节点,当遍历到的新节点在集合中出现过,说明存在环

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

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        haveSeen = set()
        while head:
            if head in haveSeen:
                return True
            haveSeen.add(head)
            head = head.next
        return False

方法二:快慢指针

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

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head or not head.next:
            return False
        fast = head
        slow = head
        while fast and fast.next:
            slow = slow.next
            if fast.next:
                fast = fast.next.next
            else:
                return False
            if slow == fast:
                return True
        return False

方法三:和用集合的方法类似,将遍历过的节点的值都设置为None,当遍历到的结点的值为None时,说明存在环

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

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        while head:
            if head.val is None:
                return True
            head.val = None
            head = head.next
        return False

142 环形链表II

链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

在这里插入图片描述

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

在这里插入图片描述

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

在这里插入图片描述

进阶:
你是否可以不用额外空间解决此题?

方法一:跟上一道题方法一同样的想法,不同在于返回值

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

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        haveSeen = list()
        while head:
            if head in haveSeen:
                return head
            haveSeen.append(head)
            head = head.next
        return head

方法二:快慢指针,假设有环的链表经过n个节点后进入环,且环中有m个节点;当fast指针与slow指针第一次相遇时,fast指针走过的步数是slow指针的两倍,因为fast指针每次走一步,因此fast指针走过的步数为f=2s,又fast指针比slow指针多走整数圈环,因此f-s=am,因此s = am。接下来,指针每次经过进入环的节点所走的步数为n+xm,那么只要slow指针再走n步就能到进入环的节点。令fast=head,当fast与slow都走n步后会在入口节点相遇,此时fast指向的节点即为要求的节点。

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

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast = head
        slow = head
        while True:
            if not fast or not fast.next:
                return
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                break
        fast = head
        while fast!= slow:
            fast, slow = fast.next, slow.next
        return fast

25 K个一组翻转链表

链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例:

给你这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明:

你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

方法一:迭代法

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

class Solution:
    def reverseList(self, head: ListNode, tail: ListNode) -> ListNode:
        pre = None
        curr = head
        while curr != tail:
            Next = curr.next
            curr.next = pre
            pre = curr
            curr = Next
        return pre

    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        newHead = ListNode(0)
        newHead.next = head
        pre = newHead
        end = newHead
        while(end.next):
            for i in range(k):
                if end:
                    end = end.next
            if not end:
                break
            start = pre.next
            nex = end.next
            pre.next = self.reverseList(start, nex)
            start.next = nex
            pre = start
            end = pre
        return newHead.next

方法二:递归

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

class Solution:
    def reverseList(self, head: ListNode, tail: ListNode) -> ListNode:
        pre = None
        curr = head
        while curr != tail:
            Next = curr.next
            curr.next = pre
            pre = curr
            curr = Next
        return pre

    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        if not head or not head.next:
            return head
        end = head
        for i in range(k):
            if not end:
                return head
            end = end.next
        
        newHead = self.reverseList(head, end)
        head.next = self.reverseKGroup(end, k)
        return newHead
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值