一枚菜鸟的leetcode刷题笔记 - Day5

23 - 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

class Solution:
    def merge2Lists(self, l1, l2):
        '''
        l1 and l2 are two linked lists
        '''
        head = ListNode()
        rehead = head
        while l1 and l2:
            if l1.val < l2.val:
                head.next = l1
                l1 = l1.next
            else:
                head.next = l2
                l2 = l2.next
            head = head.next
        if l1:
            head.next = l1
        else:
            head.next = l2
        return rehead.next


    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:
            return None
        else:
            ans = lists.pop()
            while lists:
                ans = self.merge2Lists(ans, lists.pop())
            return ans

先定义一个函数merge2Lists用于合并两个链表,然后对lists里的链表两两合并。
注意要考虑到lists为空的情况,此时应直接返回None
此题被标记为困难,通过单独定义一个函数可以分解问题,以后遇到复杂的问题可以考虑这一点。

26 - 删除排序数组中的重复项

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O ( 1 ) O(1) O(1) 额外空间的条件下完成。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        mark = 0    #mark标记的是在弹出元素后当前指针的实际位置
        for i in range(len(nums)):
            if mark >= 1 and nums[mark] == nums[mark-1]:
                nums.pop(mark)
                mark -= 1
            mark += 1
        return mark

因为题目要求只使用 O(1) 额外空间,所以不可能维护一个要弹出元素下表的列表(要使用 O ( n ) O(n) O(n)的额外空间)。那么自然想到使用指针。
定义一个指针mark,mark-1指向的是弹出元素后的列表的当前位置,也就是说,在每一次循环完成时,nums[:marker-1]都是无重复的有序列表。
索引i只是记录循环,没有别的作用,它保证了列表被完整的走完一次。

小细节:要返回的是列表nums的长度,本应是(mark-1)+1:

  1. mark-1是因为最后一次循环中的mark += 1已经超出了范围,所以要-1;
  2. 再+1是因为mark是从0开始的,所以应该在索引基础上+1

33 - 搜索旋转排序数组

升序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。

请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l, r = 0, len(nums)-1
        while l <= r:
        #必须是<=,因为l=r=mid是一种重要的产生返回值的情况
            mid = (l+r)//2
            if nums[mid] == target:
                return mid
            if nums[l] <= nums[mid]:
            #必须写成<=,因为l=mid时,[l,mid]只有一个值但也是有序的
                if nums[l] <= target < nums[mid]:
                #不能写成nums[l]<=target<=nums[mid-1],因为可能mid=l(==0)
                    r = mid - 1
                else:
                    l = mid + 1
            else:
                if nums[mid] < target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1

参考了官方解答

l和r指针维护的是当前判断的target的左右边界,mid是二分点。在每一次循环时,要分情况讨论,即判断[l,mid-1]和[mid+1,r]哪个才是有序的,对于旋转排序的数组,必然有一个是有序的。而结束的条件是nums[mid]=target,或者l>r还没有产生target.

这题的小细节不少,主要是判断条件的边界,可以自己画一画,以免出错。

你可能感兴趣的:(leetcode,指针,列表,链表,数据结构,leetcode)