Python LeetCode(六)

优化

Increasing Subsequences

题意:找出数组里面生序的子序列(按顺序)

这道题我一开始想到的就是DFS,先选择数组的一个值,作为跟,然后往数组下面进行递归,思路不错,但是TLE。

class Solution(object):
    def findSubsequences(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def recursive(nums, arr, last):
            if len(arr) >= 2 and arr not in result:
                result.append(arr[:])
            for i in range(len(nums)):
                if last and nums[i] >= last:
                    arr.append(nums[i])
                    recursive(nums[i + 1:], arr, nums[i])
                    arr.pop()
        result = []
        for i in range(len(nums)):
            recursive(nums[i+1:], [nums[i]], nums[i])
        return result

接着我想是不是递归的过程中保存的上一个值而消耗了些时间,但是其实这个保存的操作并没有必要,因为我之前加入的条件是根据后者比前者大,所以我只需要比较最后一个加入数组的值即可,没有必要再保存一个值,于是,我又这么写,但是还是TLE。

class Solution(object):
    def findSubsequences(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def recursive(nums, arr):
            if len(arr) >= 2 and arr not in result:
                result.append(arr[:])
            for i in range(len(nums)):
                if len(arr) == 0 or nums[i] >= arr[-1]:
                    arr.append(nums[i])
                    recursive(nums[i + 1:], arr)
                    arr.pop()
        result = []
        recursive(nums, [])
        return result

于是我参照了网上的写法,原理是将之前加入的都取出来再加入新的值,一直到最后一个值,我把别人的代码进行了拆分。

import copy
class Solution(object):
    def findSubsequences(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        subs = {()}
        for num in nums:
            for sub in copy.copy(subs):
                if not sub or sub[-1] <= num:
                    newsub = sub + (num,)
                    subs.add(newsub)
        # 过滤掉那些包括空集和个数为1的集合
        return [list(x) for x in subs if len(x) >= 2]
class Solution(object):
    def findSubsequences(self, nums):
        subs = {()}
        for num in nums:
            subs |= {sub + (num,)
                     for sub in subs
                     if not sub or sub[-1] <= num}
        return [sub for sub in subs if len(sub) >= 2]

和上面同样的原理,但是它多进行了一个操作,就是将前面的任意一个比当前值小的和当前值构成了一个长度为2的tuple,从而提高了效率。

class Solution(object):
    def findSubsequences(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) < 2:
            return []
        if len(nums) == 2:
            return [nums] * int(bool(nums[0] <= nums[1]))
        a = {(nums[0], nums[1])} if nums[0] <= nums[1] else {()}
        for t in range(2, len(nums)):
            # 前者将a中的set都加上当前值,比如{1,2,3}遇见4变成{1,2,3,4}
            # 后者将循环前面的值加上当前值构成一个长度为2的set,比如{1,2,3}遇见4变成{1,4}和{2,4}和{3,4}
            # 最后在进行或操作
            a |= set([i + (nums[t],) for i in a if i[-1] <= nums[t]]) | \
                 set((j, nums[t]) for j in nums[:t] if j <= nums[t])
        return list(a)

什么?你要递归?我并不推荐,递归的时间复杂度显然比前面的做法要高,但是还是给出来,之前的递归的做法花费的时间可能在判断当前数组是否存在于最终的结果中这里,所以把这个判断条件放到迭代中,避免回溯的时候加入旧的值,可以避免每次递归都要判断一次。这种做法也是可以通过的,不过所消耗的时间比前面的高得多。

# 优化后的结果
class Solution(object):
    def recursive(self, nums, result, arr):
        if len(arr) >= 2:
            result.append(arr[:])
        unique = []
        for i in range(len(nums)):
            # 防止回溯的时候加入旧的值
            if nums[i] in unique:
                continue
            if len(arr) == 0 or nums[i] >= arr[-1]:
                arr.append(nums[i])
                unique.append(nums[i])
                self.recursive(nums[i + 1:], result, arr)
                arr.pop()
    def findSubsequences(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        self.recursive(nums, result, [])
        return result

2. Construct Binary Tree from Preorder and Inorder Traversal

  • 回溯

需要注意preorder下标的计算

class Solution(object):
    def rebuild(self, preorder, inorder):
        if len(preorder) == 0 or len(inorder) == 0:
            return None
        root = TreeNode(preorder[0])
        idx = inorder.index(preorder[0])
        root.left = self.rebuild(preorder[1:idx+1], inorder[:idx])
        root.right = self.rebuild(preorder[idx+1:], inorder[idx+1:])
        return root
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        return self.rebuild(preorder, inorder)

上面的递归还可以进行优化,不对数组进行切分,而是通过下标的形式进行切分(要仔细计算好下标),可以使用字典来存储值所对应的下标,改了以后可以打败95% ,而前者只能打败25%。

class Solution(object):
    def rebuild(self, preorder, inorder, inorder_index, pre_start, pre_end, in_start, in_end):
        if pre_end - pre_start < 0:
            return None
        root = TreeNode(preorder[pre_start])
        idx = inorder_index[root.val]
        root.left = self.rebuild(preorder, inorder, inorder_index,
                                 pre_start + 1, pre_start + idx - in_start,
                                 in_start, idx - 1)
        root.right = self.rebuild(preorder, inorder, inorder_index,
                                  pre_start + idx - in_start+1, pre_end,
                                  idx + 1, in_end)
        return root
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        inorder_index = {x: i for i, x in enumerate(inorder)}
        return self.rebuild(preorder, inorder, inorder_index,
                            0, len(preorder)-1, 0, len(inorder)-1)
  • DFS迭代

其原理是先一直将左子结点(前序)加入到栈中,直到前序中的值和中序中的值相等的时候,表示此时可以进行左结点的部分已经处理完了,就会就弹出到中间结点来设置接下来的右结点。

class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if not preorder:
            return None
        root=TreeNode(preorder[0])
        stack=[root]
        i,j = 1,0
        while i<len(preorder):
            temp=None
            cur=TreeNode(preorder[i])
            # 如果相等表示栈中的多个值开始和inorder相互对应起来了
            # 那么最后一个弹出的则是中间结点
            while stack and stack[-1].val==inorder[j]:
                temp=stack.pop()
                j+=1
            # 此时temp是中间结点
            if temp:
                temp.right=cur
            else:
                # 往深处递归
                stack[-1].left=cur
            stack.append(cur)
            i+=1
        return root

3. LRU Cache

下面的实现是使用下图中的方式,往头部加入新的数据,如果该数据存在则将其放到头部,如果加入时已满,则从底部淘汰掉数据。

class LRUCache(object):
    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.d = dict()
        self.arr = []
        self.capacity = capacity
    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key not in self.arr or len(self.arr) == 0:
            return -1
        self.arr.remove(key)
        self.arr.insert(0, key)
        return self.d.get(key, -1)
    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: void
        """
        self.d[key] = value
        if key not in self.arr:
            if len(self.arr) == self.capacity:
                last = self.arr.pop()
                del self.d[last]
        else:
            self.arr.remove(key)
        self.arr.insert(0, key)

这种方式虽然简单,在频繁访问热点数据的时候效率高,但是它的缺点在于如果是偶尔的批量访问不同的数据时其命中率就会很低。比如我频繁的访问A,接着访问不同的数据直到A被淘汰,此时我再访问A,则不得不又再次把A加入到Cache中,显然这种方式是不合时宜的,因为A已经访问了很多次了,不应该将其淘汰而把一堆只访问一次的数据加入到Cache中。

于是进行了优化,用一个队列来纪录其历史的访问纪录,并且每次访问都对该键进行数量的统计,用于在当缓存中的数量大于规定的数量时将其从缓存中删除,也就是说队列来控制哈希的清理顺序,数量来控制哪个键被删除!

import collections
class LRUCache(object):
    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.queue = collections.deque()  # 纪录顺序
        self.cap = capacity
        self.cache = {}  # 纪录访问过的纪录
        self.cnt = {}  # 纪录访问过的纪录的次数
    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key in self.cache:
            self.queue.append(key)
            self.cnt[key] += 1
            return self.cache[key]
        else:
            return -1
    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: void
        """
        self.cache[key] = value
        self.queue.append(key)
        self.cnt[key] = self.cnt.get(key, 0) + 1
        while len(self.cache) > self.cap:
            k = self.queue.popleft()
            self.cnt[k] -= 1
            if self.cnt[k] == 0:
                del self.cnt[k]
                del self.cache[k]

同样的,也可以用链表来做,它的原理就是移动链表上的结点,但是它的缺点在于维护链表需要一定的开销

class Node(object):
    def __init__(self, key=None, value=None, next=None, prev=None):
        self.key = key
        self.value = value
        self.next = next
        self.prev = prev
class LRUCache(object):
    def __init__(self, capacity):
        """
        :type capacity: int
        """
        self.capacity = capacity
        # single linked list with a head node
        # always put new node to the tail
        # also move the revisted node to the tail
        self.head = Node()
        self.tail = self.head
        self.head.next = self.tail
        # <key, node.prev>
        self.hash_table = {}
    def pop_front(self):
        del self.hash_table[self.head.next.key]
        p_next = self.head.next.next
        self.head.next = p_next
        # update the reference for new front node
        self.hash_table[self.head.next.key] = self.head
    def append(self, node):
        self.hash_table[node.key] = self.tail
        self.tail.next = node
        self.tail = node
    def move_to_end(self, prev):
        node = prev.next
        if node == self.tail:
            return
        # disconnect node
        prev.next = node.next
        node.next = None
        self.hash_table[prev.next.key] = prev
        # append node
        self.append(node)
    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if key not in self.hash_table:
            return -1
        prev = self.hash_table[key]
        val = prev.next.value
        self.move_to_end(prev)
        return val
    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: void
        """
        if key in self.hash_table:
            prev = self.hash_table[key]
            prev.next.value = value
            self.move_to_end(prev)
        else:
            self.append(Node(key, value))
            if len(self.hash_table) > self.capacity:
                self.pop_front()

4. Implement Queue using Stacks

我考虑到了如果频繁的对队列进行取值或者入列或者出列会造成一定的开销,因此我并没有在pop之后将另一个栈的值给移回来,beate 78% submissions !

class MyQueue(object):
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack = []
        self.stack_temp = []
    def push(self, x):
        """
        Push element x to the back of queue.
        :type x: int
        :rtype: void
        """
        if self.stack_temp:
            while self.stack_temp:
                self.stack.append(self.stack_temp.pop())
        self.stack.append(x)
    def pop(self):
        """
        Removes the element from in front of queue and returns that element.
        :rtype: int
        """
        while self.stack:
            self.stack_temp.append(self.stack.pop())
        if self.stack_temp:
            return self.stack_temp.pop()
        return None
    def peek(self):
        """
        Get the front element.
        :rtype: int
        """
        if self.stack_temp:
            return self.stack_temp[len(self.stack_temp)-1]
        elif self.stack:
            while self.stack:
                self.stack_temp.append(self.stack.pop())
            if self.stack_temp:
                return self.stack_temp[len(self.stack)-1]
        return None
    def empty(self):
        """
        Returns whether the queue is empty.
        :rtype: bool
        """
        return False if self.stack or self.stack_temp else True

但是我看LeetCode提供的最高效率的做法就是在pop之后就移回来,我想应该是没有更频繁的进行同一个操作,而是进行了多个不同的操作,那么我的做法就有劣势。

class MyQueue(object):
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack = []
    def push(self, x):
        """
        Push element x to the back of queue.
        :type x: int
        :rtype: void
        """
        self.stack.append(x)
    def pop(self):
        """
        Removes the element from in front of queue and returns that element.
        :rtype: int
        """
        if not self.empty():
            self._stack = []
            while len(self.stack) > 1:
                self._stack.append(self.stack.pop())
            val = self.stack.pop()
            while self._stack:
                self.stack.append(self._stack.pop())
            return val
    def peek(self):
        """
        Get the front element.
        :rtype: int
        """
        if not self.empty():
            return self.stack[0]
    def empty(self):
        """
        Returns whether the queue is empty.
        :rtype: bool
        """
        return not self.stack
posted @ 2017-08-05 15:48  banananana  阅读(288)  评论(0)    收藏  举报