一、栈
1.1概念
1、栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。遵循“先进后出”的原则
压栈:插入操作,在栈顶;
出栈:删除操作,在栈顶;
2、栈的使用
Stack() --构造一个空的栈
E push(E e) --将e入栈,并返回e
E pop() --将栈顶元素出栈并返回
E peek() --获取栈顶元素
int size() --获取栈中有效元素个数
boolean empty() --检测栈是否为空
3、栈的模拟实现
(1)单链表实现栈:可以采用头插法入栈和出栈
如果采用尾插法入栈:入栈时间复杂度O(n),如有last,为O(1),但是出栈一定是O(n)
(2)双链表实现栈:头插尾插都可以,时间复杂度都为O(1)
import java.util.Arrays;
/**
* @Author 12629
* @Description:
*/
public class MyStack {
public int[] elem;
public int usedSize;
public MyStack() {
this.elem = new int[10];
}
public void push(int val) {
if(isFull()) {
this.elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize++] = val;
}
public boolean isFull() {
return usedSize == elem.length;
}
public int pop() {
if(isEmpty()) {
throw new EmptyStackException();//抛出一个异常
}
int val = elem[usedSize-1];
usedSize--;
return val;
}
//获取栈顶元素 但是不删除
public int peek() {
if(isEmpty()) {
throw new EmptyStackException();
}
return elem[usedSize-1];
}
public boolean isEmpty() {
return usedSize == 0;
}
}
public class EmptyStackException extends RuntimeException{
public EmptyStackException() {
}
public EmptyStackException(String message) {
super(message);
}
}
4、栈的应用
最小栈:
思路:入栈:1、普通栈一定要放,最小栈放的原则是,如果最小栈为空,则放;如果最小栈的栈顶元素没有当前的元素小,则放;2、如果要放的元素<=最小栈的栈顶元素,则放;
出栈:判断出栈的元素和栈顶元素是否相同,相同的话最小栈也要出栈
import java.util.Stack;
class MinStack {
public Stack<Integer> stack;
public Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.empty()) {
minStack.push(val);
}else {
int peekVal = minStack.peek();
if(val <= peekVal) {
minStack.push(val);
}
}
}
public void pop() {
if(stack.empty()) {
return;
}
int popVal = stack.pop();
if(popVal == minStack.peek()) {
minStack.pop();
}
}
public int top() {
if(stack.empty()) {
return -1;
}
return stack.peek();
}
public int getMin() {
if(minStack.empty()) {
return -1;
}
return minStack.peek();
}
}
二、队列
1、概念:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,遵循“先进先出”的原则
2、队列的使用:
在Java中,Queue是个接口,底层是通过链表实现的。
boolean offer(E e) --入队列
E poll() --出队列
peek() --获取队头元素
int size() --获取队列中有效元素个数
boolean isEmpty() --检测队列是否为空
//:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.offer(2);
q.offer(3);
q.offer(4);
q.offer(5); // 从队尾入队列
System.out.println(q.size());
System.out.println(q.peek()); // 获取队头元素
q.poll();
System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
if(q.isEmpty()){
System.out.println("队列空");
}else{
System.out.println(q.size());
}
}
3、队列的模拟实现:
(1)单链表:入队采用尾插法,出队采用删除头结点
就算有尾结点标记,也不能从头节点入队,因为删除还是需要招最后一个节点的前节点
(2)双链表:链表的头和尾都可以入队
public class MyQueue {
//双向链表节点
static class ListNode {
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode first = null;
public ListNode last = null;
public int usedSize = 0;
public void offer(int val) {
ListNode node = new ListNode(val);
if(isEmpty()) {
first = last = node;
}else {
last.next = node;
node.prev = last;
last = last.next;
}
usedSize++;
}
public int poll() {
//队列为空
//队列中只有一个元素,删除
//队列中有多个元素,删除第一个
if(isEmpty()) {
return -1;
}
int val = first.val;
first = first.next;
if(first != null) {
first.prev = null;
}
usedSize--;
return val;
}
public int peek() {
if(isEmpty()) {
return -1;
}
return first.val;
}
public boolean isEmpty() {
return usedSize == 0;
//return first == null;
}
}
(3)数组实现:循环队列
用front表示队头,rear表示队尾
队空:front=rear
队满:(rear+1)%array.length=front
class MyCircularQueue {
public int front;
public int rear;
public int[] elem;
public MyCircularQueue(int k) {
elem = new int[k+1];
}
public boolean enQueue(int value) {
if(isFull()){
return false;
}
elem[rear]=value;
rear=(rear+1)%elem.length;
return true;
}
public boolean deQueue() {
if(isEmpty()){
return false;
}
front=(front+1)%elem.length;
return true;
}
public int Front() {
if(isEmpty()){
return -1;
}
return elem[front];
}
public int Rear() {
if(isEmpty()){
return -1;
}
int index=(rear==0)?elem.length-1:rear-1;
return elem[index];
}
public boolean isEmpty() {
return rear==front;
}
public boolean isFull() {
return (rear+1)% elem.length==front;
}
}
4、栈与队列相互实现
(1)队列实现栈:两个队列,模拟入栈:放到不为空的队列当中;
模拟出栈:把不为空的队列中size-1个元素放到另一个队列中,最后剩下的这个就是我们模拟出栈的元素
import java.util.LinkedList;
import java.util.Queue;
class MyStackUseQueue {
public Queue<Integer> qu1;
public Queue<Integer> qu2;
public MyStackUseQueue() {
qu1 = new LinkedList<>();
qu2 = new LinkedList<>();
}
public void push(int x) {
if(!qu1.isEmpty()) {
qu1.offer(x);
}else if(!qu2.isEmpty()) {
qu2.offer(x);
}else {
qu1.offer(x);
}
}
public int pop() {
if(empty()) {
return -1;
}
if(!qu1.isEmpty()) {
int size = qu1.size();
for(int i = 0;i < size-1;i++) {
qu2.offer( qu1.poll());
}
return qu1.poll();
}else {
int size = qu2.size();
for(int i = 0;i < size-1;i++) {
qu1.offer( qu2.poll());
}
return qu2.poll();
}
}
public int top() {
if(empty()) {
return -1;
}
if(!qu1.isEmpty()) {
int size = qu1.size();
int val = 0;
for(int i = 0;i < size;i++) {
val = qu1.poll();
qu2.offer(val);
}
return val;
}else {
int size = qu2.size();
int val = 0;
for(int i = 0;i < size;i++) {
val = qu2.poll();
qu1.offer(val);
}
return val;
}
}
public boolean empty() {
return qu1.isEmpty() && qu2.isEmpty();
}
}
(2)用栈实现队列:两个栈,模拟入队:放到第一个栈;
模拟出队:判断第二个栈是不是空的,如果是,直接取出栈顶元素,如果不为空,将栈1中元素全部放到栈2,取出栈顶元素
import java.util.ArrayDeque;
class MyQueueUseStack {
public ArrayDeque<Integer> stack1;
public ArrayDeque<Integer> stack2;
public MyQueueUseStack() {
stack1 = new ArrayDeque<>();
stack2 = new ArrayDeque<>();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if(empty()) {
return -1;
}
if(stack2.isEmpty()) {
//第一个栈里面所有的元素 放到第二个栈当中
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if(empty()) {
return -1;
}
if(stack2.isEmpty()) {
//第一个栈里面所有的元素 放到第二个栈当中
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}