面试算法和数据结构中那些和 “K” 相关的题目

目录

一、链表

二、二叉树

三、前缀和

四、动态规划

五、堆排序

六、数组


本文会列举几道比较常见的题目中带有 “K” 的问题,有的问题在我的另一篇博客里做了详细的解释,所以这篇文章里我就不做太多解释

一、链表

1、返回倒数第 k 个节点

输入: 1->2->3->4->5 和 k = 2
输出: 4
class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        fast = head
        slow = head
        while k!=0:
            fast = fast.next
            k-=1
        while fast:
            fast = fast.next
            slow = slow.next
        return slow.val

先让快指针走k步,然后再同时走,快指针走到结尾时慢指针就走到第k个节点了

2、合并 K 个排序链表

扫描二维码关注公众号,回复: 12941390 查看本文章
输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:return None
        return self.helper(lists,0,len(lists)-1)

    def helper(self,lists,l,r):
        if l==r:
            return lists[l]
        # if l>r:
        #     return None
        mid = l + (r - l) // 2
        l1 = self.helper(lists,l,mid)
        l2 = self.helper(lists,mid+1,r)
        return self.mergeTwoList(l1, l2)

    def mergeTwoList(self,A,B):
            if not A or not B:
                return B if not A else A
            cur = ListNode(-1)
            tmp = cur
            while A and B:
                if A.val<=B.val:
                    tmp.next = A
                    A = A.next
                else:
                    tmp.next = B
                    B = B.next
                tmp = tmp.next
            tmp.next = A if A else B
            return cur.next

使用二分法来两两合并链表

3、K 个一组翻转链表

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        if not head or not head.next:
            return head
        tail = head
        for i in range(k):
            if not tail:
                return head
            tail = tail.next
        newHead = self.reverse(head,tail)
        head.next = self.reverseKGroup(tail,k)
        return newHead


    def reverse(self,start,end):
        pre = None
        while start!=end:
            node = start.next
            start.next = pre
            pre = start
            start = node
        return pre

使用递归的方法,k个反转一次,不够k个不反转,也可以使用栈的方法,将k个节点入栈,然后再弹出来加到结果列表里

class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        dummy = ListNode(0)
        p = dummy
        while True:
            count = k 
            stack = []
            tmp = head
            while count and tmp:
                stack.append(tmp)
                tmp = tmp.next
                count -= 1
            # 注意,目前tmp所在k+1位置
            # 说明剩下的链表不够k个,跳出循环
            if count : 
                p.next = head
                break
            # 翻转操作
            while stack:
                p.next = stack.pop()
                p = p.next
            #与剩下链表连接起来 
            p.next = tmp
            head = tmp
        
        return dummy.next

二、二叉树

二叉搜索树的第 k 大节点

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 4

递归版写法:

class Solution:
    count = 0
    res = 0
    def kthLargest(self, root: TreeNode, k: int) -> int:
        return self.helper(root,k)
    def helper(self,root,k):
        if not root:
            return 
        self.helper(root.right,k)
        self.count +=1
        if k==self.count:
            self.res = root.val
        self.helper(root.left,k)
        return self.res  

迭代版写法:

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        if not root:
            return None
        stack = []
        node = root
        while node or len(stack)>0:
            while node:
                stack.append(node)
                node = node.right          
            node = stack.pop()
            if k==1:
                self.res = node.val
            k-=1
            node = node.left
        return self.res

三、前缀和

和为 K 的子数组

输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

第一眼看到题,我还以为是和 组合总和 这道题一样

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

于是上来就套用回溯模板,当然最后答案是错的,看了解析发现是用前缀和。

什么是前缀和呢?其实就是数组当前项的总和

字典的为前缀和,即从第 0 项到当前项的总和,为这个 前缀和 值出现了几次
遍历 nums 之前,我们预置边界情况 (即之前提到的 prefixSum [-1] = 0):map 初始放入 0:1 键值对,即预设已经出现 1 次为 0 的前缀和

遍历 nums 的每一项,求当前项的前缀和,存入 map 中

之前没有存过,则存入,初始值为 1
之前存过,则对应值 +1,即出现次数 +1
边存边查看 map ,如果 map 中已存在 key 为 当前前缀和 - k

说明存在 【之前求出的前缀和】,它的值满足 【当前前缀和】-【之前求出的前缀和】 == k
把 【之前求出的前缀和】 出现的次数,累加给 count 计数器

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        if not nums:
            return 0
        dic = dict()
        dic[0] = 1
        acc = 0
        count = 0
        for num in nums:
            acc += num
            if (acc-k) in dic:
                count+=dic[acc-k]
            if acc not in dic:
                dic[acc] = 1
            else:
                dic[acc] += 1
        return count

四、动态规划

第 k 个数

这道题实际上是和 丑数 这道题类似的,这里使用的是动态规划的做法,当然也可以用堆的做法,这里就不介绍堆的用法

这是使用三指针的dp做法

class Solution:
    def getKthMagicNumber(self, k: int) -> int:
        a,b,c = 0,0,0
        dp = [1]*k
        for i in range(1,k):
            n1,n2,n3 = dp[a]*3,dp[b]*5,dp[c]*7
            dp[i] = min(n1,n2,n3)
            if dp[i]==n1:
                a+=1
            if dp[i]==n2:
                b+=1
            if dp[i]==n3:
                c+=1
        return dp[-1]

五、堆排序

堆排序这章的内容在我的博客 https://blog.csdn.net/Matrix_cc/article/details/106606612 里已有介绍,这里就不介绍了

六、数组

合并k个有序数组

将每个数组中的第一个元素加入到堆中,构建小顶堆,然后将堆顶元素弹出加到res数组里,接着将弹出元素对应的数组下一个元素添加到堆中,循环完成

L为数组的个数,N为数组里元素的个数

  • 时间复杂度:由于每个元素都需要读取一次,即最大次数为 L*N, 将每一个元素插入最小堆中的复杂度为 O (logL), 即总的复杂度为 O (L*NlogL)
  • 空间复杂度为:维护最小堆的大小,即 O (L)
import heapq
from collections import deque


def list_merge(*lists):
    # 入参判断, 这里直接pass
    # 将所有链表转化为deque,方便使用popleft获取链表的最左元素及根据索引返回该索引对应的剩余链表
    queues = [queue for queue in map(deque, lists)]
    heap = []
    # 初始化链表,该链表中的元素为元组, 各个链表的第一个元素及链表所在索引
    for i, lst in enumerate(queues):
        heap.append((lst.popleft(), i))
    # 将链表转换成最小堆
    heapq.heapify(heap)
    # 链表: 用于存放每次获取的堆顶层元素
    result = []

    while heap:
        # 将堆顶层元素出堆
        value, index = heapq.heappop(heap)
        # 将顶层元素追加
        result.append(value)
        # 根据索引获取对应链表的剩余元素
        if queues[index]:
            # 如果存在下一个元素,则将该元素及索引入堆
            heapq.heappush(heap, (queues[index].popleft(), index))
    return result


print(list_merge(*[[4, 8, 20], [100, 200, 350, 370], [5, 8, 350, 500, 1000]]))

参考:

leetcode题解中的一些解释

猜你喜欢

转载自blog.csdn.net/Matrix_cc/article/details/106636321