栈,队列,矩阵相关基础题目及答案

栈,队列

题目一

用数组结构实现大小固定的队列和栈
要求:
自己实现的栈应该包括 peek,push,pop方法
自己实现的队列应该包括 peek,enqueue,dequeue方法

实现的栈代码如下:

public class ArrayStack {

    private Integer[] data;
    private Integer size;

    public ArrayStack(int initSize){
        if(initSize <= 0)
            throw new IllegalArgumentException("栈的初始长度至少应为 1 ");

        this.size = 0;
        this.data = new Integer[initSize];
    }

    // push
    public void push(int num){
       if(size == data.length)
           throw new ArrayIndexOutOfBoundsException("栈已满");
       this.data[size++] = num;
    }

    // pop
    public Integer pop(){
        if(size == 0)
            throw new ArrayIndexOutOfBoundsException("栈已空");
        return data[--size];
    }

    // peek
    public Integer peek(){
        if(size == 0)
            return null;

        return data[size - 1];

    }
}

实现的队列代码如下:

public class ArrayQueue {

    private Integer[] data;
    private Integer front;
    private Integer tail;
    private Integer size;

    public ArrayQueue(int initSize){
        if(initSize <= 0)
            throw new IllegalArgumentException("队列的初始长度应至少为 1 ");

        this.front = 0;
        this.tail = 0;
        this.size = 0;
        this.data = new Integer[initSize];
    }

    // enqueue
    public void enqueue(int num){
        if(size == data.length)
            throw new ArrayIndexOutOfBoundsException("队列已满");

        data[tail] = num;
        tail = tail == data.length-1 ? 0 : tail+1;
        size++;
    }

    // dequeue
    public Integer dequeue(){
        if(size == 0)
            throw new ArrayIndexOutOfBoundsException("队列已空");

        Integer res = data[front];
        front = front == data.length-1 ? 0 : front+1;
        size--;
        return res;
    }

    // peek
    public Integer peek(){
        if(size == 0)
            return null;

        return data[front];
    }
}

题目二

实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返 回栈中最小元素的操作。
要求:
自己实现的栈应具备 pop,push,peek,getMin方法
pop、push、getMin操作的时间复杂度都是O(1)
设计的栈类型可以使用现成的栈结构

思路:如果可以复用现成的栈,可以通过两个栈来实现这样的数据结构。一个栈是data栈,一个栈是minData栈,data栈正常push或pop数据,minData栈随着data栈一起push或pop数据,不过push操作时压入的是当前最小的那个数字。
代码如下:

import java.util.Stack;

public class GetMinStack {

    private Stack<Integer> data;
    private Stack<Integer> minData;

    public GetMinStack(){
        this.data = new Stack<>();
        this.minData = new Stack<>();
    }

    // push
    public void push(int num){
        data.push(num);
        if(minData.isEmpty())
            minData.push(num);
        else{
            minData.push(minData.peek() < num ? minData.peek():num);
        }
    }

    // pop
    public Integer pop(){
        if(data.isEmpty())
            throw new RuntimeException("栈已空");

        minData.pop();
        return data.pop();
    }

    // peek
    public Integer peek(){
        return data.peek();
    }

    // getMin
    public Integer gerMin(){
        if(minData.isEmpty())
            throw new RuntimeException("栈已空");

        return minData.peek();
    }
}

题目三

如何仅用队列结构实现栈结构?
如何仅用栈结构实现队列结构?

思路:
使用队列实现栈结构
可以用两个队列来实现栈这种结构,当向自己设计的栈结构push数据的时候,其中一个队列正常进行入队操作。当需要我们pop数据的时候,将有数据的那个队列除去队末的那个数字都enqueue(入队)到另一个队列中,并且让指向两个队列的引用交换。代码如下:

import java.util.LinkedList;
import java.util.Queue;

public class TwoQueuesStack {

    private Queue<Integer> dataQueue;
    private Queue<Integer> helpQueue;

    public TwoQueuesStack(){
        this.dataQueue = new LinkedList<>();
        this.helpQueue = new LinkedList<>();
    }

    // push
    public void push(int num){
        dataQueue.add(num);
    }

    // pop
    public Integer pop(){
        if(dataQueue.isEmpty())
            throw new RuntimeException("栈已空");

        while(dataQueue.size() != 1){
            helpQueue.add(dataQueue.poll());
        }
        Integer res = dataQueue.poll();
        Queue<Integer> temp = dataQueue;
        dataQueue = helpQueue;
        helpQueue = temp;
        return res;
    }

    // peek
    public Integer peek(){
        if(dataQueue.isEmpty())
            return null;

        while(dataQueue.size() != 1){
            helpQueue.add(dataQueue.poll());
        }
        Integer res = dataQueue.poll();
        Queue<Integer> temp = dataQueue;
        dataQueue = helpQueue;
        helpQueue = temp;
        dataQueue.add(res);
        return res;
    }
}

使用栈实现队列结构
可以使用两个栈来实现一个队列结构,其中一个栈为dataStack,另一个为helpStack。当队列发生进队操作时,向dataStack里push数据,当队列发生出队操作时,如果helpStack为空,就将dataStack依次pop出的数据push进helpStack中,然后返回helpStack pop的数据,如果helpStack不为空,就直接返回helpStack pop的数据,如果没读懂,可以画一个草图感受一下;代码如下:

import java.util.Stack;

public class TwoStacksQueue {

    private Stack<Integer> dataStack;
    private Stack<Integer> helpStack;

    public TwoStacksQueue(){
        this.dataStack = new Stack<>();
        this.helpStack = new Stack<>();
    }

    // enqueue
    public void enqueue(int num){
        dataStack.push(num);
    }

    // dequeue
    public Integer dequeue(){
        if(dataStack.isEmpty() && helpStack.isEmpty())
            throw new RuntimeException("队列已空");

        if(helpStack.isEmpty()){
            while (!dataStack.isEmpty()){
                helpStack.push(dataStack.pop());
            }
        }
        return helpStack.pop();
    }

    // peek
    public Integer peek(){
        if(helpStack.isEmpty() && dataStack.isEmpty())
            return null;
        if(helpStack.isEmpty()){
            while (!dataStack.isEmpty()){
                helpStack.push(dataStack.pop());
            }
        }
        return helpStack.peek();
    }
}

题目四

实现一种狗猫队列的结构,现给出宠物,猫和狗的类型如下:
    public class Pet { 
        private String type; 
        public Pet(String type) { 
            this.type = type; 
        } 
        public String getPetType() { 
            return this.type; 
        } 
    }
    public class Dog extends Pet { 
        public Dog() {
            super("dog"); 
        } 
    } 
    public class Cat extends Pet {
        public Cat() { 
            super("cat"); 
        } 
    }
要求如下:
 * 用户可以调用add方法将cat类或dog类的 实例放入队列中;
 * 用户可以调用pollAll方法,将队列中所有的实例按照进队列 的先后顺序依次弹出;
 * 用户可以调用pollDog方法,将队列中dog类的实例按照 进队列的先后顺序依次弹出;
 * 用户可以调用pollCat方法,将队列中cat类的实 例按照进队列的先后顺序依次弹出;
 * 用户可以调用isEmpty方法,检查队列中是 否还有dog或cat的实例;
 * 用户可以调用isDogEmpty方法,检查队列中是否有dog 类的实例;
 * 用户可以调用isCatEmpty方法,检查队列中是否有cat类的实例。

这道题的思路是:在封装好的类的基础上再封装一个EnterQueuePet类,这个类中有一个记录入队编号的变量,pollCat和pollDog方法很简单,pollAll方法则需要判断猫队列和狗队列队首的编号,哪个编号小,就弹出哪个。具体的代码如下:

import java.util.LinkedList;
import java.util.Queue;

public class DogCatQueuePromblem {

    public static class Pet {
        private String type;
        public Pet(String type) {
            this.type = type;
        }
        public String getPetType() {
            return this.type;
        }
    }

    public static class Dog extends Pet {
        public Dog() {
            super("dog");
        }
    }

    public static class Cat extends Pet {
        public Cat() {
            super("cat");
        }
    }

    public static class EnterQueuePet{

        private Pet pet;
        // 编号
        private int number;

        public EnterQueuePet(Pet pet,int number){
            this.pet = pet;
            this.number = number;
        }

        public int getNumber(){
            return this.number;
        }
        public Pet getPet(){
            return this.pet;
        }
        public String getPetType(){
            return this.pet.getPetType();
        }
    }

    public static class DogCatQueue{
        private Queue<EnterQueuePet> dogQueue;
        private Queue<EnterQueuePet> catQueue;
        private int number;

        public DogCatQueue(){
            this.dogQueue = new LinkedList<>();
            this.catQueue = new LinkedList<>();
            this.number = 0;
        }

        public void add(Pet pet){
            if(!pet.getPetType().equals("cat") && !pet.getPetType().equals("dog"))
                throw new IllegalArgumentException("只能放入猫或者狗");

            if(pet.getPetType().equals("cat")){
                catQueue.add(new EnterQueuePet(pet,number++));
            }else{
                dogQueue.add(new EnterQueuePet(pet,number++));
            }
        }

        public Pet pollAll(){
            if(dogQueue.isEmpty() && catQueue.isEmpty())
                throw new RuntimeException("猫狗队列均为空");
            
            if(catQueue.isEmpty())
                return dogQueue.poll().getPet();
            
            if(dogQueue.isEmpty())
                return catQueue.poll().getPet();
            
            return dogQueue.peek().getNumber() < catQueue.peek().getNumber() ? dogQueue.poll().getPet() : catQueue.poll().getPet();
        }

        public Dog pollDog(){
            if(dogQueue.isEmpty())
                throw new RuntimeException("狗队列为空");

            return (Dog)dogQueue.poll().getPet();
        }

        public Cat pollCat(){
            if(catQueue.isEmpty())
                throw new RuntimeException("猫队列为空");

            return (Cat)catQueue.poll().getPet();
        }

        public boolean isEmpty(){
            return catQueue.isEmpty() && dogQueue.isEmpty();
        }

        public boolean isDogEmpty(){
            return dogQueue.isEmpty();
        }

        public boolean isCatEmpty(){
            return catQueue.isEmpty();
        }
    }
}

矩阵

题目一

在行列都排好序的矩阵中找数
给定一个有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)

解题思路:
对于已排好序矩阵找数,可以从矩阵的右上角或者左下角出发,索引从矩阵右上角出发的话,如果K与索引数字比较,K更大,那么说明K在当前矩阵索引的那个数的下面,如果更小说明K在当前矩阵索引的那个数的左边;索引从矩阵左下角出发的话,如果K与索引数字比较,K更大,那么说明K在当前矩阵索引的那个数的右边,如果更小 ,那么说明K在当前矩阵索引的那个数的上面。代码如下,思路为从矩阵的左下角开始找K:

public class FindNumInSortedMatrix {

    public static boolean isNumInSortedMatrix(int[][] matrix,int K){
        int indexRow = matrix.length-1;
        int indexColumn = 0;

        while(indexRow >= 0 && indexColumn <= matrix[0].length - 1){
            
           if(K > matrix[indexRow][indexColumn]){
               indexColumn++;
           }else if(K < matrix[indexRow][indexColumn]){
               indexRow--;
           }else{
               return true;
           }
        }
        return false;
    }
}

题目二

转圈打印矩阵
给定一个整型矩阵matrix,请按照顺时针转圈的方式打印它
例如:
1   2  3  4
5   6  7  8
9  10 11 12
13 14 15 16
打印结果为:
1,2,3,4,8,12,16,15,14,13,9, 5,6,7,11, 10
要求:
额外空间复杂度为O(1)

解题思路:
将顺时针转圈打印转换成一圈一圈地打印,只要知道矩阵左上角的点和矩阵右下角的点就可以将打印出一圈。打印的顺序如图:


image.png

接下来,左上角的点和右下角的点横纵坐标减一,再继续打印内圈的部分,这样就和顺时针转圈打印的效果是一样的。重点是需要考虑好几种情况:

 1  2  3 
 4  5  6
 7  8  9
10 11 12
=========
1 2 3 4
5 6 7 8
2 2 3 4

当矩阵如上,打印外层后,内层为一个竖棒或者横棒,这种情况需要另作考虑
代码如下:

public class PrintMatrixSpiralOrder {

    public static void printMatrixSpiralOrder(int[][] matrix){
        int startX = 0;
        int startY = 0;
        int endX = matrix.length - 1;
        int endY = matrix[0].length - 1;

        while(startX <= endX && startY <= endY){
            printOuterCircle(matrix,startX++,startY++,endX--,endY--);
        }
    }

    public static void printOuterCircle(int[][] matrix,int startX,int startY,int endX,int endY){
        int indexX = startX;
        int indexY = startY;

        if(startX == endX){
            while(indexY <= endY){
                System.out.print(matrix[startX][indexY++] + " ");
            }
            return;
        }else if(startY == endY){
            while(indexX <= endX){
                System.out.print(matrix[indexX++][startY] + " ");
            }
            return;
        }else{

            while(indexY < endY)
                System.out.print(matrix[indexX][indexY++] + " ");

            while(indexX < endX)
                System.out.print(matrix[indexX++][indexY] + " ");

            while(indexY > startY)
                System.out.print(matrix[indexX][indexY--] + " ");

            while(indexX > startX)
                System.out.print(matrix[indexX--][indexY] + " ");

            return;
        }
    }
}

题目三

旋转正方形矩阵
给定一个整型正方形矩阵matrix,请把该矩阵调整成 顺时针旋转90度的样子
要求
额外空间复杂度为O(1)

解题思路:
同上一题的解题思路一样,先将外层顺时针旋转,然后逐步向内。代码如下:

public class RotateMatrix {

    public static void rotateMatrix(int[][] matrix){
        int startX = 0;
        int startY = 0;
        int endX = matrix.length - 1;
        int endY = matrix[0].length - 1;

        while(startX < endX && startY < endY){
            rotateOuter(matrix,startX++,startY++,endX--,endY--);
        }
    }

    public static void rotateOuter(int[][] matrix,int startX,int startY,int endX,int endY){

        int i = 0;
        int temp = 0;
        while(i < endY - startY){
            temp = matrix[startX][startY + i];
            matrix[startX][startY + i] = matrix[endX - i][startY];
            matrix[endX - i][startY] = matrix[endX][endY - i];
            matrix[endX][endY - i] = matrix[startX + i][endY];
            matrix[startX + i][endY] = temp;
            i++;
        }
    }
}

题目四

“之”字形打印矩阵
给定一个矩阵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)

之字形打印的示意图如下:


image.png

本题的思路:
设定两个点,这两个点起始的位置均为(0,0),A点先从右走,走到头后向下走,B点从下走,走到头后从右走,之字形打印其实就是打印 在A点和B点相遇之前,A->B或者B->A的所有数字,至于是A->B还是B->A只需要增加一个布尔变量来进行判断即可。代码如下所示:

public class ZigZagPrintMatrix {

    public static void printZigZagMatrix(int[][] matrix){

        int endX = matrix.length - 1;
        int endY = matrix[0].length - 1;

        int aX = 0;
        int aY = 0;
        int bX = 0;
        int bY = 0;

        boolean turn = false;

        while(aX <= endX && aY <= endY){
            printStartToEnd(matrix,aX,aY,bX,bY,turn);

            turn = !turn;
            
            //  顺序不能变
            aX = aY == endY ? aX+1 : aX;
            aY = aY < endY ? aY+1 : aY;
            bY = bX == endX ? bY+1 : bY;
            bX = bX < endX ? bX+1 : bX;
        }
    }

    public static void printStartToEnd(int[][] matrix,int aX,int aY,int bX,int bY,boolean turn){

        if(turn){
            while(aX <= bX && aY >= bY){
                System.out.print(matrix[aX++][aY--] + " ");
            }
        }else{
            while(bX >= aX && bY <= aY){
                System.out.print(matrix[bX--][bY++] + " ");
            }
        }
    }
}

本题中注释处,代码实际上为if语句,不过以三目运算的方式写出来,需要注意的是代码的顺序是不能变的,如果对于代码:

aX = aY == endY ? aX+1 : aX;
aY = aY < endY ? aY+1 : aY;

写成了:

aY = aY < endY ? aY+1 : aY;
aX = aY == endY ? aX+1 : aX;

那么就会少打印一行,原因在于,如果先对aY进行判断,那么aY在等于endY的情况下,就会直接让aX = aX+1,那么存在(0,endY)的那一行就不会打印出来。请用心体会。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容