栈、队列、矩阵、链表问题(一)

目录

  • 用数组结构实现大小固定的队列和栈
  • 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
  • 如何仅用队列结构实现栈结构?
  • 如何仅用栈结构实现队列结构?
  • 猫狗队列
  • 转圈打印矩阵
  • 旋转正方形矩阵
  • 反转单向和双向链表

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

栈的实现:引入一个变量size,表示当前栈结构里对象的个数(size会一直指向栈顶的上面一个下标,例如栈顶对象在数组中下标为5,那么size指向下标6)。栈结构压入一个对象,size+1;栈结构弹出一个对象,size-1。

public static class ArrayStack{

        Integer size;
        Integer[] arr;

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

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

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

        public Integer pop(){
            if(size == 0){
                throw new ArrayIndexOutOfBoundsException("The queue is empty");
            }
            return arr[--size];
        }
    }

队列的实现:引入变量size,表示当前队列中对象的个数;引入变量start,表示队列头部的数组下标,随着弹出队列操作进行变化(即当队列头部弹出一个对象后,start+1);引入变量end,表示队列尾部的数组下标,随着加入队列操作进行变化(即当队列尾部加入一个对象后,end+1);当start或者end超出数组下标时,start或者end等于0,又从数组0位置开始计算,一直循环。

public static class ArrayQueue{

        private Integer size;
        private Integer start;
        private Integer end;
        private Integer[] arr;

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

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

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

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

    }

实现一个特殊的栈

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

【要求】

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

思路:
getMin操作如果是遍历整个栈,那么时间复杂度就变成O(N)了,所以需要另外建立一个只存放最小数的栈,最小数栈的个数与实际的栈同步,当实际栈加入一个数,最小数栈就加入当前栈中的最小数,出栈操作同理。

    Stack<Integer> stackData = new Stack<>();
    Stack<Integer> stackMin = new Stack<>();

    public void push(int obj){
        if(stackMin.isEmpty()){
            stackMin.push(obj);
        }else if(stackMin.peek() >= obj){
            stackMin.push(obj);
        }else{
            int num = stackMin.peek();
            stackMin.push(num);
        }
        stackData.push(obj);
    }

    public Integer pop(){
        if (stackData.isEmpty()) {
            throw new RuntimeException("Your stack is empty.");
        }
        stackMin.pop();
        return stackData.pop();
    }

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

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

思路:建立两个队列queue和help,当peek或者pop时,将queue中除末尾以外的全部对象倒入help里面,返回末尾对象,然后交换两个队列的指针。

public static class TwoQueuesStack {

        private Queue<Integer> queue;
        private Queue<Integer> help;

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

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

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

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

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

    }

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

思路:建立两个栈结构stackPush和stackPop,stackPush负责加入队列操作,stackPop负责弹出队列操作。当stackPop为空时,将stackPush中的数据倒入stackPop中。倒入操作需要满足两点:

  1. 一次倒入操作必须把stackPush中的数据倒完;
  2. 必须在stackPop为空时才能倒入数据。
public static class TwoStacksQueue {

        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

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

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

        public int peek(){
            if(stackPush.isEmpty() && stackPop.isEmpty()){
                throw new RuntimeException("Queue is empty!");
            }else if(stackPop.isEmpty()){
                while (!stackPush.isEmpty()){
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }

        public int pop(){
            if(stackPush.isEmpty() && stackPop.isEmpty()){
                throw new RuntimeException("Queue is empty!");
            }else if(stackPop.isEmpty()){
                while (!stackPush.isEmpty()){
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }

    }

猫狗队列

已知有宠物、狗、猫类如下,实现一种猫狗队列的结构,要求实现add、pollAll、pollDog、pollCat、isEmpty、isDogEmpty、isCatEmpty等方法。

class Pet {
    private String type;
    public Pet(String type) {
        this.type = type;
    }
    public String getType() {
        return this.type;
    }
}
 
class Dog extends Pet {
    public Dog() {
        super("Dog");
    }
}
 
class Cat extends Pet {
    public Cat() {
        super("Cat");
    }
}

分析:本题考查实现特殊数据结构及针对特殊功能的算法设计能力。创建一个猫队列和一个狗队列,可以解决大部分的需求。

但是由于在pollAll方法中,需要判断最先加入的猫和最先加入的狗的顺序,所以需要在每个宠物加入时加一个类似时间戳的标记。

我们创建一个PetEnterQueue类,用它来替代Pet类,count就代表加入的顺序。

    public static class PetEnterQueue {
        private Pet pet;
        private long count;

        public PetEnterQueue(Pet pet, long count) {
            this.pet = pet;
            this.count = count;
        }

        public Pet getPet() {
            return this.pet;
        }

        public long getCount() {
            return this.count;
        }

        public String getEnterPetType() {
            return this.pet.getPetType();
        }
    }

    public static class DogCatQueue {
        private Queue<PetEnterQueue> dogQ;
        private Queue<PetEnterQueue> catQ;
        private long count;

        public DogCatQueue() {
            this.dogQ = new LinkedList<PetEnterQueue>();
            this.catQ = new LinkedList<PetEnterQueue>();
            this.count = 0;
        }

        public void add(Pet pet) {
            if (pet.getPetType().equals("dog")) {
                this.dogQ.add(new PetEnterQueue(pet, this.count++));
            } else if (pet.getPetType().equals("cat")) {
                this.catQ.add(new PetEnterQueue(pet, this.count++));
            } else {
                throw new RuntimeException("err, not dog or cat");
            }
        }

        public Pet pollAll() {
            if (!this.dogQ.isEmpty() && !this.catQ.isEmpty()) {
                if (this.dogQ.peek().getCount() < this.catQ.peek().getCount()) {
                    return this.dogQ.poll().getPet();
                } else {
                    return this.catQ.poll().getPet();
                }
            } else if (!this.dogQ.isEmpty()) {
                return this.dogQ.poll().getPet();
            } else if (!this.catQ.isEmpty()) {
                return this.catQ.poll().getPet();
            } else {
                throw new RuntimeException("err, queue is empty!");
            }
        }

        public Dog pollDog() {
            if (!this.isDogQueueEmpty()) {
                return (Dog) this.dogQ.poll().getPet();
            } else {
                throw new RuntimeException("Dog queue is empty!");
            }
        }

        public Cat pollCat() {
            if (!this.isCatQueueEmpty()) {
                return (Cat) this.catQ.poll().getPet();
            } else
                throw new RuntimeException("Cat queue is empty!");
        }

        public boolean isEmpty() {
            return this.dogQ.isEmpty() && this.catQ.isEmpty();
        }

        public boolean isDogQueueEmpty() {
            return this.dogQ.isEmpty();
        }

        public boolean isCatQueueEmpty() {
            return this.catQ.isEmpty();
        }

    }

转圈打印矩阵

给定一个整型矩阵matrix,请按照转圈的方式打印它。

思路:转圈打印其实可以理解为,将矩阵分为从外到内一圈一圈的矩形,每次只打印一个矩形,那么只需要找到每个矩形的上下左右四个边界就可以得到这个矩形。

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

        int top = 0;
        int left = 0;
        int bottom = matrix.length - 1;
        int right = matrix[0].length - 1;

        while (top <= bottom && left <= right){
            printEdge(matrix, top++, left++, bottom--, right--);
        }

    }

    public static void printEdge(int[][] matrix, int top, int left, int bottom, int right){

        if(top == bottom){
            for(int i = left; i <= right; i++){
                System.out.print(matrix[top][i] + " ");
            }
        }else if(left == right){
            for(int i = top; i <= bottom; i++){
                System.out.print(matrix[i][top] + " ");
            }
        }else{
            int r = left;
            int b = top;
            while (r != right){
                System.out.print(matrix[top][r] + " ");
                r++;
            }
            while (b != bottom){
                System.out.print(matrix[b][right] + " ");
                b++;
            }
            while (r != left){
                System.out.print(matrix[bottom][r] + " ");
                r--;
            }
            while (b != top){
                System.out.print(matrix[b][left] + " ");
                b--;
            }
        }
    }

旋转正方形矩阵

给定一个整型正方形矩阵matrix,请把该矩阵调整成顺时针旋转90度的样子。

思路:与上题一样将整个矩阵视为一圈一圈的矩形,由外到内依次旋转。

public static void rotate(int[][] matrix) {
        int top = 0;
        int left = 0;
        int bottom = matrix.length - 1;
        int right = matrix[0].length - 1;

        while (top < bottom) {
            rotateEdge(matrix, top++, left++, bottom--, right--);
        }
    }

    public static void rotateEdge(int[][] matrix, int top, int left, int bottom, int right) {

        int times = right - left;
        int tmp = 0;
        for(int i = 0; i < times; i++){
            tmp = matrix[top][left+i];
            matrix[top][left+i] = matrix[bottom-i][left];
            matrix[bottom-i][left] = matrix[bottom][right-i];
            matrix[bottom][right - i] = matrix[top+i][right];
            matrix[top+i][right] = tmp;
        }

    }

反转单向和双向链表

    private class Node{
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }

    private static Node reverseList(Node head){

        Node pre = null;    //上一个节点
        Node next = null;   //下一个节点

        if(head != null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    public static class DoubleNode {
        public int value;
        public DoubleNode last;
        public DoubleNode next;

        public DoubleNode(int data) {
            this.value = data;
        }
    }

    private static DoubleNode reverseList(DoubleNode head){
        
        DoubleNode pre = null;
        DoubleNode next = null;

        if(head != null){
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head = next;
        }
        return pre;
    }

参考资料:牛客网左程云初级算法教程

    原文作者:管弦_
    原文地址: https://www.jianshu.com/p/53ed4e1b0e8c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞