【搞定左神算法初级班】第3节:栈、队列、链表、矩阵结构及相关常见面试题

这篇博客主要讲解了如何使用栈、队列、链表和矩阵结构来解决一系列算法问题。对于栈,探讨了如何在固定数组中实现栈和队列以及实现能返回最小元素的栈。队列的部分,讨论了如何用栈实现队列。链表部分涵盖了反转链表、判断回文链表以及荷兰国旗问题。最后,讲解了矩阵的打印和旋转问题,包括顺时针打印和旋转90度。所有问题都提供了思路分析,适合算法初学者练习。
摘要由CSDN通过智能技术生成

目  录:

一、栈

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

题目2:能返回栈中最小元素的栈

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

二、队列

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

题目2:猫狗队列

三、链表

题目1:反转单向和双向链表

题目2:给出两个有序链表的头结点,打印出两个链表中相同的元素

题目3:判断一个链表是否为回文结构

题目4:将单向链表按某值划分成左边小、中间相等、右边大的形式【荷兰国旗问题】

题目5:复制含有随机指针节点的链表

题目6:两个单链表相交的一系列问题【重点:很有意思】

四、矩阵的打印和旋转

题目1:顺时针打印矩阵

题目2:将一个正方形顺时针旋转90度

题目3:之字形打印矩阵

题目4:在一个行和列都有序的m行n列的矩阵中查找一个数是否存在


一、栈

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

  • 固定大小的数组实现栈结构
package com.offer.class3;

/**
 * 固定数组实现栈结构
 */
public class StackWithArray {

    private int[] arr;
    private int index;   // 指向即将放入元素的位置

    public StackWithArray(int initialSize){
        if(initialSize < 0){
            throw new IllegalArgumentException("the init size is less than 0");
        }
        arr = new int[initialSize];
        index = 0;
    }

    // 压栈
    public void push(int obj){
        if(index == arr.length){
            throw new ArrayIndexOutOfBoundsException("the stack is full!");
        }
        arr[index++] = obj;   // index指向的就是当前要存储数据的位置
    }

    // 弹栈(删除元素)
    public int pop(){
        if(index == 0){
            throw new ArrayIndexOutOfBoundsException("the stack is empty!");
        }
        return arr[--index];  // 删除的是index指向的前一个元素,因为index指向的是位置为空
    }

    // 弹出元素,但不删除
    public int peek(){
        if(index == 0){
            throw new ArrayIndexOutOfBoundsException("the stack is empty!");
        }
        return arr[index - 1];  // index并没有减小,所以index位置上的元素并没有删除
    }
}
  • 固定大小的数组实现队列

注意:start、end、size 这三个变量的实际意义,size 变量实现了对 start 和 end 变量之间的解耦。

package com.offer.class3;

/**
 * 固定数组实现队列
 * 三个变量:start、end、size
 */
public class QueueWithArray {

    private int start;  // 指向队头,每次要取数据的位置
    private int end;    // 指向队尾,每次要添加数据的位置
    private int size;   // 队列中元素的个数,利用size实现start和end之间的解耦
    private int[] arr;

    public QueueWithArray(int initialSize){
        if(initialSize < 0){
            throw new IllegalArgumentException("the initialSzie is less than 0");
        }
        arr = new int[initialSize];
        start = 0;
        end = 0;
        size = 0;
    }

    // 添加一个元素
    public void push(int obj){
        if(size == arr.length){
            throw new ArrayIndexOutOfBoundsException("the queue is full");
        }
        size++;
        arr[end] = obj;
        // 如果end指向数组中最后一个元素的位置,那么需要跳到开始的位置,从头开始
        end = (end == arr.length - 1) ? 0 : end + 1;
    }

    public int poll(){
        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];
    }

}

题目2:能返回栈中最小元素的栈

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

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

思路:用一个额外的栈存储最小元素。 

package com.offer.class3;

import java.util.Stack;

/**
 * 用一个额外的栈空间维持一个最小元素
 */
public class MyStack {

    private Stack<Integer> dataStack;
    private Stack<Integer> minStack;

    public MyStack(){
        dataStack = new Stack<Integer>();
        minStack = new Stack<Integer>();
    }

    public void push(int obj){
        dataStack.push(obj);
        if(minStack.isEmpty()){
            minStack.push(obj);    // 当最小值栈为空时,直接将数存进去
        }else if(obj <= minStack.peek()){
            minStack.push(obj);    // 当obj小于等于最小值栈中的最小值时,直接压入栈中
        }else{
            minStack.push(minStack.peek());  // 将最小值栈中的最小值再压入一遍
        }
    }

    public int pop(){
        minStack.pop();
        return dataStack.pop();
    }

    public int getMin(){
        if(minStack.isEmpty()){
            throw new ArrayIndexOutOfBoundsException("the stack is empty!");
        }
        return minStack.peek();
    }
}

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

  • 原理:可以用两个队列(queue、help)来实现栈,加元素时加总是在queue;删除元素时,把 queue 最后一位前的元素全部弹出放入 help 队列中,然后再弹出返回 queue 的最后一位元素(这就达成栈后入先出的要求了),然后交换 help 和queue 指针即可
  • 队列:poll(移除并返回队列的头部),add(添加一个元素到队列尾部),peek(返回队列的头部,不删除)
package com.offer.class3;

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

/**
 * 用两个栈实现队列
 */
public class TwoQueueWithStack {

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

    public TwoQueueWithStack(){
        // LinkedList实现了Queue接口
        queue = new LinkedList<Integer>();
        help = new LinkedList<Integer>();
    }

    // 插入一个元素
    public void push(int obj){
        // 插入元素永远都是插入到queue中
        queue.add(obj);
    }

    // 删除一个元素
    public int pop(){
        if(queue.isEmpty()){
            throw new RuntimeException("stack is empty!");
        }
        while(queue.size() > 1){
            // 将queue中除最后一个元素外,全部弹出添加到help中
            help.add(queue.poll());
        }
        int res = queue.poll();
        swap();
        return res;
    }

    // 弹出一个元素(不删除)
    public int peek(){
        if(queue.isEmpty()){
            throw new RuntimeException("stack is empty!");
        }
        while(queue.size() > 1){
            // 将queue中除最后一个元素外,全部弹出添加到help中
            help.add(queue.poll());
        }
        int res = queue.poll();
        help.add(res);
        swap();
        return res;
    }

    // 互换queue和help的指针,help只是辅助队列,始终操作的还是queue
    public void swap(){
        Queue<Integer> temp = help;
        help = queue;
        queue = temp;
    }
}

二、队列

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

  • 原理:可以用两个栈(stack1和stack2)来实现队列 ,进入时放入stack1栈,出栈时从stack2栈出,这样就能把顺序变为先进先出,( 栈:push,pop,peek)

需要注意的点:

  • 1、只有当stack2为空时,stack1才能往stack2中放数据,不然顺序就会乱了;
  • 2、如果stack1要往stack2中放数据,肯定是一次性将stack1中的数据全部放到stack2中。
package com.offer.class3;

import java.util.Stack;

/**
 * 用两个栈实现队列
 */
public class TwoStackWithQueue {

    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    // 添加元素
    public void add(int obj){
        stack1.push(obj);
    }

    // 删除元素
    public int poll(){
        if(stack2.isEmpty() && stack1.isEmpty()){
            throw new RuntimeException("queue is empty!");
        }else if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                // stack2如果为空,则stack1中的元素全部倒进stack2中
                stack2.push(stack1.pop());
            }
        }
        // 如果stack2中有元素,则直接弹出。只有当stack2为空时,才会从stack1中往stack2中放数据,而且肯定是一次性放完
        return stack2.pop();
    }

    // 弹出元素,不删除
    public int peek(){
        if(stack2.isEmpty() && stack1.isEmpty()){
            throw new RuntimeException("queue is empty!");
        }else if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                // stack2如果为空,则stack1中的元素全部倒进stack2中
                stack2.push(stack1.pop());
            }
        }
        // 弹出stack2中最上面的元素,即实现了队列的先进先出
        return stack2.peek();   // 前面的和poll一样,只不过最后需要返回而不是删除
    }
}

题目2:猫狗队列

宠物、狗和猫的类如下:
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类的实例。
  • 分析:

1、建一个猫狗队列类,这个类里包含 DogQueue 和 CatQueue 两个队列,用于分别加猫和加狗,但这个会导致在pollAll时无法判断之前猫狗的顺序,所以有了第2步;
2、建一个 CatDog 类,里面包含 pet 和 count 两个成员,pet 用于记录 CatDog 类的这个实例是猫还是狗,count 用于记录当前 pet 的顺序,count小的在前面,那么就能判断猫狗的顺序了。

package com.offer.class3;

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

/**
 * 猫狗队列问题
 */
public class CatDogQueue {

    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 CatDog{
        private Pet pet;     // 用于记录是猫还是狗
        private long count;  // 用于记录当前pet的顺序

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

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

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

    // CatDogQueue 的正式代码
    private Queue<CatDog> catQueue = new LinkedList<CatDog>();   // 猫队列
    private Queue<CatDog> dogQueue = new LinkedList<CatDog>();   // 狗队列
    private long count = 0;

    // 增加元素
    public void 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值