《程序员代码面试指南》第一章 栈和队列

来源:互联网 发布:梦里花落知多少网王txt 编辑:程序博客网 时间:2024/05/17 06:06

1、设计一个有getMin功能的栈

LeetCode对应题目 155. Min Stack

实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

华为2017校招的一个题目就是实现一个有getMingetMax功能的栈,采用了同样的方法。

栈的一个很重要的功能就是用于记录历史操作,并用于回滚。比如Word中的撤销功能,就是记录了指定次数的连续操作,如果需要回滚就弹出栈中的记录进行回滚操作,并把最新的行为推入栈中。利用栈的这一性质可以记录在每一阶段中,每一个栈深度内的最小值。

用一个栈保存正常栈的所有数据元素,用另外一个栈来记录每步操作的”历史” 栈中最小值。当一个数据入栈时,如果这个数据小于或者等于”历史”栈中的顶部值,也就是刷新了当前栈中的最小值记录,就把这个值也压入最小值栈中。根据这个压栈条件,只有不比最小栈栈顶元素大的数据才能入最小栈,也就是这个最小栈中的元素自底向上是非下降的。再来一个新的数据如果不能刷新当前记录,就不能入最小栈,也就是这个最小栈里的这条数据是到目前为止的最小记录,它的前一条数据就是之前的最小记录。

出栈的时候就要考虑当前出栈的这个数据是否是当前的最小记录,如果是的话这个数据会在出栈后消失,则最小记录就要更新了。而更新之后的记录就是最小栈出栈后的top

class MinStack {    public MinStack() {        stackData = new Stack<Integer>();        stackMin = new Stack<Integer>();    }    public void push(int x) {        stackData.push(x);        if (stackMin.empty()) stackMin.push(x);        else if (stackMin.peek() >= x) stackMin.push(x);    }    public void pop() {        if (stackMin.empty()) stackData.pop();        if (stackMin.peek() >= stackData.peek()) { stackMin.pop(); stackData.pop(); }        else stackData.pop();    }    public int top() {        return stackData.peek();    }    public int getMin() {        return stackMin.peek();    }    private Stack<Integer> stackData;    private Stack<Integer> stackMin;}

2、由两个栈组成的队列

LeetCode对应题目 232. Implement Queue using Stacks

栈的特点是先进后出,而队列的特点是先进先出。使用两个栈可以把顺序反过来实现类似队列的操作。

一个栈用以作为压入栈,另一个栈作为弹出栈。即压入的时候就将元素放在压入栈,而弹出时若栈中为空,就一次性将压入栈中的元素顺次弹出并且压入弹出栈中,若不空就正常弹出。

public class TwoStacksQueue {    public TwoStacksQueue() {        stackPush = new Stack<Integer>();        stackPop = new Stack<Integer>();    }    public void push(int x) {        stackPush.push(x);    }    public int pop() {        if (stackPop.empty()) {            while (!stackPush.empty()) stackPop.push(stackPush.pop());        }        return stackPop.pop();    }    public int peek() {        if (stackPop.empty()) {            while (!stackPush.empty()) stackPop.push(stackPush.pop());        }        return stackPop.peek();    }    public boolean empty() {        return stackPush.empty() && stackPop.empty();    }    private Stack<Integer> stackPush;    private Stack<Integer> stackPop;}

3、如何仅用递归函数和栈操作逆序一个栈

觉得与栈并无太大关联,难点在于利用递归函数在每次调用时可以产生一个栈帧保存临时变量,此栈是系统栈。

设计递归函数可以学到的经验是,如果要设计一个带返回值的递归函数,把这个递归函数单纯看做完成此功能的函数去调用,思维不要跟着递归函数递归进去,再对递归结束条件进行仔细考虑。
对于无返回值的递归函数,该函数主要改变了传入参数的状态,这时就要跟进去,一层一层思考这个函数还有哪些地方需要完善,最后确定递归结束条件。

public class ReverseStack {    public static int getAndRemoveLastElement(Stack<Integer> stack) {        int top = stack.pop();        if (stack.empty()) return top;        else {            int last = getAndRemoveLastElement(stack);            stack.push(top);            return last;        }    }    public static void reverse(Stack<Integer> stack) {        if (stack.empty()) return;        int last = getAndRemoveLastElement(stack);        reverse(stack);        stack.push(last);    }}

4、猫狗队列

设计题,留坑

5、用一个栈实现另一个栈的排序

辅助栈用来顺序放置stack里的元素,当遇到一个比help栈顶元素大的stack元素时,用stack来临时放置比该元素小的help栈里的元素,类似插入排序。

class SortStack {    public static void sortStackByStack(Stack<Integer> stack) {        Stack<Integer> help = new Stack<Integer>();        while (!stack.isEmpty()) {            int cur = stack.pop();            while (!help.isEmpty() && help.peek() < cur) {                stack.push(help.pop());            }            help.push(cur);        }        while (!help.isEmpty()) {            stack.push(help.pop());        }    }}

6、用栈来求解汉诺塔问题

难题留坑

7、生成窗口最大值数组

class Solution {    public int[] maxSlidingWindow(int[] nums, int k) {        if (nums == null || k < 1 || nums.length < k) return new int[0];        LinkedList<Integer> qmax = new LinkedList<Integer>();        int[] res = new int[nums.length-k+1];        int index = 0;        for (int i = 0; i < nums.length; ++i) {            while (!qmax.isEmpty() && nums[qmax.peekLast()] <= nums[i]) {                qmax.pollLast();            }            qmax.addLast(i);            if (qmax.peekFirst() == i-k) {                qmax.pollFirst();            }            if (i >= k-1) {                res[index++] = nums[qmax.peekFirst()];            }        }        return res;    }}

8、构造数组的MaxTree

9、求最大子矩阵的大小

在直方图最大矩形的基础上稍作改变。统计每一行矩阵的高度,再直接放入largestRectangleArea算法中求出,更新最大值。

class Solution {public:    int maximalRectangle(vector<vector<char>>& matrix) {        if (matrix.size() == 0) return 0;        int h = matrix.size(), maxarea = 0;        vector<int> vec(matrix[0].size(), 0);        for (int i = 0; i < h; ++i) {            for (int j = 0; j < matrix[i].size(); ++j) {                vec[j] = matrix[i][j] == '0' ? 0 : vec[j]+1;            }            maxarea = max(maxarea, largestRectangleArea(vec));        }        return maxarea;    }    int largestRectangleArea(vector<int>& heights) {        if (heights.size() == 0) return 0;        int len = heights.size(), maxarea = 0;        stack<int> stk;        for (int i = 0; i < len; ++i) {            while (!stk.empty() && heights[i] <= heights[stk.top()]) {                int p = stk.top(); stk.pop();                int k = stk.empty() ? -1 : stk.top();                maxarea = max(maxarea, (i-k-1)*heights[p]);            }            stk.push(i);        }        while (!stk.empty()) {            int p = stk.top(); stk.pop();            int k = stk.empty() ? -1 : stk.top();            maxarea = max(maxarea, (len-k-1)*heights[p]);        }        return maxarea;    }};

10、最大值减去最小值小于或等于num的子数组数量