Python LeetCode(八)

不会做

1. Smallest Range

这道题我一开始的想法是错误的,以为是求由数组中的所有数字中构成的最小区间,但是因为没有考虑到无法保证这个区间包含了所有的子数组中的至少一个元素,所以错误,同时还因为不断的缩小范围,最后发现计算出来的和答案完全是两个不交集的区间,原因在于你缩小的是整体的区间,不代表着在这个区间里面的子区间是最小的,所以错误。

错误思路

class Solution(object):
    def smallestRange(self, nums):
        """
        :type nums: List[List[int]]
        :rtype: List[int]
        """
        # 返回两两数组中符合规定范围的值,不断的缩小区间
        def min_max_arr(a, b):
            mi = max(a[0], b[0])
            ma = min(a[len(a)-1], b[len(b)-1])
            print mi, ma
            return [x for x in sorted(set(a) | set(b)) if x>=mi and x<=ma]
        # 计算数组中两两个值构成的区间最小
        def min_res(mi):
            for i in range(2, len(res)):
                if res[i] - res[i-1] < mi[1] - mi[0]:
                    mi = [res[i-1], res[i]]
                elif res[i] - res[i-1] == mi[1] - mi[0] and res[i] < mi[0]:
                    mi = [res[i - 1], res[i]]
            return mi
        nl = len(nums)
        if nl == 0:
            return []
        res = nums[0]
        for i in xrange(1, nl):
            res = min_max_arr(res, nums[i])
            print res
        rl = len(res)
        if rl <= 1:
            return res+res
        elif rl == 2:
            return [res[0], res[1]]
        mi = [res[0], res[1]]
        return min_res(mi)

炒鸡暴力法

分别用两个循环去选择一个值,构成一个区间,显然时间复杂度非常高。

def smallestRange(nums):
    minx, miny = 0, 1e9
    for i in range(len(nums)):
        for j in range(len(nums[i])):
            for p in range(len(nums)):
                for q in range(j+1 if p == i else 0, len(nums[p])):
                    x = min(nums[i][j], nums[p][q])
                    y = max(nums[i][j], nums[p][q])
                    m, n = 0, 0
                    while m < len(nums):
                        n = 0
                        while n < len(nums[m]):
                            # 只要该行的数字存在于该区间中,就退出该循环
                            if x <= nums[m][n] <= y:
                                break
                            n += 1
                        # 遍历到了最后还是没找到,说明该区间不包含该行数据,不是我们想要的
                        if n == len(nums[m]):
                            break
                        m += 1
                    # 遍历到了最后,说明该区间符合
                    if m == len(nums):
                        if miny - minx > y - x or (y - x == miny - minx and minx > x):
                            minx, miny = x, y
    return [minx, miny]

暴力优化法

我们发现在判断该区间是否符合条件的时候,可以使用二分法来定位,从而减少时间复杂度。

import bisect
def smallestRange2(nums):
    minx, miny = 0, 1e9
    for i in range(len(nums)):
        for j in range(len(nums[i])):
            for p in range(len(nums)):
                for q in range(j+1 if p == i else 0, len(nums[p])):
                    x = min(nums[i][j], nums[p][q])
                    y = max(nums[i][j], nums[p][q])
                    m = 0
                    while m < len(nums):
                        idx = bisect.bisect_left(nums[m], x)
                        # 因为是left,所以不可能在最后,同时原插入位置的值也要在区间中,所以只要无法符合该区间,说明数组中不存在该值,立马退出
                        if idx == len(nums[m]) or nums[m][idx] < x or nums[m][idx] > y:
                            break
                        m += 1
                    if m == len(nums):
                        if miny - minx > y - x or (y - x == miny - minx and minx > x):
                            minx, miny = x, y
    return [minx, miny]

移动窗口法

这种做法非常的巧妙,用一个next数组纪录每一个子数组的最小值的下标,记录下其移动到了哪个位置,比如next[i] = j是指第i+1行(对应子数组)已经移动到了第j+1个位置。我们通过next数组纪录的下标对应的找出相对最小值,相对最大值所在的行,即min_i, max_i,同时不断向后移动最小值所在的列,直到移动到任何一个子数组的最后位置就退出,否则无法保证该子数组的数字存在于区间中。运行的图片可以参考这里

问题1:如何保证区间中的值包含至少一个子数组中的数字?

因为我们通过找所有子数组对应的next下标的最小值和最大值,所以必然包括了每个子数组的值。

问题2:如何保证区间最小?

因为我们通过不断的向后移动相对最小值所在的列下标,找出相对最小值和相对最大值去更新最小最大值。

def smallestRange3(nums):
    minx, miny = 0, 1e9
    next = [0] * len(nums)
    l = len(nums)
    for i in range(l):
        for j in range(len(nums[i])):
            min_i, max_i = 0, 0
            # 找出最大最小值所在的行数
            for k in range(l):
                if nums[max_i][next[max_i]] < nums[k][next[k]]:
                    max_i = k
                if nums[min_i][next[min_i]] > nums[k][next[k]]:
                    min_i = k
            # 计算最小区间
            if miny - minx > nums[max_i][next[max_i]] - nums[min_i][next[min_i]]:
                miny = nums[max_i][next[max_i]]
                minx = nums[min_i][next[min_i]]
            # 移动最小值所在的列数
            next[min_i] += 1
            # 当最小值的列数到了末尾,则返回
            if next[min_i] == len(nums[min_i]):
                return [minx, miny]
    return [minx, miny]

优先队列

和上面一样的思路,利用一个tuple保存值和对应的行及列,然后优先队列会帮你排好相对的顺序,所以你并不需要去遍历计算相对最小值最大值所在的行,也不需要用next去纪录每行的移动下标,只要从队列里面取出来的必然是相对最小值。

import heapq
def smallestRange4(nums):
    pq = [(row[0], i, 0) for i, row in enumerate(nums)]
    heapq.heapify(pq)
    ans = -1e9, 1e9  # tuple
    right = max(row[0] for row in nums)  # 所有元素中第一位数的最大值
    while pq:
        left, i, j = heapq.heappop(pq)  # 顺序有讲究,left 放在前面,这样每次都能取出相对最小值
        if right - left < ans[1] - ans[0]:
            ans = left, right
        if j + 1 == len(nums[i]):  # 同样的到了数组末尾,表明该行已经处理完了,则返回结果
            return ans
        v = nums[i][j + 1]  # 移动相对最小值的列
        right = max(right, v)  # 更新相对最大值
        heapq.heappush(pq, (v, i, j + 1))
    return list(ans)

由于上面使用的是最小堆,因此每次加入值都要进行logn的调整,我们可以使用字典的方式将其保存下来,再进行移动,其思路也是相同的,通过移动滑动窗口,它通过纪录行下标的方式,保证字典中每一个行下标都存在才进行更新,并且也是通过不断删除前面的下标缩小范围。

import collections
def smallestRange6(nums):
    """
    :type nums: List[List[int]]
    :rtype: List[int]
    """
    dmap = collections.defaultdict(set)  # 纪录行下标
    cover = collections.defaultdict(set)  # 纪录每个下标覆盖的数字
    nsize = len(nums)
    # 数字映射多个下标,因为可能存在多个相同的元素
    for idx, nlist in enumerate(nums):
        for num in nlist:
            dmap[num].add(idx)
    # 全部数字
    snum = sorted(set(n for nlist in nums for n in nlist))
    ssize = len(snum)
    start = end = 0
    ans = [snum[0], snum[-1]]
    gap = 0x7FFFFFFF
    while start < ssize and end < ssize:
        # end向右移动,把end覆盖的都加入到cover中
        while end < ssize and len(cover) < nsize:
            for idx in dmap[snum[end]]:
                cover[idx].add(snum[end])
            end += 1
        # 循环保证了start到end之间必然包含了每一行的至少一个元素
        # start向右移动,并且删除掉start覆盖的值,从而缩小范围
        while start < ssize and len(cover) == nsize:
            # cover每一行都存在至少一个数,满足条件可以进行更新
            # start不可能追上end,因为要保证每个下标都有值,因此它们会存在一个长度差
            if len(cover) == nsize and snum[end - 1] - snum[start] < gap:
                gap = snum[end - 1] - snum[start]
                ans = [snum[start], snum[end - 1]]
            for idx in dmap[snum[start]]:
                cover[idx].remove(snum[start])
                if not cover[idx]: del cover[idx]
            start += 1
    return ans

不同的做法

1. Remove Linked List Elements

这道题目并不困难,不过需要注意一些地方,比如删除的既是头结点也是尾结点,或者删除的元素是尾结点等等。

我的做法是,Beat 90%。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        node = head
        while node and node.val != val:
            node = node.next
        while node:
            if node.val == val:
                if node.next != None:
                    node.val = node.next.val
                    node.next = node.next.next
                elif head == node:  # 头结点
                    head = None
                    break
                else: # 尾结点
                    cur = head
                    while cur.next and cur.next != node:
                        cur = cur.next
                    cur.next = None
                    break
            else:
                node = node.next
        return head

其实我一开始的想法就是使用要删除结点的前一个结点,但是由于考虑到可能会复杂,结果没想到更简单,在head前面加上一个结点就可以了!

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        start = ListNode(0)
        start.next = head
        prev = start
        cur = head
        while cur is not None:
            if cur.val != val:
                prev = cur
                cur = cur.next
            else:
                prev.next = cur.next
                cur = prev.next
        return start.next

如果不想在前面加,也可以这么做:

class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        try:
            while head.val == val:
                head = head.next
            cur = head
            nex = cur.next
            while nex:
                if nex.val != val:
                    cur.next = nex
                    cur = cur.next
                nex = nex.next
            if cur.next.val == val:
                cur.next = None
        except:
            True
        return head

2. Reverse Linked List

呃,有点丢脸,自己写的代码太丑太长了,虽然都能AC。。

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head == None:
            return None
        elif head.next == None:
            return head
        elif head.next.next == None:
            temp = head.next
            head.next.next = head
            head.next = None
            return temp
        # 纪录当前结点和下一个结点和下下个结点
        node = head
        n_node = node.next
        nn_node = node.next.next
        node.next = None
        while node and n_node and nn_node:
            n_node.next = node
            node = n_node
            n_node = nn_node
            nn_node = n_node.next
        if n_node:
            n_node.next = node
            return n_node
        else:
            return node

只是纪录了两个结点(当前结点的前一个结点和当前结点的下一个结点),而我的纪录了3个结点

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """        
        pre = None
        while head:
            cur = head
            head = head.next
            cur.next = pre
            pre = cur
        return pre

3. Merge Two Sorted Lists

写的多了,其实没有必要把等号也弄进来,当成一种情况即可。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """ 
        head = ListNode(0)
        node = head
        while l1 and l2:
            if l1.val > l2.val:
                node.next = l2
                l2 = l2.next
            elif l1.val < l2.val:
                node.next = l1
                l1 = l1.next
            else:
                node.next = l1
                l1 = l1.next
                node = node.next
                node.next = l2
                l2 = l2.next
            node = node.next
        if l1:
            node.next = l1
        elif l2:
            node.next = l2
        return head.next

简化版:

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        dummy = current = ListNode(0)
        while l1 and l2:
            if l1.val < l2.val:
                current.next = l1
                l1 = l1.next
            else:
                current.next = l2
                l2 = l2.next
            current = current.next
        current.next = l1 if l1 else l2
        return dummy.next

同样可以使用递归

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """ 
        if not l1:
            return l2
        elif not l2:
            return l1
        head = None
        if l1.val > l2.val:
            head = l2
            head.next = self.mergeTwoLists(l1, l2.next)
        else:
            head = l1
            head.next = self.mergeTwoLists(l1.next, l2)
        return head
posted @ 2017-08-05 15:49  banananana  阅读(273)  评论(0)    收藏  举报