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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43928720/article/details/100189469

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

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

要求:

  1. pop、push、getMin操作时间复杂度都是O(1)
  2. 设计的栈类型可以使用现成的栈结构

代码:

public class Chapter1_1 {
    private Stack<Integer> stackData;//数据栈,压栈的数据
    private Stack<Integer> stackMin;//辅助栈,从栈顶到栈底 由小到大有序的数据

    public Chapter1_1(){
        stackData = new Stack<Integer>(); //在new的时候 初始化stackData内存空间
        stackMin = new Stack<Integer>(); //在new的时候 初始化stackMin内存空间
    }
    //压入栈中
    public void push(int value){
        //将数据压入栈中
        stackData.push(value);
        //如果辅助栈是空的则直接压入
        if(stackMin.isEmpty()){
            stackMin.push(value);
        }else if(value<stackMin.peek()){
            //value比栈顶元素小
            stackMin.push(value);
        }
    }

    //弹栈
    public int pop(){
        //如果数据栈中已空,则 返回 -1
        if(stackData.isEmpty()){
            return -1;
        }
        //数据栈弹出
       int value =  stackData.pop();
       if(value ==stackMin.peek()){
           stackMin.pop();
       }
        return value;
    }
    //获取最小值
    public int getMin(){
        //获取辅助栈顶元素即可
        return stackMin.peek();
    }
}

2. 由两个栈组成的队列

题目:
用两个栈实现队列,可以进行 add、poll、peek 操作

代码:

public class Chapter1_2 {
    private Stack<Integer> stack01;//数据栈,压栈的数据
    private Stack<Integer> stack02;//辅助栈,stack01的逆序
    public Chapter1_2(){
        stack01 = new Stack<Integer>(); //在new的时候 初始化stack01内存空间
        stack02 = new Stack<Integer>(); //在new的时候 初始化stack02内存空间
    }
    //队尾入
    public void add(int value){
        //将辅助栈中的元素转移到 01栈中,保证新的元素在以前元素的后面
        while(!stack02.isEmpty()){
            stack01.push(stack02.pop());
        }
        stack01.push(value);
    }
    //队头出
    public int poll(){
        //如果01栈和02栈都为空,则说明 队列为空了
        if(stack01.isEmpty() && stack02.isEmpty() ){
            return -1;
        }
        //如果02不为空 则说明 是连续的出队列操作,此时所有的元素都在02栈中,直接出02栈即可
        if(!stack02.isEmpty() ){
            return stack02.pop();
        }
        //02栈为空,将01栈中的元素转移到 02栈中,保证01最底部的元素在02的栈顶
        while(!stack01.isEmpty()){
            stack02.push(stack01.pop());
        }
        return stack02.pop();
     }
    //查看队头数据
    public int peek(){
        //如果02栈为空,则说明 队列为空了
        if(stack02.isEmpty() ){
            return -1;
        }
        return stack02.pop();
    }
    public boolean isEmpty(){
        //如果01栈和02栈都为空,则说明 队列为空了
        return stack01.isEmpty() && stack02.isEmpty();
    }
}
  1. 猫狗队列
    题目:
    通过给定的类实现猫狗队列

代码:

public class Chapter1_4 {
    private static final String DOG ="dog";
    private static final String CAT ="cat";

    private Queue<PetQueue> dogQueue ; //存放狗的队列
    private Queue<PetQueue> catQueue; //存放猫的队列
    private long count;//标记存放先后顺序的
    public Chapter1_4(){
        this.dogQueue = new LinkedList<PetQueue>();//初始化
        this.catQueue = new LinkedList<PetQueue>();//初始化
        this.count = 0;//初始化
    }
    public void add(Pet pet){
        if(DOG.equalsIgnoreCase(pet.getType())){
             dogQueue.offer(new PetQueue(pet,this.count++));
        }else{
            catQueue.offer(new PetQueue(pet,this.count++));
        }
    }

    //取出队列中最早进入队列的宠物
    public Pet pollAll(){
       //如果 两个队列中都有值
        if(!dogQueue.isEmpty() && !catQueue.isEmpty()){
            //如果dog的存放的编号小于cat的编号,则 说明dog比cat存放的早
            if(dogQueue.peek().getNumber()<catQueue.peek().getNumber()){
                return dogQueue.poll().getPet();
            }else{
                return catQueue.poll().getPet();
            }
        }else if(!dogQueue.isEmpty()) {
            //如果dog队列中有值而 cat队列已为空
            return dogQueue.poll().getPet();
        }else if(!catQueue.isEmpty()){
            //如果cat队列中有值而 dog队列已为空
            return catQueue.poll().getPet();
        }else{
            //都为空
            return null;
        }
    }
    //判断对列中是否都为空
    public boolean isEmpty(){
        return dogQueue.isEmpty() && catQueue.isEmpty();
    }
    //判断dog队列中是否都为空
    public boolean dogIsEmpty(){
        return dogQueue.isEmpty();
    }
    //判断cat队列中是否都为空
    public boolean catIsEmpty(){
        return catQueue.isEmpty();
    }
}

4. 用一个栈实现另一个栈的排序

题目:
将一个存放整数的栈,从栈顶到栈底 由小到大排列,只能用一个辅助栈,可以用辅助变量,不能用其他数据结构

代码:

public class Chapter1_5 {

    //借助一个辅助栈排序
    public Stack<Integer> sort(Stack<Integer> stack){
        Stack<Integer> heleStack = new Stack<Integer>();//辅助栈
        int vlaue = 0; //辅助变量 暂存栈中弹出的元素
        while(!stack.isEmpty()){
            vlaue =   stack.pop();
            while(!heleStack.isEmpty() && vlaue< heleStack.peek()){
                stack.push(heleStack.pop());
            }
            heleStack.push(vlaue);
        }
        // 此时 辅助栈中栈顶到栈底 是从大到小的,再放进原栈中,则元素为从小到大
        while(!heleStack.isEmpty()){
            stack.push(heleStack.pop());
        }
        return stack;
    }
 }

5. 用栈实现汉诺塔问题

题目:
用栈实现汉诺塔,不能直接从左移到右,或者从右移到左,必须经过中间柱子。

代码:

public class Chapter1_6 {
    public static final String LEFT = "left";
    public static final String MID = "mid";
    public static final String RIGHT = "right";

    public int recMoveHanoi(int n, String left, String mid, String right) {
        if (n < 1) {
            return 0;
        }
        return recMoveHanoi(n, left, mid, right, LEFT, RIGHT);
    }

    //递归实现
    public int recMoveHanoi(int n, String left, String mid, String right, String from, String to) {
        //递归的终止条件 n==1
        if (n == 1) {
            //从左或右 移动到中间的,或者从中间移动到左或者右的,都只要一步
            if (MID.equalsIgnoreCase(from) || MID.equalsIgnoreCase(to)) {
                System.out.println("move 1 from " + from + " to " + to);
                return 1;
            } else {
                System.out.println("move 1 from " + from + " to  mid");
                System.out.println("move 1 from mid to " + to);
                return 2;
            }
        }
        //从左或右 移动到中间的,或者从中间移动到左或者右的,都只要一步
        if (MID.equalsIgnoreCase(from) || MID.equalsIgnoreCase(to)) {
            //获取对应的作为辅助的柱子,例如 from为mid ,to为left,则 right为辅助的柱子,
            String another = LEFT.equalsIgnoreCase(to) || LEFT.equalsIgnoreCase(from) ? RIGHT : LEFT;
            int step1 = recMoveHanoi(n - 1, left, mid, right, from, another);
            int step2 = 1;
            System.out.println("move " + n + " from " + from + " to " + to);
            int step3 = recMoveHanoi(n - 1, left, mid, right, another, to);
            return step1 + step2 + step3;
        } else {
            //从左到右 或者 从右到左
            //1.此时 n在form上,要经过mid,才能到to ,所以先将to作为辅助,将 n-1 移到to上
            int step1 = recMoveHanoi(n - 1, left, mid, right, from, to);
            //2.此时 n在form上, n-1在to上,所以将n移到mid上
            int step2 = 1;
            System.out.println("move " + n + " from " + from + " to  mid");
            //3.此时 n-1在to上,n在mid上,from作为辅助 ,所以先将n-1移到from上
            int step3 = recMoveHanoi(n - 1, left, mid, right, to, from);
            //4.此时 n-1在from上,n在mid上,将n移到to上
            int step4 = 1;
            System.out.println("move " + n + " from  mid to " + to);
            //5.此时 n-1在from上,n在to上,所以先将n-1移到to上
            int step5 = recMoveHanoi(n - 1, left, mid, right, from, to);
            return step1 + step2 + step3 + step4 + step5;
        }
    }
}

6. 生成窗口最大值数组

题目:
有一个整形数组arr,一个大小为 w 的窗口从数组最左边滑到最右边,每次移动一个位置,输出在每个窗口下的窗口最大值

代码:

public class Chapter1_7 {

    public int[] getMaxWindow(int[] arr, int w) {
        if (arr == null || arr.length < 1) {
            return new int[]{0};
        }
        //存放最终结果
        int length = arr.length;
        //比如 length =4,w=3, 则有两个窗口最大值
        int[] res = new int[length - w + 1];
        int index = 0;
        //存放数组的索引
        LinkedList<Integer> qmax = new LinkedList<Integer>();
        //循环数组
        for (int i = 0; i < arr.length; i++) {
            while (!qmax.isEmpty() && arr[qmax.peekLast()] < arr[i]) {
                qmax.pollLast();
            }
            qmax.offerLast(i);
            //如果 qmax中第一个存放的索引失效,则删除,比如 i=5,w=3,则 5-3=2 索引2和2之前的都是失效的
            if (qmax.peekFirst() <= i - w) {
                qmax.pollFirst();
            }
            //最大值结果是从 i>=w-1开始的记录的,例如 i=1 ,w=3 ,3-1=2, 此时还未到第一个窗口
            if (i >= w - 1) {
                res[index++] = arr[qmax.peekFirst()];
            }
        }
        return res;
    }
}

7. 构造数组的maxTree

题目:

public class Chapter1_8 {
    public Node getMaxTree(int[] arr) {
        if (arr == null) {
            return null;
        }
        int length = arr.length;
        Node[] nodes = new Node[length];
        //初始化Node数组
        for (int i = 0; i < length; i++) {
            nodes[i] = new Node(arr[i]);
        }
        //存放当前节点往左 第一个大于当前节点的节点
        Map<Node, Node> leftMap = new HashMap<Node, Node>();
        //存放当前节点往右 第一个大于当前节点的节点
        Map<Node, Node> rightMap = new HashMap<Node, Node>();
        //存放遍历到节点
        Stack<Node> stack = new Stack<Node>();
        for (int i = 0; i < length; i++) {
            //关键地方,判断栈顶元素的值是否小于当前元素的值,如果小于则在map中记录栈顶元素的往左数第一个元素
            while (!stack.isEmpty() && stack.peek().value < nodes[i].value) {
                popStackToMap(stack, leftMap);
            }
            //存放当前元素,前提保证栈中没有比小的元素
            stack.push(nodes[i]);
        }
        //如果栈中还有元素,则循环获取栈中元素的往左数第一个大于他的元素
        while (!stack.isEmpty()) {
            popStackToMap(stack, leftMap);
        }

        for (int i = length - 1; i >= 0; i--) {
            //关键地方,判断栈顶元素的值是否小于当前元素的值,如果小于则在map中记录栈顶元素的往右数第一个元素
            while (!stack.isEmpty() && stack.peek().value < nodes[i].value) {
                popStackToMap(stack, rightMap);
            }
            //存放当前元素,前提保证栈中没有比小的元素
            stack.push(nodes[i]);
        }
        //如果栈中还有元素,则循环获取栈中元素的往右数第一个大于他的元素
        while (!stack.isEmpty()) {
            popStackToMap(stack, rightMap);
        }
        //再将 每个元素 左右 最大的元找见后,开始构造MaxTree
        Node head = null;
        for (int i = 0; i < length; i++) {
            Node node = nodes[i];
            Node leftNode = leftMap.get(node);
            Node rightNode = rightMap.get(node);
            //左右最大的元素都为空,则此元素是数组中最大,作为根节点
            if (leftNode == null && rightNode == null) {
                head = node;
            } else if (leftNode == null) {
                if (rightNode.left == null) {
                    rightNode.left = node;
                } else {
                    rightNode.right = node;
                }
            } else if (rightNode == null) {
                if (leftNode.left == null) {
                    leftNode.left = node;
                } else {
                    leftNode.right = node;
                }
            } else {
                Node parent = leftNode.value < rightNode.value ? leftNode : rightNode;
                if (parent.left == null) {
                    parent.left = node;
                } else {
                    parent.right = node;
                }
            }
        }
        return head;
    }

    public void popStackToMap(Stack<Node> stack, Map<Node, Node> map) {
        Node node = stack.pop();
        if (stack.isEmpty()) {
            map.put(node, null);
        } else {
            map.put(node, stack.peek());
        }
    }

    //简单的用递归中序遍历一下生成的MaxTree
    public void recInOrder(Node head) {
        if (head == null) {
            return;
        }
        recInOrder(head.left);
        System.out.print(head.value + " ");
        recInOrder(head.right);
    }
}

8. 求最大矩阵大小

题目:
给定一个整形数组 map,其中值只有0和1两种,求所有全是1的所有子矩阵中,最大的矩阵全是1的数量

代码:

public class Chapter1_9 {
    public int maxSubMatrixSize(int[][] map) {
        if (map == null) {
            return 0;
        }
        int[] height = new int[map[0].length];
        int maxSize = 0;
        for (int i = 0; i < map.length; i++) {
            //每一行中,子矩阵的高度
            for (int j = 0; j < map[0].length; j++) {
                height[j] = map[i][j] == 0 ? 0 : height[j] + 1;
            }
            maxSize = Math.max(getMatrixMaxSize(height), maxSize);
        }
        return maxSize;
    }

    public int getMatrixMaxSize(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        int maxSize = 0;
        Stack<Integer> stack = new Stack<Integer>();
        for (int i = 0; i < height.length; i++) {
            while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
                int j = stack.pop();
                int k = stack.isEmpty() ? -1 : stack.peek();
                //例如: i=6,k =4 则 元素的边界为4到6,
                maxSize = Math.max((i - k - 1) * height[j], maxSize);
            }
            stack.push(i);
        }
        //栈中可能还有未计算完的元素,此时height的长度作为元素的边界
        while (!stack.isEmpty()) {
            int j = stack.pop();
            int k = stack.isEmpty() ? -1 : stack.peek();
            maxSize = Math.max((height.length - k - 1) * height[j], maxSize);
        }
        return maxSize;
    }
}

9. 最大值减去最小值等于num的子数组数量

题目:
给定整数数组arr和整数num,共返回多少的数组满足如下情况
max(arr[i…j]) - min(arr[i…j]) <= num
max(arr[i…j])表示数组arr[i…j] 中最大值,min(arr[i…j])表示数组arr[i…j] 中最小值

代码:

public class Chapter1_10 {
    public int getNum(int[] arr, int k) {
        //存放最大值的索引
        LinkedList<Integer> qmax = new LinkedList<Integer>();
        //存放最小值的索引
        LinkedList<Integer> qmin = new LinkedList<Integer>();
        int i=0;
        int j=0;
        int res = 0;
        while (i < arr.length) {
            while (j < arr.length) {
                while (!qmax.isEmpty() && arr[qmax.peekLast()] < arr[j]) {
                    qmax.pollLast();
                }
                qmax.offerLast(j);
                while (!qmin.isEmpty() && arr[qmin.peekLast()] > arr[j]) {
                    qmin.pollLast();
                }
                qmin.offerLast(j);
                //左边界为i 右边界向右能扩展的最大距离
                if (arr[qmax.peekFirst()] - arr[qmin.peekFirst()] > k) {
                    break;
                }
                j++;
            }
            if (i == qmax.peekFirst()) {
                qmax.pollFirst();
            }
            if (i == qmin.peekFirst()) {
                qmin.pollFirst();
            }
            res += j - i;
            i++;
        }
        return res;
    }
 }

10. 有效的括号

题目:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

代码:

class Solution {
    public boolean isValid(String s) {
        if("".equals(s))
        {
            return true;
        }
        if(s.length() %2 != 0)
        {
            return false;
        }
        Map<Character, Integer> map = new HashMap<>();
        map.put('(',1);
        map.put(')',-1);
        map.put('[',2);
        map.put(']',-2);
        map.put('{',3);
        map.put('}',-3);
        char [] arr = s.toCharArray();
        
       Stack<Integer> stack = new Stack<>();
        stack.push(map.get(arr[0]));
        for(int i=1;i<arr.length;i++)
        {
            if(stack.isEmpty())
            {
                stack.push(map.get(arr[i]));
            }
            else if(map.get(arr[i]) + stack.peek() == 0)
            {
                stack.pop();
            }
            else{
                stack.push(map.get(arr[i]));
            }
        }
        
        if(stack.isEmpty())
        {
            return true;
        }
        return false;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43928720/article/details/100189469