腾讯精选练习50(第二个)


前言

之前的好像因为写太多了所以编辑起来很卡…所以就新写一个


第12天

146.LRU缓存机制

网址:https://leetcode-cn.com/problems/lru-cache/

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
    ~~~    LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
    ~~~    int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
    ~~~    void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间

第一个想法就是用链表来储存,但是遇到的困难就是如何得知链表中存在数据,最后看的官方题解,是用双向链表和hash表来写的。hash表中value值来储存地址。

class DLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None


class LRUCache:

    def __init__(self, capacity: int):
        self.cache = dict()
        # 使用伪头部和伪尾部节点    
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        # 如果 key 存在,先通过哈希表定位,再移到头部
        node = self.cache[key]
        self.moveToHead(node)
        return node.value

    def put(self, key: int, value: int) -> None:
        if key not in self.cache:
            # 如果 key 不存在,创建一个新的节点
            node = DLinkedNode(key, value)
            # 添加进哈希表
            self.cache[key] = node
            # 添加至双向链表的头部
            self.addToHead(node)
            self.size += 1
            if self.size > self.capacity:
                # 如果超出容量,删除双向链表的尾部节点
                removed = self.removeTail()
                # 删除哈希表中对应的项
                self.cache.pop(removed.key)
                self.size -= 1
        else:
            # 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
    
    def addToHead(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    def removeNode(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def moveToHead(self, node):
        self.removeNode(node)
        self.addToHead(node)

    def removeTail(self):
        node = self.tail.prev
        self.removeNode(node)
        return node

执行用时:200 ms, 在所有 Python3 提交中击败了46.41%的用户
内存消耗:23.7 MB, 在所有 Python3 提交中击败了5.05%的用户

148.排序链表

网址:https://leetcode-cn.com/problems/sort-list/

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

想了半天没想出来…最后看的官方题解
使用归并算法,先设置长度为1,对各个链表进行排序,之后设置长度为2。。。直到设置长度大于总长度
用到了之前做过的有序链表合并的函数merge

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        def merge(head1: ListNode, head2: ListNode) -> ListNode: #合并两个有序链表
            dummyHead = ListNode(0)
            temp, temp1, temp2 = dummyHead, head1, head2
            while temp1 and temp2:
                if temp1.val <= temp2.val:
                    temp.next = temp1
                    temp1 = temp1.next
                else:
                    temp.next = temp2
                    temp2 = temp2.next
                temp = temp.next
            if temp1:
                temp.next = temp1
            elif temp2:
                temp.next = temp2
            return dummyHead.next
        
        if not head:
            return head
        #计算链表长度
        length = 0
        node = head
        while node:
            length += 1
            node = node.next
        
        dummyHead = ListNode(0, head)
        subLength = 1 #先设置长度为1进行链表排序
        while subLength < length:
            prev, curr = dummyHead, dummyHead.next
            while curr:
                head1 = curr #指向第一部分的链表头
                for i in range(1, subLength):
                    if curr.next:
                        curr = curr.next
                    else:
                        break
                head2 = curr.next #指向第二部分的链表头
                curr.next = None #切断第一部分
                curr = head2
                for i in range(1, subLength):
                    if curr and curr.next:
                        curr = curr.next
                    else:
                        break
                
                succ = None #用来储存第二部分后面的链表
                if curr:
                    succ = curr.next
                    curr.next = None
                
                merged = merge(head1, head2) #进行排序
                prev.next = merged #接头
                while prev.next:
                    prev = prev.next
                curr = succ #下一个循环对剩余部分的链表进行
            subLength <<= 1 #位操作,乘以二
        
        return dummyHead.next

执行用时:600 ms, 在所有 Python3 提交中击败了6.00%的用户
内存消耗:30 MB, 在所有 Python3 提交中击败了13.51%的用户

155.最小栈

网址:https://leetcode-cn.com/problems/min-stack/

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
    ~~~    push(x) —— 将元素 x 推入栈中。
    ~~~    pop() —— 删除栈顶的元素。
    ~~~    top() —— 获取栈顶元素。
    ~~~    getMin() —— 检索栈中的最小元素。

做法就是利用一个辅助栈,在进栈的同时储存对应的最小值
我看到评论区里面还有一种做法就是在stack中储存与最小值的差值,然后用min来储存最小值,这样子就可以省下来储存的空间了。等以后再去尝试

class MinStack:
    
    def __init__(self):
        self.stack=[]
        self.min_stack=[float('inf')]

    def push(self, x: int) -> None:
        self.stack.append(x)
        self.min_stack.append(min(x,self.min_stack[-1]))

    def pop(self) -> None:
        self.min_stack.pop()
        return self.stack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.min_stack[-1]

执行用时:68 ms, 在所有 Python3 提交中击败了75.34%的用户
内存消耗:18 MB, 在所有 Python3 提交中击败了12.27%的用户

第十三天

160.相交链表

网址:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

编写一个程序,找到两个单链表相交的起始节点。
注意:

如果两个链表没有交点,返回 null. 在返回结果后,两个链表仍须保持原有的结构。 可假定整个链表结构中没有循环。 程序尽量满足 O(n)
时间复杂度,且仅用 O(1) 内存。

第一个想法是先遍历两个数组计算出各自的长度,然后将两个链表的尾部对齐,用两个假头一起往下遍历

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        if not headA or not headB: return None
        def getlen(head):
            n=0
            while head:
                head=head.next
                n+=1
            return n
        a=getlen(headA)
        b=getlen(headB)
        curA=curB=None
        #总使curA为较长的一个
        if a<b: #headB长
            curB=headA
            curA=headB
        else:
            curA=headA
            curB=headB
        #让两个链表尾部对齐
        for i in range(abs(a-b)):
            curA=curA.next

        for i in range(b):
            if curA==curB:
                return curA
            curA=curA.next
            curB=curB.next
        return None

执行用时:168 ms, 在所有 Python3 提交中击败了61.16%的用户
内存消耗:29.5 MB, 在所有 Python3 提交中击败了34.46%的用户

不过还有可以改进的地方,就是可以看成两个新链表,一个是A+B,一个是B+A,即假头A在链表A尾部结束后到B链表的头部
在这里两个链表相交的地方加入一个none,这样子可以减少情况的判断,同时照样符合上述的描述

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        if not headA or not headB: return None
        a=headA
        b=headB
        while a!=b:
            if not a:
                a=headB
            else:
                a=a.next
            if not b:
                b=headA
            else:
                b=b.next
        return a

执行用时:156 ms, 在所有 Python3 提交中击败了90.47%的用户
内存消耗:29.6 MB, 在所有 Python3 提交中击败了12.95%的用户

169.多数元素

网址:https://leetcode-cn.com/problems/majority-element/

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

摩尔投票法,因为我们要找到的多数元素是众数,因此在投票时总是占据优势的
时间复杂度为 O(n)、空间复杂度为 O(1)

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        a,b=0,0
        for i in range(len(nums)):
            if b==0:
                a=nums[i]
            b=b+1 if a==nums[i] else b-1
        return a

执行用时:56 ms, 在所有 Python3 提交中击败了40.81%的用户
内存消耗:15.9 MB, 在所有 Python3 提交中击败了32.80%的用户

还有一种办法,就是先对数列进行排序,这样子时间复杂度会稍微高一点
因为是众数,且数组经过排序,那么中间数就是我们所需要查找的

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

执行用时:44 ms, 在所有 Python3 提交中击败了85.36%的用户
内存消耗:16.2 MB, 在所有 Python3 提交中击败了5.26%的用户

206.反转链表

网址:https://leetcode-cn.com/problems/reverse-linked-list/

反转一个单链表。

递归法

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

执行用时:56 ms, 在所有 Python3 提交中击败了7.91%的用户
内存消耗:20.9 MB, 在所有 Python3 提交中击败了5.04%的用户

递归法二:设定函数返回一个已经反转了的链表,
1->2->3->4->5->NULL
变成
1->2<-3<-4<-5
然后将head指向None即可

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

执行用时:36 ms, 在所有 Python3 提交中击败了93.36%的用户
内存消耗:19.6 MB, 在所有 Python3 提交中击败了5.04%的用户

迭代法,用三个指针来确保可以向下进行

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        a=head
        if not a:return None
        b=a.next
        if not b:
            return a
        c=b.next
        a.next=None
        while True:
            if not c:
                b.next=a
                return b
            b.next=a
            a,b,c=b,c,c.next

执行用时:36 ms, 在所有 Python3 提交中击败了93.36%的用户
内存消耗:15.6 MB, 在所有 Python3 提交中击败了27.44%的用户

第14天

215.数组中的第K个最大元素

网址:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

这道题目考察排序算法的运用,我就没有仔细写了
那种用自带的就是sort之后再return第-k个元素即可

217.存在重复元素

网址:https://leetcode-cn.com/problems/contains-duplicate/

给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

方法一就是将其作为一个集合,去除重复元素,然后计算长度

class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        a=set(nums)
        return len(nums)!=len(a)

执行用时:56 ms, 在所有 Python3 提交中击败了15.66%的用户
内存消耗:20.1 MB, 在所有 Python3 提交中击败了31.32%的用户

或者还可以用哈希表或排序来求解

230.二叉搜索树中第K小的元素

网址:https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。

考虑到二叉搜索树的特殊性,可以用中序遍历的方法来得出从小到大的排序,然后找到第K小的元素。
以下是递归方法,迭代方法也是同样的道理

class Solution:
    def kthSmallest(self, root: TreeNode, k: int) -> int:
        def inorder(r):
            return inorder(r.left) + [r.val] + inorder(r.right) if r else []
    
        return inorder(root)[k - 1]

第15天

231.2的幂

网址:https://leetcode-cn.com/problems/power-of-two/

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

对于一个数字,判断是否为2的幂,先查看是否为1,再看看是不是2的倍数,用递归进行进一步的判断

class Solution:
    def isPowerOfTwo(self, n: int) -> bool:
        if n==0: return False
        if n==1: return True
        if n//2!=n/2: return False
        return self.isPowerOfTwo(n/2)

执行用时:36 ms, 在所有 Python3 提交中击败了88.33%的用户
内存消耗:14.8 MB,在所有 Python3 提交中击败了15.92%的用户

这种是看题解的,如果是2的幂,那么只有最高位才是1,当n-1时除了最高位全是1,因此与操作得出结果是0

class Solution:
    def isPowerOfTwo(self, n: int) -> bool:
        return n>0 and n&(n-1)==0

执行用时:32 ms, 在所有 Python3 提交中击败了96.50%的用户
内存消耗:14.8 MB, 在所有 Python3 提交中击败了16.63%的用户

235.二叉搜索树的最近公共祖先

网址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

要明白一点就是,如果两个结点在当前节点的两边,那么当前结点就是最近的公共祖先节点。
同时对于当前结点要优先判断是否为其中一个节点,如果是的话那么它就是最终结果了
以下是递归

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root ==p or root==q: return root
        if (root.val-p.val)*(root.val-q.val)<0:return root #两个结点分隔在两侧
        elif root.val-p.val>0: return self.lowestCommonAncestor(root.left,p,q) #两个结点都在左边
        else: return self.lowestCommonAncestor(root.right,p,q)

执行用时:80 ms, 在所有 Python3 提交中击败了90.28%的用户
内存消耗:18.8 MB, 在所有 Python3 提交中击败了15.33%的用户

以下是迭代法

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        while True:
            if root ==p or root==q: return root
            if (root.val-p.val)*(root.val-q.val)<0:return root #两个结点分隔在两侧
            elif root.val-p.val>0: root=root.left #两个结点都在左边
            else: root=root.right

执行用时:84 ms, 在所有 Python3 提交中击败了78.64%的用户
内存消耗:18.8 MB, 在所有 Python3 提交中击败了8.02%的用户

236.二叉树的最近公共祖先

网址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

相较于之前的二叉搜索树,这里不能剪枝操作。
所以这里就是将所有的结点进行深度优先搜索,对于q和p,将其带上来,也就是相当于把p和q的上面结点进行记录,直到遇到两个的共同祖先

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left: return right
        if not right: return left
        return root #这种就是left and right的情况了

执行用时:76 ms, 在所有 Python3 提交中击败了79.79%的用户
内存消耗:25.6 MB, 在所有 Python3 提交中击败了34.97%的用户

第16天

237.删除链表中的节点

网址:https://leetcode-cn.com/problems/delete-node-in-a-linked-list/

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
提示:
链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。

这个题目刚开始都没看懂,后来才明白,这是把下一个节点的值拿过来然后舍弃下一个节点,这样子就变相等于删除了当前的节点。

class Solution:
    def deleteNode(self, node):
        node.val=node.next.val
        node.next=node.next.next

238.除自身以外数组的乘积

网址:https://leetcode-cn.com/problems/product-of-array-except-self/

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

如果采取暴力算法,那么就会产生很多重复的步骤,因此对于暴力算法的优化就能得到题目要求的结果
对于最终数组的每一个值,都可分为两部分:左边相乘和右边相乘
因此,可以对数组进行遍历两次,一次得出各个值左边的乘,一次从右边开始遍历得出右边的乘
两次相乘合并就是最终的output

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        output=[]
        left = 1
        for i in range(len(nums)):
            if i > 0 :
                left *= nums[i-1]
            output.append(left)
        right = 1
        for i in range(len(nums)-1,-1,-1):
            if i < len(nums)-1:
                right *= nums[i+1]
            output[i]*=right
        return output

292.Nim游戏

网址:https://leetcode-cn.com/problems/nim-game/

你和你的朋友,两个人一起玩 Nim 游戏:
桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手。
每一回合,轮到的人拿掉 1 - 3 块石头。
拿掉最后一块石头的人就是获胜者。
假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

  • 当n为1-3时,无论如何都能拿完
  • 当n为4时,无论如何都会被对方拿完

以此类推,如果要输的话,那么对方应该留下4个给我,因为没人只能拿1-3个棋子,所以当n为4的倍数时,对方总有办法能够使一个来回内取走的棋子为4,从而必输

当n为1-3时,无论如何都会赢

class Solution:
    def canWinNim(self, n: int) -> bool:
        return n%4!=0

第17天

344.反转字符串

网址:https://leetcode-cn.com/problems/reverse-string/

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

第一个无脑操作,直接用库函数

class Solution:
    def reverseString(self, s: List[str]) -> None:

        s.reverse()

第二种方法是双指针,然后交换两个的值

class Solution:
    def reverseString(self, s: List[str]) -> None:
        l,r=0,len(s)-1
        while l<r:
            s[l],s[r]=s[r],s[l]
            l+=1
            r-=1

557.反转字符串中的单词|||

网址:https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

将每个单词取出然后反转再合并

class Solution:
    def reverseWords(self, s: str) -> str:
        return " ".join([i[::-1] for i in s.split(' ')])

执行用时:40 ms, 在所有 Python3 提交中击败了81.66%的用户
内存消耗:15.4 MB, 在所有 Python3 提交中击败了32.89%的用户

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值