美文网首页Algorithms
剑指offer之(链表和栈)

剑指offer之(链表和栈)

作者: 桃之夭夭的简书 | 来源:发表于2020-05-09 16:33 被阅读0次

题目列表
链表
面试题06. 从尾到头打印链表
面试题18. 删除链表的节点
面试题22. 链表中倒数第k个节点
面试题24. 反转链表(标记)
面试题25. 合并两个排序的链表
面试题35. 复杂链表的复制(medium)
面试题36. 二叉搜索树与双向链表(medium)
面试题52. 两个链表的第一个公共节点

面试题09. 用两个栈实现队列
面试题30. 包含min函数的栈
面试题59 - I. 滑动窗口的最大值

链表:

面试题06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
递归法:

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        return self.reversePrint(head.next) +[head.val] if head else []

栈:

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        stack = []
        while head:
            stack.append(head.val)
            head = head.next
        return stack[::-1]

非常简单

面试题18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:
题目保证链表中节点的值互不相同
若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if head.val == val: return head.next
        
        pre, cur = head, head.next
        while cur and cur.val != val:
            pre, cur = cur, cur.next
        
        if cur:
            pre.next = cur.next
      
        return head

面试题22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        
        p = q = head
        count = 0
        while q:
            if count >= k:
                p = p.next
            count += 1
            q = q.next
        return p if count >= k else None

写法2,同样思路不同写法

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        
        slow = head
        fast = head
        
        for _ in range(k):
            if not fast: return None
            fast = fast.next
        
        while fast:
            fast = fast.next
            slow = slow.next
        return slow

面试题24. 反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:0 <= 节点个数 <= 5000

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        curr = head
        
        while curr:
            tmp = curr.next
            curr.next = pre
            pre = curr
            curr = tmp
        return pre

emmm,每次都头大,来回来去的搞混

面试题25. 合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
限制:0 <= 链表长度 <= 1000

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(0)
        node = dummy
        
        while l1 and l2:
            if l1.val > l2.val:
                node.next = l2
                l2 = l2.next
            else:
                node.next = l1
                l1 = l1.next
            node = node.next
        node.next = l1 if l1 else l2
        return dummy.next

基本操作。

面试题35. 复杂链表的复制

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
解法1 深度优先

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        def dfs(head):
            if not head: return None
            
            if head in visited:
                return visited[head]
            copy = Node(head.val, None, None)
            visited[head] = copy
            
            copy.next = dfs(head.next)
            copy.random = dfs(head.random)
            return copy
 
        visited = {}
        return dfs(head)

还有迭代和广度优先的方法,觉得复杂,不做了;

面试题36. 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。
有点难?
[图片上传中...(image.png-f13ed5-1589009921053-0)]

要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:



我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。



特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

这狗题目好难


面试题52. 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表:
在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p1 = headA
        p2 = headB
        while p1 != p2:
            p1 = headB if p1 is None else p1.next
            p2 = headA if p2 is None else p2.next
        
        return p1

======================================================

面试题09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

class CQueue:

    def __init__(self):
        self.A, self.B = [], []

    def appendTail(self, value: int) -> None:
        self.A.append(value)

    def deleteHead(self) -> int:
        if self.B:
            return self.B.pop()    
        if not self.A:
            return -1  
        while self.A:
            self.B.append(self.A.pop())
        return self.B.pop()

deleteHead解析:
删除队首deleteHead()函数: 有以下三种情况。
当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
否则,当 A 为空: 即两个栈都为空,无元素,因此返回 -1−1 。
否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。

面试题30. 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
解题思路:
push(x) 函数: 重点为保持栈 B 的元素是 非严格降序 的。
将 x 压入栈 A (即 A.add(x) );
若 ① 栈 B 为空 或 ② x 小于等于 栈 B 的栈顶元素,则将 xx 压入栈 BB (即 B.add(x) )。
pop() 函数: 重点为保持栈 A,B 的 元素一致性 。
执行栈 A 出栈(即 A.pop() ),将出栈元素记为 y ;
若 y 等于栈 B 的栈顶元素,则执行栈 B 出栈(即 B.pop() )。
top() 函数: 直接返回栈 A 的栈顶元素即可
min() 函数: 直接返回栈 B 的栈顶元素即可

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.s1 = []
        self.s2 = []

    def push(self, x: int) -> None:
        self.s1.append(x)
        if not self.s2 or self.s2[-1] >= x:
            self.s2.append(x)

    def pop(self) -> None:
        if self.s1.pop() == self.s2[-1]:     ##这注意下
            self.s2.pop()
        

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

    def min(self) -> int:
        return self.s2[-1]

面试题59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
暴力的遍历法解题

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums or k== 0: return []
        res = []
        for i in range(0, len(nums)-k+1):
            res.append(max(nums[i:i+k]))
        
        return res

暴力法的算法的时间复杂度是O(n),当要求优化算法,算法时间复杂读是O(1)时,使用一个队列来记录最大值

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        if not nums or k== 0: return []
        deque = collections.deque()
        res = []
        for i in range(k):
            while deque and deque[-1] < nums[i]: deque.pop()
            deque.append(nums[i])
        res.append(deque[0])

        for i in range(k, len(nums)):
            if deque[-1] == nums[i-k]: deque.popleft()
                
            while deque and deque[-1] < nums[i]: deque.pop()
            deque.append(nums[i])    
            res.append(deque[0])
        
        return res

面试题59 - II. 队列的最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]

均摊时间复杂度为O(1)
import queue
class MaxQueue:

    def __init__(self):
        self.deque = queue.deque()
        self.queue = queue.Queue()

    def max_value(self) -> int:
        return self.deque[0] if self.deque else -1


    def push_back(self, value: int) -> None:
        while self.deque and self.deque[-1] < value:
            self.deque.pop()
        self.deque.append(value)
        self.queue.put(value)

    def pop_front(self) -> int:
        if not self.deque:
            return -1
        ans = self.queue.get()
        if ans == self.deque[0]:
            self.deque.popleft()
        return ans

额外的题目

相关文章

  • 剑指offer之(链表和栈)

    题目列表链表面试题06. 从尾到头打印链表面试题18. 删除链表的节点面试题22. 链表中倒数第k个节点面试题24...

  • 剑指offer4J【C2 P6】倒序打印链表

    题目 倒序打印链表 题解 递归 非递归方式 我们使用栈即可 源码: 剑指offer4J[https://githu...

  • 剑指 Offer 35. 复杂链表的复制

    剑指 Offer 35. 复杂链表的复制

  • 两个链表的第一个公共节点

    《剑指offer》刷题笔记。如有更好解法,欢迎留言。 关键字:栈 链表 题目描述: 输入两个链表,找出它们的第一个...

  • 链表中倒数第k个结点

    《剑指offer》刷题笔记。如有更好解法,欢迎留言。 关键字:栈 题目描述: 输入一个链表,输出该链表中倒数第k个...

  • (6)链表相关题目

    (1)从尾到头打印链表 算法思路:(1)递归(2)借助栈(剑指offer6题) (2)O(1)时间内删...

  • 贪心、分治、动态规划算法

    书籍:大话数据结构、剑指offer(必看 必看 必看)、编程之美、编程珠玑 10个数据结构:数组、链表、栈、队列、...

  • [剑指offer] 链表

    链表中环的入口结点 题目描述给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。 解题思路:...

  • 《剑指Offer》链表

    1基本概念 链表是一种动态数据结构,内存分配不是在创建链表时一次性完成的,而是每添加一个节点分配一次内存。由于没有...

  • 剑指Offer之反转链表

    题目描述输入一个链表,反转链表后,输出链表的所有元素。 思路一:从输入链表中循环取出节点作为新链表的头节点。 思路...

网友评论

    本文标题:剑指offer之(链表和栈)

    本文链接:https://www.haomeiwen.com/subject/zwvhnhtx.html