算法 | 第3章 栈与队列相关《程序员面试金典》

2021-10-17  本文已影响0人  多氯环己烷

前言

本系列笔记主要记录笔者刷《程序员面试金典》算法的一些想法与经验总结,按专题分类,主要由两部分构成:经验值点和经典题目。其中重点放在经典题目上;


0. *经验总结

0.1 程序员面试金典 P82

0.2 Java常用数据结构API

详细见以下文章:
Java | 个人总结的Java常用API手册汇总

0.3 关于延迟移动


1. 三合一 [easy]

三合一

1.1 考虑点

1.2 解法

1.2.1 三指针法

class TripleInOne {
    int[] stack;
    int stackSize;
    int t0;
    int t1;
    int t2;

    public TripleInOne(int stackSize) {
        stack = new int[stackSize*3];
        int t0 = -3;
        int t1 = -2;
        int t2 = -1;

        this.stackSize = stackSize;
        this.t0 = t0;
        this.t1 = t1;
        this.t2 = t2;
    }
    
    public void push(int stackNum, int value) {
        if( stackNum == 0 ){
            this.t0 = pushVal( t0, value );
        } else if( stackNum == 1 ){
            this.t1 = pushVal( t1, value);
        } else if( stackNum == 2){
            this.t2 = pushVal( t2, value);
        }
    }
    public int pushVal( int top, int value ){
        if( top + 3 < stackSize*3 ){
            top += 3;
            stack[top] = value;
        }
        return top;
    }
    
    public int pop(int stackNum) {
        int val = peek(stackNum);
        if( val != -1 ){
            if( stackNum == 0 ){
                stack[t0] = -1;
                t0 -= 3;
            } else if( stackNum == 1 ){
                stack[t1] = -1;
                t1 -= 3;
            } else if( stackNum == 2){
                stack[t2] = -1;
                t2 -= 3;
            }
            return val;
        }
        return -1;

    }
    
    public int peek(int stackNum) {
        if( stackNum == 0 && t0 >= 0 ){
            return stack[t0];
        } else if( stackNum == 1 && t1 >= 1){
            return stack[t1];
        } else if( stackNum == 2 && t2 >= 2){
            return stack[t2];
        }
        return -1;
    }
    
    public boolean isEmpty(int stackNum) {
        int value = peek(stackNum);
        if( value == -1){
            return true;
        }
        return false;
    }
}

1.2.2 二维数组法(优)

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]++;
        }
    }
    
    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;
    }
}

1.2.3 一维数组法

class TripleInOne {
    int N = 3;
    int[] data;
    int[] locations;
    int size;
    public TripleInOne(int stackSize) {
        size = stackSize;
        data = new int[size * N];
        locations = new int[N];
        for (int i = 0; i < N; i++) {
            locations[i] = i * size;
        }
    }
    
    public void push(int stackNum, int value) {
        int idx = locations[stackNum];
        if (idx < (stackNum + 1) * size) {
            data[idx] = value;
            locations[stackNum]++;
        }
    }
    
    public int pop(int stackNum) {
        int idx = locations[stackNum];
        if (idx > stackNum * size) {
            locations[stackNum]--;
            return data[idx - 1];
        } else {
            return -1;
        }
    }
    
    public int peek(int stackNum) {
        int idx = locations[stackNum];
        if (idx > stackNum * size) {
            return data[idx - 1];
        } else {
            return -1;
        }
    }
    
    public boolean isEmpty(int stackNum) {
        return locations[stackNum] == stackNum * size;
    }
}

2. 栈的最小值 [easy]

栈的最小值

2.1 考虑点

2.2 解法

2.2.1 循环遍历法

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;

    /** initialize your data structure here. */
    public MinStack() {
        Stack<Integer> stack = new Stack<>();
        Stack<Integer> minStack = new Stack<>();
        this.stack = stack;
        this.minStack = minStack;
    }

    public void push(int x) {
        stack.add(x);
        if( minStack.isEmpty() ){
            minStack.add(x);
            return;
        }
        Stack<Integer> cache = new Stack<>();
        boolean isMatter = false;
        while( !isMatter ){
            if( !minStack.isEmpty() && minStack.peek() < x ){
                cache.add( minStack.pop() );
            } else {
                minStack.add(x);
                isMatter = true;
            }
        }
        while( !cache.isEmpty() ){
            minStack.add(cache.pop());
        }
    }

    public void pop() {
        if( stack.isEmpty() ){
            return;
        }
        int topNum = stack.pop();
        Stack<Integer> cache = new Stack<>();
        boolean isMatter = false;
        while( !isMatter ){
            if( minStack.peek() == topNum ){
                minStack.pop();
                isMatter = true;
            } else {
                cache.add(minStack.pop());
            }
        }
        while( !cache.isEmpty() ){
            minStack.add(cache.pop());
        }
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

2.2.2 主辅栈法(优)

在这里插入图片描述
class MinStack {
    Deque<Integer> xStack;
    Deque<Integer> minStack;

    public MinStack() {
        xStack = new LinkedList<Integer>();
        minStack = new LinkedList<Integer>();
        minStack.push(Integer.MAX_VALUE);
    }
    
    public void push(int x) {
        xStack.push(x);
        minStack.push(Math.min(minStack.peek(), x));
    }
    
    public void pop() {
        xStack.pop();
        minStack.pop();
    }
    
    public int top() {
        return xStack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

3. 堆盘子 [medium]

堆盘子

3.1 考虑点

3.2 解法

3.2.1 单链表寻栈法

class StackOfPlates {
    int cap;
    List<Stack<Integer>> list;

    public StackOfPlates(int cap) {
        List<Stack<Integer>> list = new ArrayList<>();
        this.cap = cap;
        this.list = list;
    }
    
    public void push(int val) {
        if( cap == 0 ){
            return;
        }
        Stack<Integer> stack;
        if( list.isEmpty() ){
            stack = new Stack<Integer>();
            list.add(stack);
        } else {
            stack = list.get(list.size() - 1);
            if( stack.size() >= cap ){
                stack = new Stack<Integer>();
                list.add(stack);
            }
        }
        if( stack.size() < cap ){
            stack.add(val);
        }
    }
    
    public int pop() {
        if( cap == 0 ){
            return -1;
        }
        if( list.isEmpty() ){
            return -1;
        }
        Stack<Integer> stack = list.get(list.size() - 1);
        int result = stack.pop();
        if(stack.isEmpty()){
            list.remove( list.size() - 1 );
        }
        return result;
    }
    
    public int popAt(int index) {
        if( cap == 0 ){
            return -1;
        }
        if( index > list.size()-1 || index < 0 || list.isEmpty() ){
            return -1;
        }
        Stack<Integer> stack = list.get(index);
        int result = stack.pop();
        if(stack.isEmpty()){
            list.remove( index );
        }
        return result;
    }
}

3.2.2 二维链表法(优)

class StackOfPlates {
    private LinkedList<LinkedList<Integer>> setOfStacks;
    private int cap;

    public StackOfPlates(int cap) {
        this.setOfStacks = new LinkedList<>();
        this.cap = cap;
    }

    private boolean setIsEmpty() {
        return setOfStacks.isEmpty();
    }

    private boolean lastStackIsFUll() {
        if (setIsEmpty()) {
            return true;
        }
        return setOfStacks.getLast().size()>=cap;
    }

    private boolean lastStackIsEmpty() {
        return setOfStacks.getLast().isEmpty();
    }

    public void push(int val) {
        if (cap <= 0) {
            return;
        }
        if (setIsEmpty() || lastStackIsFUll()) {
            setOfStacks.addLast(new LinkedList<>());
        }
        setOfStacks.getLast().addLast(val);
    }

    public int pop() {
        int val = -1;
        if (setIsEmpty()) {
            return val;
        }
        val = setOfStacks.getLast().removeLast();
        if (lastStackIsEmpty()) {
            setOfStacks.removeLast();
        }
        return val;
    }

    public int popAt(int index) {
        int val = -1;
        if (setIsEmpty() ||setOfStacks.size()-1<index ) {
            return val;
        }
        val = setOfStacks.get(index).removeLast();
        if (setOfStacks.get(index).isEmpty()) {
            setOfStacks.remove(index);
        }
        return val;
    }
}

4. 化栈为队 [easy]

化栈为队

4.1 考虑点

4.2 解法

4.2.1 双栈法

class MyQueue {
    Stack<Integer> stack;
    Stack<Integer> cache;

    /** Initialize your data structure here. */
    public MyQueue() {
        Stack<Integer> stack = new Stack<>();
        Stack<Integer> cache = new Stack<>();
        
        this.stack = stack;
        this.cache = cache;
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        stack.add(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if(stack.isEmpty()){
            return -1;
        }
        while( !stack.isEmpty() ){
            cache.add(stack.pop());
        }
        int result = cache.pop();
        while( !cache.isEmpty() ){
            stack.add(cache.pop());
        }
        return result;
    }
    
    /** Get the front element. */
    public int peek() {
        if(stack.isEmpty()){
            return -1;
        }
        while( !stack.isEmpty() ){
            cache.add(stack.pop());
        }
        int result = cache.peek();
        while( !cache.isEmpty() ){
            stack.add(cache.pop());
        }
        return result;
    }
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack.isEmpty();
    }
}

4.2.2 延迟移动法(优)

class MyQueue {
    Deque<Integer> inStack;
    Deque<Integer> outStack;

    public MyQueue() {
        inStack = new LinkedList<Integer>();
        outStack = new LinkedList<Integer>();
    }
    
    public void push(int x) {
        inStack.push(x);
    }
    
    public int pop() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.pop();
    }
    
    public int peek() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.peek();
    }
    
    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }

    private void in2out() {
        while (!inStack.isEmpty()) {
            outStack.push(inStack.pop());
        }
    }
}

5. 栈排序 [medium]

栈排序

5.1 考虑点

5.2 解法

5.2.1 单栈法

class SortedStack {
    Stack<Integer> stack;
    Stack<Integer> cache;

    public SortedStack() {
        Stack<Integer> stack = new Stack<>();
        Stack<Integer> cache = new Stack<>();
        this.stack = stack;
        this.cache = cache;
    }
    
    public void push(int val) {
        if( stack.isEmpty() ){
            stack.add(val);
            return;
        }
        if( stack.peek() > val ){
            stack.add(val);
        } else {
            cache.add(stack.pop());
            stack.add(val);
            stack.add(cache.pop());
        }
    }
    
    public void pop() {
        if( stack.isEmpty() ){
            return;
        }
        stack.pop();
        int val;
        if( !stack.isEmpty() ){
            val = stack.peek(); //忘记peek
        } else {
            return;
        }
        while(!stack.isEmpty()){
            if( stack.peek() >= val ){
                cache.add(stack.pop());
            } else {
                val = stack.peek();
            }
        }
        boolean isEliminate = false;
        while( !cache.isEmpty() ){
            if( cache.peek() == val && !isEliminate ){
                cache.pop();
                isEliminate = true; //忘记上锁
            } else {
                stack.add( cache.pop() );
            }
        }
        stack.add(val);
    }
    
    public int peek() {
        if(stack.isEmpty()){
            return -1;
        }
        return stack.peek(); 
    }
    
    public boolean isEmpty() {
        return stack.isEmpty();
    }
}

5.2.2 根据插入值分离栈法(优)

class SortedStack {
    //原始栈
    Deque<Integer> stack = new LinkedList<>();
    //辅助栈
    Deque<Integer> tmp = new LinkedList<>();
    public SortedStack() {
    }
    
    public void push(int val) {
        int max = stack.isEmpty() ? Integer.MAX_VALUE : stack.peek();
        int min = tmp.isEmpty() ? Integer.MIN_VALUE : tmp.peek();
        //比较原始栈与辅助栈栈顶值,使其满足 辅助栈 <= val <= 原始栈
        while(true){
            if(val > max){
                tmp.push(stack.pop());
                max = stack.isEmpty() ? Integer.MAX_VALUE : stack.peek();
            } else if(val < min){
                stack.push(tmp.pop());
                min = tmp.isEmpty() ? Integer.MIN_VALUE : tmp.peek();
            } else{
                stack.push(val);
                break;
            }
        }
    }
    
    public void pop() {
        //将辅助栈元素push回原始栈
        while (!tmp.isEmpty()){
            stack.push(tmp.pop());
        }
        if (!stack.isEmpty())
            stack.pop();
    }
    
    public int peek() {
        //将辅助栈元素push回原始栈
        while (!tmp.isEmpty()){
            stack.push(tmp.pop());
        }
        return stack.isEmpty() ? -1 : stack.peek();
    }
    
    public boolean isEmpty() {
        return stack.isEmpty() && tmp.isEmpty();
    }
}

6. 动物收容所 [easy]

动物收容所

6.1 考虑点

6.2 解法

6.2.1 单链表法

class AnimalShelf {
    List<String> list;
    
    public AnimalShelf() {
        List<String> list = new ArrayList<>();
        this.list = list;
    }
    
    public void enqueue(int[] animal) {
        if( animal[0] < 0 || animal[1] < 0 || animal[1] > 2){
            return;
        }
        String animalStr = String.valueOf(animal[0]) + String.valueOf(animal[1]);
        list.add(animalStr);
    }
    
    public int[] dequeueAny() {
        if( list.isEmpty() ){
            return new int[]{-1, -1};
        }
        String result = list.get(0);
        list.remove(0);
        int num = Integer.parseInt(result);
        return new int[]{num/10, num%10};
    }
    
    public int[] dequeueDog() {
        if( list.isEmpty() ){
            return new int[]{-1, -1};
        }
        for( int i = 0; i < list.size(); i++){
            int num = Integer.parseInt( list.get(i) );
            if( num%10 == 1 ){
                list.remove(i);
                return new int[]{num/10, num%10};
            }
        }
        return new int[]{-1, -1};
    }
    
    public int[] dequeueCat() {
        if( list.isEmpty() ){
            return new int[]{-1, -1};
        }
        for( int i = 0; i < list.size(); i++){
            int num = Integer.parseInt( list.get(i) );
            if( num%10 == 0 ){
                list.remove(i);
                return new int[]{num/10, num%10};
            }
        }
        return new int[]{-1, -1};
    }
}

6.2.2 双队列法(优)

class AnimalShelf {
    LinkedList<int[]> queueCat;
    LinkedList<int[]> queueDog;

    public AnimalShelf() {
        queueCat = new LinkedList<>();
        queueDog = new LinkedList<>();
    }

    public void enqueue(int[] animal) {
        // 判断种类后入队
        if (animal[1] == 0) {
            queueCat.addLast(animal);
        } else if (animal[1] == 1) {
            queueDog.addLast(animal);
        }
    }

    public int[] dequeueAny() {
        // 取出cat的队首,判空则直接返回
        int[] headCat;
        if (!queueCat.isEmpty()) {
            headCat = queueCat.getFirst();
        } else if (!queueDog.isEmpty()) {
            return queueDog.removeFirst();
        } else {
            return new int[]{-1,-1};
        }
        // 取出dog的队首,判空则直接返回
        int[] headDog;
        if (!queueDog.isEmpty()) {
            headDog = queueDog.getFirst();
        } else {
            return queueCat.removeFirst();
        }
        // 比较后返回
        if (headCat[0]<=headDog[0]) {
            return queueCat.removeFirst();
        } else {
            return queueDog.removeFirst();
        }
    }

    public int[] dequeueDog() {
        if (!queueDog.isEmpty()) {
            return queueDog.removeFirst();
        } else {
            return new int[]{-1,-1};
        }
    }

    public int[] dequeueCat() {
        if (!queueCat.isEmpty()) {
            return queueCat.removeFirst();
        } else {
            return new int[]{-1,-1};
        }
    }
}

最后

\color{blue}{\rm\small{新人制作,如有错误,欢迎指出,感激不尽!}}

\color{blue}{\rm\small{欢迎关注我,并与我交流!}}

\color{blue}{\rm\small{如需转载,请标注出处!}}

上一篇 下一篇

猜你喜欢

热点阅读