【算法】牛客网算法初级班(栈、队列、链表、数组和矩阵结构介绍及常见面试题讲解(上))

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ARPOSPF/article/details/81903372

栈、队列、链表、数组和矩阵结构介绍及常见面试题讲解(上)


题目一:用数组结构实现大小固定的队列和栈

实现栈结构:

栈结构是先进后出的,只需要一个数组和一个记录位置的变量size,当进来一个元素,size就++,出去一个元素size就--。

实现队列结构:

相对栈结构要难搞一些,队列的先进先出的,需要一个数组和三个变量,size记录已经进来了多少个元素,end记录刚进来的元素应该放在哪个位置,start表示用户要求弹出的元素所在的位置。size的作用不止于此,它还是end与start的操作的关键信息,使得end与start解耦,避免的很多的麻烦,当end或者start达到底部的时候就跳回0处。

代码:

/**
 * 用数组实现大小固定的栈和队列
 */
public class Array_To_Stack_Queue {
    public static class ArrayStack {
        private Integer[] arr;
        private Integer next;

        public ArrayStack(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            next = 0;
        }

        public Integer peek() {
            if (next == 0) {
                return null;
            }
            return arr[next - 1];
        }

        public void push(int obj) {
            if (next == arr.length) {
                throw new ArrayIndexOutOfBoundsException("This stack is full");
            }
            arr[next++] = obj;
        }

        public Integer pop() {
            if (next == 0) {
                throw new ArrayIndexOutOfBoundsException("This stack is empty");
            }
            return arr[next--];
        }
    }

    public static class ArrayQueue {
        private Integer[] arr;
        private Integer size;
        private Integer start;
        private Integer end;

        public ArrayQueue(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            size = 0;
            start = 0;
            end = 0;
        }

        public Integer peek() {
            if (size == 0) {
                return null;
            }
            return arr[start];
        }

        public void push(int obj) {
            if (size == arr.length) {
                throw new ArrayIndexOutOfBoundsException("This queue is full");
            }
            size++;
            arr[end] = obj;
            end = end == arr.length - 1 ? 0 : end + 1;
        }

        public Integer poll() {
            if (size == 0) {
                throw new ArrayIndexOutOfBoundsException("This queue is empty");
            }
            size--;
            int temp = start;
            start = start == arr.length - 1 ? 0 : start + 1;
            return arr[temp];
        }
    }

    public static void main(String[] args) {

    }
}

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

要求:

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

思路:

设计两个栈,一个dataStack,一个minStack。起初,第一个入dataStack的时候,minStack中null,也入,以后的newNum,dataStack正常入栈,minStack用栈顶元素num和newNum比较,如果num小于newNum,minStack重复入num,如果num 大于newNum,入新元素,即newNum。就要保证两个栈中元素个数一致。getMin的时候,minStack调用peek函数,只返回,不弹出。真正的弹出要调用dataStack的pop函数,这个时候minStack也要pop一个,只不过不return而已

代码:

/**
 * 实现一个特殊的栈,在实现栈的基础功能的基础上,再实现返回栈中最小元素的操作。
 */
public class GetMinStack {
    public static class MyStack1 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack1() {
            this.stackData = new Stack<>();
            this.stackMin = new Stack<>();
        }

        public void push(int newNum) {
            //同步更新
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum < this.getmin()) {//插入的数小于最小值,则将最小值插入栈
                this.stackMin.push(newNum);
            } else {//否则,找出最小值,插入栈
                int newMin = this.stackMin.peek();
                this.stackMin.push(newMin);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            //同步弹出
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty");
            }
            this.stackMin.pop();
            return this.stackData.pop();
        }

        public int getmin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty");
            }
            return this.stackMin.peek();
        }
    }

    public static class MyStack2 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack2() {
            this.stackData = new Stack<>();
            this.stackMin = new Stack<>();
        }

        public void push(int newNum) {
            if (this.stackMin.isEmpty()) {
                this.stackMin.push(newNum);
            } else if (newNum <= this.getMin()) {
                this.stackMin.push(newNum);
            }
            this.stackData.push(newNum);
        }

        public int pop() {
            if (this.stackData.isEmpty()) {
                throw new RuntimeException("Your stack is empty");
            }
            int value = this.stackData.pop();
            //当数据栈弹出值等于最小栈顶数的时候,弹出 
            if (value == this.getMin()) {
                this.stackMin.pop();
            }
            return value;
        }

        public int getMin() {
            if (this.stackMin.isEmpty()) {
                throw new RuntimeException("Your stack is empty");
            }
            return this.stackMin.peek();
        }
    }
}

题目三:如何仅用队列结构实现栈结构?如何仅用栈结构实现队列结构?

仅用队列实现栈

思路 :

一个队列只存放数据data,一个队列只用来辅助保持数据; 
比如1,2,3,4,5入了data队列,当要取出元素的时候,把1,2,3,4存到help里,这样队列data就只有一个5了~然后data.poll返回。然后在互换data和help的引用,使得原来有四个数据的help队列为data,新换完的help队列没内容。每次add数据只操作data队列,取出也是,只不过需要help辅助存储

代码:

/**
     * 使用队列结构实现栈结构
     */
    public static class TwoQueueStack {
        private Queue<Integer> queue;
        private Queue<Integer> help;

        public TwoQueueStack() {
            queue = new LinkedList<>();
            help = new LinkedList<>();
        }

        public void push(int pushInt) {
            queue.add(pushInt);
        }

        public int peek() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Queue is empty");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            help.add(res);
            swap();
            return res;
        }

        public int pop() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Queue is empty");
            }
            while (queue.size() > 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            swap();
            return res;
        }

        private void swap() {
            Queue<Integer> temp = help;
            help = queue;
            queue = temp;
        }
    }

仅用栈实现队列

思路:

用两个栈,一个是push栈,一个是pop栈,将数据压入push栈,要取数据时,将push栈中的数据全部倒入pop栈中,最后将栈顶数据弹出,且只有在pop栈为空时,才能倒,而且一次性倒完。

代码:

/**
     * 用栈结构实现队列结构
     */
    public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

        public TwoStacksQueue() {
            stackPush = new Stack<>();
            stackPop = new Stack<>();
        }

        public void push(int pushInt) {
            stackPush.push(pushInt);
            dao();
        }

        public int poll() {
            if (stackPush.isEmpty() && stackPop.isEmpty()) {
                throw new RuntimeException("empty");
            }
            dao();
            return stackPop.pop();
        }

        public int peek() {
            if (stackPush.isEmpty() && stackPop.isEmpty()) {
                throw new RuntimeException("empty");
            }
            dao();
            return stackPop.peek();
        }

        public void dao() {
            if (stackPop.isEmpty()) {//为空
                while (!stackPush.isEmpty()) {//一次倒完
                    stackPop.push(stackPush.pop());
                }
            }
        }
    }

转圈打印矩阵

题目:

给定一个整型矩阵matrix,请按照转圈的方式打印它。例如:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,打印结果为:1,2,3,4,5,12,16,15,14,13,9,5,6,7,11,10

要求:

额外空间复杂度为O(1)

 思路:

矩阵分圈处理。在矩阵中用左上角的坐标(tR,tC)和右下角的坐标(dR,dC)就可以表示一个矩阵,比如,题目中的矩阵,当(tR,tC)=(0,0),(dR,dC)=(3,3)时,表示的子矩阵就是整个矩阵,那么这个子矩阵的最外层的部分如下:

1          2          3          4

5                                  8

9                                 12

13       14        15        16

如果能把这个子矩阵的外层转圈打印出来,那么在(tR,tC)=(0,0)、(dR,dC)=(3,3)时,打印结果为:1,2,3,4,8,12,16,15,14,13,9,5。接下来令tR和tC加1,即(tR,tC)=(1,1),令dR和dC减1,即(dR,dC)=(2,2),此时表示的子矩阵如下:

6        7

10      11

再把这个子矩阵转圈打印出来,结果为:6,7,11,10。把tR和tC加1,即(tR,tC)=(2,2),令dR和dC减1,即(dR,dC)=(1,1)。如果发现左上角坐标跑到右下角坐标的右方或下方,整个过程就停止。已经打印的所有结果连接起来就是我们要求的打印结果。

代码:

/**
 * 转圈打印矩阵
 */
public class PrintMatrixSpiralOrder {
    public void spiralOrderPrint(int[][] matrix) {
        int tR = 0;
        int tC = 0;
        int dR = matrix.length - 1;
        int dC = matrix[0].length - 1;
        while (tR <= dR && tC <= dC) {
            printEdge(matrix, tR++, tC++, dR--, dC--);
        }
    }

    //转圈打印一个子矩阵的外层,左上角点(tR,tC),右下角点(dR,dC)
    private void printEdge(int[][] matrix, int tR, int tC, int dR, int dC) {
        if (tR == dR) {//子矩阵只有一行时
            for (int i = tC; i <= dC; i++) {
                System.out.print(matrix[tR][i] + " ");
            }
        } else if (tC == dC) {//子矩阵只有一列时
            for (int i = tR; i <= dR; i++) {
                System.out.print(matrix[i][tC] + " ");
            }
        } else {//一般情况
            int curCol = tC;
            int curRow = tR;
            while (curCol != dC) {//从左向右
                System.out.print(matrix[tR][curCol] + " ");
                curCol++;
            }
            while (curRow != dR) {//从上到下
                System.out.print(matrix[curRow][dC] + " ");
                curRow++;
            }
            while (curCol != tC) {//从右到左
                System.out.print(matrix[dR][curCol] + " ");
                curCol--;
            }
            while (curRow != tR) {//从下到上
                System.out.print(matrix[curRow][tC] + " ");
                curRow--;
            }
        }
    }
}

将正方形矩阵顺时针转动90°

题目:

给定一个N×N的矩阵matrix,把这个矩阵调整成顺时针转动90°后的形式。

例如:

1        2        3        4

5        6        7        8

9        10      11      12

13      14      15      16

顺时针转动90°后为:

13      9       5      1

14      10     6      2

15      11     7      3

16      12     8      4

要求:

额外空间复杂度为O(1).

思路:

仍然使用分圈处理的方式,

在矩阵中用左上角的坐标(tR,tC)和右下角的坐标(dR,dC)就可以表示一个矩阵,比如,题目中的矩阵,当(tR,tC)=(0,0),(dR,dC)=(3,3)时,表示的子矩阵就是整个矩阵,那么这个子矩阵的最外层的部分如下:

1          2          3          4

5                                  8

9                                 12

13       14        15        16

在这个外圈中,1,4,16,13为一组,然后让1占据4的位置,4占据16的位置,16占据13的位置,13占据1的位置,一组就调整完了。然后2,8,15,9为一组,继续占据调整的过程,最后3,12,14,5为一组,继续占据调整的过程。然后(tR,tC)=(0,0)、(dR,dC)=(3,3)的子矩阵外层就调整完毕,接下来令tR和tC加1,即(tR,tC)=(1,1),令dR和dC减1,即(dR,dC)=(2,2),此时表示的子矩阵如下:

6       7

10     11

这个外层只有一组,就是6,7,11,10,占据调整之后即可。所以,如果子矩阵的大小是M×M,一共就有M-1组,分别进行占据调整即可。

代码:

/**
 * 将正方形矩阵顺时针旋转90°
 */
public class RotateMatrix {
    public static void rotate(int[][] matrix) {
        int tR = 0;
        int tC = 0;
        int dR = matrix.length - 1;
        int dC = matrix[0].length - 1;
        while (tR < dR) {
            rotateEdge(matrix, tR++, tC++, dR--, dC--);
        }
    }

    private static void rotateEdge(int[][] matrix, int tR, int tC, int dR, int dC) {
        int times = dC - tC;//times就是总的组数
        int temp = 0;
        for (int i = 0; i != times; i++) {//一次循环就是一组占据调整
            temp = matrix[tR][tC + i];
            matrix[tR][tC + i] = matrix[dR - i][tC];
            matrix[dR - i][tC] = matrix[dR][dC - i];
            matrix[dR][dC - i] = matrix[tR - i][dC];
            matrix[tR - i][dC] = temp;
        }
    }
}

“之”字形打印矩阵

题目:

给定一个矩阵matrix,按照“之”字形的方式打印矩阵,例如:

1        2        3        4

5        6        7        8

9        10      11      12

“之”字形打印的结果为:1,2,5,9,6,3,4,7,10,11,8,12

要求:

额外空间复杂度为O(1)

思路;

  1. 上坐标(tR,tC)初始化为(0,0),先沿着矩阵第一行移动(tC++),当到达第一行最右边的元素后,在沿着矩阵最后一列移动(tR++)。
  2. 下坐标(dR,dC)初始为(0,0),先沿着矩阵第一列移动(dR++),当到达第一列最下边的元素时,再沿着矩阵最后一行移动(dC++)。
  3. 上坐标与下坐标同步移动,每次移动后的上坐标与下坐标的连线就是矩阵中的一条斜线,打印斜线上的元素即可。
  4. 如果上次斜线是从左下向右上打印的,这次一定是从右上向左下打印,反之亦然。总之,可以把打印的方向用boolean值表示,每次取反即可。

代码:

/**
 * 之字形打印矩阵
 */
public class PrintMatrixZigZag {
    public static void printMatrixZigZag(int[][] matrix) {
        int tR = 0;
        int tC = 0;
        int dR = 0;
        int dC = 0;
        int endRow = matrix.length - 1;
        int endCol = matrix[0].length - 1;
        boolean fromUp = false;
        while (tR != endRow + 1) {
            printLevel(matrix, tR, tC, dR, dC, fromUp);
            tR = tC == endCol ? tR + 1 : tR;
            tC = tC == endCol ? tC : tC + 1;
            dR = dR == endRow ? dR : dR + 1;
            dC = dR == endRow ? dC + 1 : dC;
            fromUp = !fromUp;
        }
        System.out.println();
    }

    private static void printLevel(int[][] matrix, int tR, int tC, int dR, int dC, boolean fromUp) {
        if (fromUp) {
            while (tR != dR + 1) {//从左下到右上
                System.out.print(matrix[tR++][tC--] + " ");
            }
        } else {
            while (dR != tR - 1) {//从右上到左下
                System.out.print(matrix[dR--][dC++] + " ");
            }
        }
    }
}

在行列都排好序的矩阵中找数

题目:

给定一个有N×M的整型矩阵matrix和一个整数K,matrix的每一行和每一列都是排好序的。实现一个函数,判断K是否在matrix中。

例如:

0       1       2       5

2       3       4       7

4       4       4       8

5       7       7       9

如果K为7,返回true,如果K为6,返回false

要求:

时间复杂度为O(N+M),额外空间复杂度为O(1)。

思路:

1.从矩阵最右上角的数开始寻找(row=0,col=M-1)。

2.比较当前数matrix[row][col]与K的关系:

  • 如果与K相等,说明已找到,直接返回true

  • 如果比K大,因为矩阵每一列都已排好序,所以在当前数所在的列中,处于当前数下方的数都会比K大,则没有必要继续在第col列上寻找,令col=col-1,重复步骤2.

  • 如果比K小,因为矩阵每一行都已排好序,所以在当前数所在的行中,处于当前数左方的数都会比K小,则没有必要继续在第row行上寻找,令row=row+1,重复步骤2.

3.如果找到越界都没有发现与K相等的数,则返回false。

或者可以从矩阵的最左下角的数开始寻找(row=N-1,col=0),具体过程类似。

代码:

/**
 * 在行列都排好序的矩阵中找数
 */
public class IsContains {
    public boolean isContains(int[][] matrix, int K) {
        int row = 0;
        int col = matrix[0].length - 1;
        while (row < matrix.length && col > -1) {
            if (matrix[row][col] == K) {
                return true;
            } else if (matrix[row][col] > K) {
                col--;
            } else {
                row++;
            }
        }
        return false;
    }
}

猜你喜欢

转载自blog.csdn.net/ARPOSPF/article/details/81903372