剑指Offer-1(队列,栈,链表)
基本概念
栈:先进后出
- 栈顶,栈底,进栈,出栈
- 实现方式:顺序栈(数组),链栈(链表)
两种实现方式的区别,仅限于数据元素在实际物理空间上存放的相对位置,顺序栈底层采用的是数组,链栈底层采用的是链表
- 应用:浏览器的回退(
page.history
),IDE的括号匹配 - 相关题目:虽然栈的运作机制很简单实现功能也较少,但由于不同于其他结构的先进后出的结构,通过主栈加辅栈的组合也能实现不错的功能
- 剑指09. 用两个栈实现一个队列
- 剑指30. 包含min函数的栈
队列:先进先出
- 实现方式:
- 列表:
list
和list.pop(0)
- python库:
collections.dqueue
和dqueue.popleft()
时间复杂度的不同:list 是列表,数组移除头部元素的方式是把后面的元素全部往前移动一位,所以复杂度是 O(N) ; deque 是双端队列,底层是链表,因此头部和尾部是等价的,插入删除都是 O(1)
- 列表:
- 应用:遍历树或图时
- 相关题目:
链表
- 相关题目:
- 利用链表有序的特点进行遍历:
- 剑指16. 从尾到头打印链表
- 剑指18. 删除链表的节点
- 但是也因为有序的特点,如果我们想翻转链表的顺序时就变得麻烦;我们可以使用最常见的遍历改变节点间的指向,也可以使用递归进行反向操作,或者利用特殊的双指针模式避免逆序操作。
- 剑指24. 翻转链表(中转+递归)
- 剑指22. 链表中倒数第k个字节(快慢指针)
- 利用链表有序的特点进行遍历:
剑指09. 用两个栈实现一个队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
解题思路:
两个栈,一个主要,一个辅助。添加时可以直接用append实现末尾添加。删除时需要把主栈元素倒回辅助,删掉第一个,再倒回来。
复杂度
- 删除:空间O(n), 时间O(n)
- 插入:空间O(1), 时间O(1)
## 注意题目要求:两个栈 |
剑指30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)
解题思路:
- 栈的特性:top没有出来之前,下面的是一定存在的。换言之,当top在的时候,对应的当前栈的最小值是不会变的。
- 创建动态储存最小值的辅助栈。push的时候对比上一个最小值和当前input,将小的push进辅助栈。pop的时候一并pop辅助
- 为避免第一个push时out of index,初始化辅助栈时先加入一个无穷大(
math.inf
)
Tip
- python数组索引可以通过负值来倒着取出
- 栈顶是指最后一个进来的
list.pop()
默认pop最后一位
class MinStack(object): |
剑指16. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
解题思路:
用list顺序存储单向链表,使用切片直接倒序输出。
Tip:
- 列表切片规则
List[start:stop:step]
,不加则是默认全部区间 List[::-1]
则代表倒置列表
复杂度
- 时间O(n), 空间O(n)
# Definition for singly-linked list. |
剑指24. 翻转链表(中转+递归)
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
解题思路:
需要解决的问题其实有两个:1. 如何改变node之间的指向 2. 如何保存头部节点
- 使用中转点对上一个node保存,遍历链表同时改变指向,最后一个到达的就是新链表的头部节点
- 递归。只有到达最后一个节点后才开始进行翻转。这里通过返回值不停传递末节点,即新链表的头结点。
- 传递参数:当前节点和下一节点
- 停止条件:当前节点为空
- 返回值:上一节点
- 执行动作:将下一节点指向当前节点
复杂度
- 时间:O(n),空间:O(2)
中转站法
# Definition for singly-linked list. |
- 时间:O(n),
- 空间:O(n):递归深度达到n,需要n的空间
递归
class Solution: |
剑指18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
时间O(n), 空间O(1)
# Definition for singly-linked list. |
剑指22. 链表中倒数第k个字节(快慢指针)
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
遍历法很容易想到就不在这里赘述了
- 时间O(n) 空间O(1)
# Definition for singly-linked list. |
剑指25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
- 善用伪头结点可以减少很多步骤,如判断哪个才是头结点造成的代码行数增加
- 因为是链表结构而非数组,因此不用考虑最后剩下的需要用while再一个个塞进去
- 时间O(n), 空间O(1)
# Definition for singly-linked list. |