目录
- 用数组结构实现大小固定的队列和栈
- 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
- 如何仅用队列结构实现栈结构?
- 如何仅用栈结构实现队列结构?
- 猫狗队列
- 转圈打印矩阵
- 旋转正方形矩阵
- 反转单向和双向链表
用数组结构实现大小固定的队列和栈
栈的实现:引入一个变量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];
}
}
实现一个特殊的栈
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返
回栈中最小元素的操作。
【要求】
- pop、push、getMin操作的时间复杂度都是O(1)。
- 设计的栈类型可以使用现成的栈结构。
思路:
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中。倒入操作需要满足两点:
- 一次倒入操作必须把stackPush中的数据倒完;
- 必须在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;
}
参考资料:牛客网左程云初级算法教程