1. 剑指offer 栈和队列
剑指 Offer 09. 用两个栈实现队列【简单】
java
class CQueue {
LinkedList<Integer> A, B;
public CQueue() {
A = new LinkedList<Integer>();
B = new LinkedList<Integer>();
}
public void appendTail(int value) {
A.addLast(value);
}
public int deleteHead() {
//当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
if(!B.isEmpty()){
return B.removeLast();
}
//当 A 为空: 即两个栈都为空,无元素,因此返回 −1-1−1 。
if(A.isEmpty()){
return -1;
}
//将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
while(!A.isEmpty()){
B.addLast(A.removeLast);
}
return B.removeLast();
}
}
python中用[][]表示
时间复杂度: appendTail()函数为 O(1) ;deleteHead() 函数在 N 次队首元素删除操作中总共需完成 N个元素的倒序。
空间复杂度 O(N) : 最差情况下,栈 A 和 B 共保存 N个元素。
剑指 Offer 30. 包含min函数的栈 【简单】
时间复杂度 O(1) : push(), pop(), top(), min() 四个函数的时间复杂度均为常数级别。
空间复杂度 O(N) : 当共有 N个待入栈元素时,辅助栈 B 最差情况下存储 N 个元素,使用 O(N) 额外空间。
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.A, self.B =[],[]
# 为保持栈B的元素是 非严格降序 的。
def push(self, x: int) -> None:
# 将x压入栈A
self.A.append(x)
# 若B为空 或者 x小于B的栈顶元素,x压入栈中
if not self.B or self.B[-1] >= x:
self.B.append(x)
#保持A,B的元素一致性
def pop(self) -> None:
# 执行A出栈,若等于B的栈顶元素
if self.A.pop() == self.B[-1]:
# B出栈
self.B.pop()
#直接返回栈 A的栈顶元素即可,
def top(self) -> int:
return self.A[-1]
# 直接返回栈 B 的栈顶元素即可
def min(self) -> int:
return self.B[-1]
2. 剑指offer 搜索与回溯算法
剑指 Offer 32 - I. 从上到下打印二叉树 【中等】
杂度 O(N) : NNN 为二叉树的节点数量,即 BFS 需循环 N 次。
空间复杂度 O(N) : 最差情况下,即当树为平衡二叉树时,最多有 N/2 个树节点同时在 queue 中,使用 O(N) 大小的额外空间
注意java的数组和链表。
1. 数组(Array):
- 数组是一种连续的数据结构,可以在内存中分配一块连续的空间来存储元素。
- 数组的大小在创建时需要提前确定,并且不可改变(静态数组)。
- 数组的元素可以通过索引进行随机访问,即通过给定的索引可以直接访问对应位置的元素。
- 数组适用于对元素的随机访问和修改,具有O(1)的时间复杂度。
- 但是,数组的插入和删除操作需要移动其他元素,时间复杂度为O(n)。
2. 链表(Linked List):
- 链表是一种离散的数据结构,元素分散存储在内存中,通过指针将这些元素链接在一起。
- 链表的大小可以动态增长或缩小,不需要提前确定,可以根据需要进行修改。
- 链表的每个元素(节点)都包含了一个存储数据的域(value)和一个指向下一个节点的指针(next)。
- 链表只能通过遍历从头开始访问每个元素,无法通过索引进行随机访问。
- 链表适用于频繁的插入和删除操作,具有O(1)的时间复杂度。
- 但是,链表的空间开销相对较大,需要额外的指针存储节点之间的链接关系。
综上所述,数组和链表各有优劣,适用于不同的场景。数组适用于需要频繁随机访问元素的场景,而链表适用于频繁插入和删除元素的场景。在选择使用数组还是链表时,需要根据具体的需求和操作进行权衡和选择。
class Solution {
public int[] levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
//因为java数组不可以动态扩容
ArrayList<Integer> res = new ArrayList<>();
if(root != null) q.add(root);
while(!q.isEmpty()){
for(int i = q.size(); i > 0;i--){
TreeNode node = q.poll();
res.add(node.val);
if(node.left != null) q.add(node.left);
if(node.right != null) q.add(node.right);
}
}
int[] ans = new int[res.size()];
for(int i =0;i< res.size();i++){
ans[i] = res.get(i);
}
return ans;
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II 【简单】
层次遍历BFS常见模板。
初始化一个队列用来存放结点,一个链表存放结果
BFS循环:当q为空时,跳出
新建一个临时列表,用来存储当前层的结果(循环次数为当前层的节点数)
出队-打印-添加子节点
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null){
q.add(root);
}
while(!q.isEmpty()){
List<Integer> tmp = new ArrayList<>();
for (int i = q.size();i > 0;i--){
TreeNode node = q.poll();
tmp.add(node.val);
if(node.left != null) q.add(node.left);
if(node.right != null) q.add(node.right);
}
res.add(tmp);
}
return res;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III 【中等】
双端队列
在Java中,双端队列的实现主要通过`Deque`接口及其实现类来完成。`Deque`接口继承自`Queue`接口,并提供了在队列两端进行插入和删除操作的方法。以下是Java中常用的双端队列实现类:
1. `ArrayDeque`:使用数组实现的双端队列,可以在队列的头部和尾部进行高效的插入和删除操作。由于使用数组实现,在添加元素时会动态调整内部数组的大小。
2. `LinkedList`:使用双向链表实现的双端队列。由于是双向链表,因此在头部和尾部进行插入和删除操作的效率都很高。除了作为双端队列,`LinkedList`还可以作为普通的链表来使用。
这些双端队列实现类都可以通过向队列头部或尾部添加和删除元素来实现双端队列的功能。根据具体的需求,可以选择合适的实现类来使用双端队列。
时间复杂度 O(N) : N 为二叉树的节点数量,即 BFS 需循环 N 次,占用 O(N);双端队列的队首和队尾的添加和删除操作的时间复杂度均为 O(1) 。
空间复杂度 O(N) : 最差情况下,即当树为满二叉树时,最多有 N/2个树节点 同时 在 deque 中,使用 O(N)大小的额外空间。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) q.add(root);
//注意双端队列!
while(!q.isEmpty()){
LinkedList<Integer> tmp = new LinkedList<>(); //链表可以模拟双端队列
for(int i = q.size() ;i > 0;i--){
TreeNode node = q.poll();
if(res.size() % 2 == 0 ){
tmp.addLast(node.val); //偶数层 -》队列头部 右边
}else{
tmp.addFirst(node.val); //奇数层 -》 队列尾部 左边
}
if(node.left != null) q.add(node.left);
if(node.right != null) q.add(node.right);
}
res.add(tmp);
}
return res;
}
}