栈和队列
逻辑概念
栈:数据先进后出
队列:数据先进先出
栈和队列的实际实现
双向链表实现
代码实现:
package class02;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class Code03_DoubleEndsQueueToStackAndQueue {
public static class Node<T> {
public T value;
public Node<T> last;
public Node<T> next;
public Node(T data) {
value = data;
}
}
public static class DoubleEndsQueue<T> {
public Node<T> head;
public Node<T> tail;
public void addFromHead(T value) {
Node<T> cur = new Node<T>(value);
if (head == null) {
head = cur;
tail = cur;
} else {
cur.next = head;
head.last = cur;
head = cur;
}
}
public void addFromBottom(T value) {
Node<T> cur = new Node<T>(value);
if (head == null) {
head = cur;
tail = cur;
} else {
cur.last = tail;
tail.next = cur;
tail = cur;
}
}
public T popFromHead() {
if (head == null) {
return null;
}
Node<T> cur = head;
if (head == tail) {
head = null;
tail = null;
} else {
head = head.next;
cur.next = null;
head.last = null;
}
return cur.value;
}
public T popFromBottom() {
if (head == null) {
return null;
}
Node<T> cur = tail;
if (head == tail) {
head = null;
tail = null;
} else {
tail = tail.last;
tail.next = null;
cur.last = null;
}
return cur.value;
}
public boolean isEmpty() {
return head == null;
}
}
public static class MyStack<T> {
private DoubleEndsQueue<T> queue;
public MyStack() {
queue = new DoubleEndsQueue<T>();
}
public void push(T value) {
queue.addFromHead(value);
}
public T pop() {
return queue.popFromHead();
}
public boolean isEmpty() {
return queue.isEmpty();
}
}
public static class MyQueue<T> {
private DoubleEndsQueue<T> queue;
public MyQueue() {
queue = new DoubleEndsQueue<T>();
}
public void push(T value) {
queue.addFromHead(value);
}
public T poll() {
return queue.popFromBottom();
}
public boolean isEmpty() {
return queue.isEmpty();
}
}
public static boolean isEqual(Integer o1, Integer o2) {
if (o1 == null && o2 != null) {
return false;
}
if (o1 != null && o2 == null) {
return false;
}
if (o1 == null && o2 == null) {
return true;
}
return o1.equals(o2);
}
public static void main(String[] args) {
int oneTestDataNum = 100;
int value = 10000;
int testTimes = 100000;
for (int i = 0; i < testTimes; i++) {
MyStack<Integer> myStack = new MyStack<>();
MyQueue<Integer> myQueue = new MyQueue<>();
Stack<Integer> stack = new Stack<>();
Queue<Integer> queue = new LinkedList<>();
for (int j = 0; j < oneTestDataNum; j++) {
int nums = (int) (Math.random() * value);
if (stack.isEmpty()) {
myStack.push(nums);
stack.push(nums);
} else {
if (Math.random() < 0.5) {
myStack.push(nums);
stack.push(nums);
} else {
if (!isEqual(myStack.pop(), stack.pop())) {
System.out.println("oops!");
}
}
}
int numq = (int) (Math.random() * value);
if (stack.isEmpty()) {
myQueue.push(numq);
queue.offer(numq);
} else {
if (Math.random() < 0.5) {
myQueue.push(numq);
queue.offer(numq);
} else {
if (!isEqual(myQueue.poll(), queue.poll())) {
System.out.println("oops!");
}
}
}
}
}
System.out.println("finish!");
}
}
数组实现
数组实现栈:
分析:首先是给一个固定大小的数组的大小,首先定义一个变量index,初始没有值的时候为-1,增加值的时候arr[++index]=value;
代码实现:
package class02;
public class Code04_ArrtoStack {
public static void push(int[]arr,int value,int index){
arr[++index]=value;
}
public static int pop(int index){
return index--;
}
}
数组实现队列:
分析:由于队列的逻辑是先进先出的,所以我们要准备两个指针,一个指针指向队头,一个指向队尾,head和tail,开始时head=tail=-1;加入数时++tail,删除数时,++head,但是存在一个问题,当tail到达数组最大下标时,而head的小标不为0,此时就不能加入数据了,但此时数组里面还存在空间还能存储值,所以我们用limit来做限制
代码如下:
package class02;
public class Code04_RingArray {
public static class MyQueue {
public int[] arr;//数组
public int pushindex;//队头加值
public int popindex;//队尾减值
public int size;//装的值
public int limit;//队列最大能装的数量
public MyQueue(int i) {
arr = new int[i];
popindex=pushindex=0;
size=0;
limit=i;
}
public void push(int value){
if(size == limit)
throw new RuntimeException("空间满了,不能再加了");
size++;
arr[popindex] = value;
if(size < limit - 1)
popindex++;
else
popindex = 0;
}
public int pop(){
if(size == 0)
throw new RuntimeException("没有值,不能往外拿值");
int ans = arr[popindex];
if(size<limit-1)
popindex--;
else
popindex = 0;
return ans;
}
}
}
既然语言都有这些结构和api,为什么还要我们自己底层实现呢?
1)算法问题无关语言
2)语言提供的api是有限的,当我们需要实现新的功是api不能提供的时候,就需要自己去改写
3)任何软件工具的底层都是最基本的算法和数据结构,这是绕不过去的
栈和队列的常见面试题
实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能
1.pop,push,getMin操作的时间复杂度都是O(1)
2 .设计的栈类型可以使用现成的栈结构
分析:应该准备A,B两个栈,首先先往A栈压入值,其最小值就是压入栈的值,把该值也压入B栈,然后再把值压入A栈,比较A栈顶于B栈顶的大小,如果A栈顶的值小于B栈顶的值,蒋A栈顶的值也压入B栈,如果不大于,则B栈再压入原来B栈顶的值,依次这样,然后再弹栈的时候一次弹出A,B两个栈的栈顶,随便怎么弹,B的栈顶就是A栈的最小值,实现了O(1)。
1)如何用栈结构实现队列结构
分析:准备两个栈,把一个栈装满,然后把这个栈的东西导到另一个栈,从另外一个栈导出去。
2)如何用队列结构实现栈结构
分析:准备两个队列,把这个队列的东西只留一个值,剩下的值导进另外一个队列,然后把这个队列的值导出去,依次循环。