前言
如果想进大厂工作,除了基础知识,项目经验,还有一些非常重要的能力需要锻炼,其中最重要的就是算法。算法题千千万,但是类型也就几十种,接下来的系列文章,我将面试中常见的算法题类型进行归纳。
栈面试题01. 三合一
三合一。描述如何只用一个数组来实现三个栈。
你应该实现push(stackNum, value)、pop(stackNum)、isEmpty(stackNum)、peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。
构造函数会传入一个stackSize参数,代表每个栈的大小。
分析:
二维数组
题目只要求我们使用「一个数组」来实现栈,并没有限制我们使用数组的维度。
因此一个简单的做法是,建立一个二维数组 datadata 来做。
二维数组的每一行代表一个栈,同时使用一个 locations,locations 记录每个栈「待插入」的下标。
具体步骤:
(1)获取二维数组的一行,即其中一个栈
(2)得到该栈待插入的下标
(3)进行相应的操作,push, pop等
(4)该栈待插入的下标+1,或者-1,根据情况变化
代码:
class TripleInOne {
int N = 3;
// 3 * n 的数组,每一行代表一个栈
int[][] data;
// 记录每个栈「待插入」位置
int[] locations;
public TripleInOne(int stackSize) {
data = new int[N][stackSize];
locations = new int[N];
}
public void push(int stackNum, int value) {
int[] stk = data[stackNum];// 获取二维数组的一行,即一个栈
int loc = locations[stackNum];// 查看该栈目前待插入的下标
if (loc < stk.length) {
stk[loc] = value;
locations[stackNum]++;// 已经插入数据,该栈目前待插入的下标+1
}
}
public int pop(int stackNum) {
int[] stk = data[stackNum];
int loc = locations[stackNum];
if (loc > 0) {
int val = stk[loc - 1];
locations[stackNum]--;
return val;
} else {
return -1;
}
}
public int peek(int stackNum) {
int[] stk = data[stackNum];
int loc = locations[stackNum];
if (loc > 0) {
return stk[loc - 1];
} else {
return -1;
}
}
public boolean isEmpty(int stackNum) {
return locations[stackNum] == 0;
}
}
栈面试题02. 堆盘子
堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构SetOfStacks,模拟这种行为。SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。
进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。
当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,pop,popAt 应返回 -1.
分析:
放盘子push操作时,当最后一堆数量等于容量capacity时,就要新开一堆往里放。
拿盘子pop操作时,拿完之后如果这一堆没有盘子了,就得把这堆删除。popAt同理。
边界条件:即没有盘子时,pop 和 popAt都直接返回-1
隐藏边界条件:给定的初始化容量可能等于0,说明后边的操作都无法放置盘子。
代码:
class StackOfPlates {
int capacity;// 每堆盘子的容量
List<Deque<Integer>> allPlates; // 盘子堆
// 初始化容量及盘子堆
public StackOfPlates(int cap) {
this.capacity = cap;
allPlates = new LinkedList<>();
}
public void push(int val) {
if(capacity <= 0){// 容量为0,不需要继续放盘子
return;
}
// 没有盘子堆 或者 最后一堆的数量等于容量时,需要新开一堆盘子
if(allPlates.isEmpty() || allPlates.get(allPlates.size() - 1).size() == capacity){
allPlates.add(new ArrayDeque<>());
}
allPlates.get(allPlates.size()-1).offerFirst(val);// 放最后
}
public int pop() {
// 没有盘子了 返回-1
if(allPlates.size() == 0){
return -1;
}
// 从最后一堆盘子顶上拿一个盘子
int result = allPlates.get(allPlates.size() - 1).pollFirst();
// 拿完之后,如果空了,就把这堆删掉
if(allPlates.get(allPlates.size() - 1).size() == 0){
allPlates.remove(allPlates.size() - 1);
}
return result;
}
public int popAt(int index) {
// 如果索引不合法,并且没有盘子了,就返回-1
if(index < 0 || index >= allPlates.size() || allPlates.isEmpty()){
return -1;
}
int result = allPlates.get(index).pollFirst();
if(allPlates.get(index).size() == 0){
allPlates.remove(index);
}
return result;
}
}
栈面试题03. 化栈为队
实现一个MyQueue类,该类用两个栈来实现一个队列。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明: 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
代码:
class MyQueue {
// stack的一些方法介绍
// boolean empty() 测试堆栈是否为空
// Object peek() 查看堆栈顶部的对象,但不从堆栈中移除它
// Object pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象
// Object push(Object element)把项压入堆栈顶部
// 解题思路
// (1)入队操作(push)时,先把数据存入stack1
// (2)查询队列第一个数据(peek)时,把stack1的数据填充到stack2中,弹出Stack2的第
// 一个数据(即结果),然后把stack2中的数据填充到stack1中
// (3)删除队列第一个数据(pop)时,把stack1的数据填充到stack2中,弹出Stack2的第
// 一个数据(即结果),然后把stack2中的数据填充到stack1中
// (4)判断队列是否为空,stack1和stack2同时为空,则队列为空
// 初始化两个栈stack1,stack2
Stack<Integer> stack1;
Stack<Integer> stack2;
/** Initialize your data structure here. */
// 在此初始化数据结构
public MyQueue() {
stack1 = new Stack<Integer>();
stack2 = new Stack<Integer>();
}
/** Push element x to the back of queue. */
// 将元素x推送到队列的后面
public void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
// 从队列前面删除该元素并返回该元素
public int pop() {
while (!stack1.empty()) {
stack2.push(stack1.pop());
}
int result = stack2.pop();
while (!stack2.empty()) {
stack1.push(stack2.pop());
}
return result;
}
/** Get the front element. */
// 获取前元素
public int peek() {
while (!stack1.empty()) {
stack2.push(stack1.pop());
}
int result = stack2.peek();
while (!stack2.empty()) {
stack1.push(stack2.pop());
}
return result;
}
/** Returns whether the queue is empty. */
// 返回队列是否为空
public boolean empty() {
return stack1.empty() && stack2.empty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
栈面试题04. 栈排序
栈排序。编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。该栈支持如下操作:push、pop、peek 和 isEmpty。当栈为空时,peek 返回 -1。
说明:
栈中的元素数目在[0, 5000]范围内。
思路:
临时栈
题目要求只能使用一个临时栈存放数据
入栈时,先将栈中小于val的数值暂存到临时栈中
将val入栈
再将临时栈中的数据push会栈中
代码:
public class SortedStack {
private Deque<Integer> stack, temp;
public SortedStack() {
stack = new LinkedList<>();
temp = new LinkedList<>();
}
public void push(int val) {
// 栈为空,入栈
if (isEmpty()) stack.push(val);
else {
// 将栈中小于val的值暂存到temp中
while (!stack.isEmpty() && val > stack.peek()) {
temp.push(stack.pop());
}
// 入栈
stack.push(val);
// 将temp中暂存内容push回来
while (!temp.isEmpty()) {
stack.push(temp.pop());
}
}
}
public void pop() {
if (stack.isEmpty()) return;
stack.pop();
}
public int peek() {
if (stack.isEmpty()) return -1;
return stack.peek();
}
public boolean isEmpty() {
return stack.isEmpty();
}
}