栈,队列
题目一
用数组结构实现大小固定的队列和栈
要求:
自己实现的栈应该包括 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)
解题思路:
将顺时针转圈打印转换成一圈一圈地打印,只要知道矩阵左上角的点和矩阵右下角的点就可以将打印出一圈。打印的顺序如图:
接下来,左上角的点和右下角的点横纵坐标减一,再继续打印内圈的部分,这样就和顺时针转圈打印的效果是一样的。重点是需要考虑好几种情况:
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)
之字形打印的示意图如下:
本题的思路:
设定两个点,这两个点起始的位置均为(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)的那一行就不会打印出来。请用心体会。