1.栈
1.1概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
原理图:
1.2java中的栈(Stack)
常用的方法:
1.3通过代码来感受一下
import java.util.Stack;
public class Test0530 {
public static void main(String[] args) {
Stack<Integer> stack=new Stack<>();
System.out.println("入栈元素1");
stack.push(1);
System.out.println("查看栈顶元素"+stack.peek());
System.out.println("出栈");
stack.pop();
System.out.println("看栈是否为空"+stack.empty());
}
}
运行结果:
2.手动实现一个自己的栈(MyStack)
手动实现一个泛型版本的栈,可以用存储任意类型的数据:
public class MyStack<T> {
private T[] arr=null;
private int size;
private int capacity=5;
public MyStack() {
arr= (T[]) new Object[capacity];
this.size=0;
}
//入栈
public void push(T var){
if(size==capacity){ //判断栈是否满了
//栈满了就扩容
T[] newarr= (T[]) new Object[2*capacity];
for(int i=0;i<size;i++){
newarr[i]=arr[i];
}
arr=newarr;
arr[size++]=var;
return;
}
arr[size++]=var;
return;
}
//出栈
public void pop(){
if(empty()){ //空栈
return;
}
size--;
return;
}
//查看栈顶元素
public T peek(){
if(empty()){
return null;
}
return (T)arr[size-1];
}
//判断栈是否为空
public boolean empty(){
if (size==0){
return true;
}
return false;
}
public static void main(String[] args) {
MyStack<Integer> myStack=new MyStack<>();
System.out.println(myStack.empty());
myStack.push(1); //入栈
myStack.push(2);
myStack.push(3);
myStack.push(4);
myStack.push(5);
myStack.push(6); //触发扩容
myStack.pop();
myStack.pop();
Integer result = myStack.peek();
System.out.println(result);
System.out.println(myStack.empty());
System.out.println("============");
MyStack<String> myStack1=new MyStack<>();
System.out.println(myStack1.empty());
myStack1.push("C++");
myStack1.push("java");
myStack1.push("Go");
System.out.println(myStack1.peek());
}
}
运行结果:
3.队列
3.1概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。
原理图:
3.2java中队列(Queue)
常用的方法:
3.3通过代码来感受一下
import java.util.LinkedList;
import java.util.Queue;
public class Test0530 {
public static void main(String[] args) {
Queue<Integer> queue=new LinkedList<>();
queue.add(1);
queue.offer(2);
System.out.println(queue.element());
System.out.println(queue.peek());
queue.poll();
queue.remove();
System.out.println(queue.isEmpty());
}
}
运行结果:
4.手动实现一个自己的队列( MyQueue)
手动实现一个泛型版本的队列,可以用存储任意类型的数据:
class MyExciption extends RuntimeException{
public MyExciption(String msg) {
super(msg);
}
}
class Node<T>{
T var;
Node<T> pre;
Node<T> next;
public Node(T var) {
this.var = var;
}
@Override
public String toString() {
return "Node{" +
"var=" + var +
'}';
}
}
public class MyQueue<T> {
private Node<T> head;
private Node<T> tail;
private int size;
public MyQueue() {
this.head = null;
this.tail = null;
this.size = 0;
}
//入队列 1 2 3
public void offer(T var){
Node<T> newNode=new Node<>(var);
if(head==null){
head=newNode;
tail=newNode;
size++;
return;
}
//一般情况 1 2 3
tail.next=newNode;
newNode.pre=tail;
tail=newNode;
size++;
return;
}
//入队列
public void add(T var){
Node<T> newNode=new Node<>(var);
if(head==null){
head=newNode;
tail=newNode;
size++;
return;
}
//一般情况 1 2 3
tail.next=newNode;
newNode.pre=tail;
tail=newNode;
size++;
return;
}
//出队列
public void poll(){
if(head==null){
return ;
}
if(head.next==null){
head=null;
tail=null;
size--;
return;
}
//一般情况 1 2 3
Node<T> curNode=head;
Node<T> nextNode=curNode.next;
nextNode.pre=null;
head=nextNode;
size--;
return;
}
//出队列
public void remove(){
if(head==null){
throw new MyExciption("位置非法!!!");
}
if(head.next==null){
head=null;
tail=null;
size--;
return;
}
//一般情况 1 2 3
Node<T> curNode=head;
Node<T> nextNode=curNode.next;
nextNode.pre=null;
head=nextNode;
size--;
return;
}
//判断是否为空队列
public boolean isEmpty(){
return size<=0;
}
//取队首元素
public T peek(){
if(head==null){
return null;
}
return (T)head;
}
//取队首元素
public T element(){
if(head==null){
throw new MyExciption("空指针异常!!!");
}
return (T)head;
}
public static void main(String[] args) {
MyQueue<Integer> myQueue=new MyQueue<>();
myQueue.add(1);
myQueue.add(2);
myQueue.offer(3);
System.out.println(myQueue.peek());
myQueue.poll();
System.out.println(myQueue.element());
System.out.println(myQueue.isEmpty());
}
}
5.栈和队列常见的笔试题
//用两个队列实现一个栈
class MyStack {
private Queue<Integer> A = new LinkedList<>();
private Queue<Integer> B = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
// 入栈的时候, 就直接往 A 中插入
A.offer(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
// 出栈的时候, 把 A 中的元素往 B 中倒腾. 当 A 中只剩一个元素的时候,
// 最后这个元素就是被删除的元素
if (A.isEmpty() && B.isEmpty()) {
// 针对空栈的判定. 但是人家给的方法不是 Integer, 没法返回 null, 只能随便返回个值了
// 在线 OJ 的代码咱们不要抛出异常!!!
return 0;
}
while (A.size() > 1) {
int tmp = A.poll();
B.offer(tmp);
}
// 当上面的循环结束, 此时 A 中就只剩一个元素了.
// 把这个最后的元素就作为出栈的结果即可.
int ret = A.poll();
// 最终完成操作之后, 要交换 A 和 B 的身份. 保证下次入栈的时候, 还是往 A 中插入.
swapAB();
return ret;
}
private void swapAB() {
Queue<Integer> tmp = A;
A = B;
B = tmp;
}
/** Get the top element. */
public int top() {
// 取栈顶元素和出栈类似. 唯一的区别就是, 出栈操作是把 A 的最后一个元素给删了.
// 取栈顶元素操作是把 A 的最后一个元素还要再塞回到 B 中.
// 出栈的时候, 把 A 中的元素往 B 中倒腾. 当 A 中只剩一个元素的时候,
// 最后这个元素就是被删除的元素
if (A.isEmpty() && B.isEmpty()) {
// 针对空栈的判定. 但是人家给的方法不是 Integer, 没法返回 null, 只能随便返回个值了
// 在线 OJ 的代码咱们不要抛出异常!!!
return 0;
}
while (A.size() > 1) {
int tmp = A.poll();
B.offer(tmp);
}
// 当上面的循环结束, 此时 A 中就只剩一个元素了.
// 把这个最后的元素就作为出栈的结果即可.
int ret = A.poll();
// 这个操作是唯一的区别.
B.offer(ret);
// 最终完成操作之后, 要交换 A 和 B 的身份. 保证下次入栈的时候, 还是往 A 中插入.
swapAB();
return ret;
}
/** Returns whether the stack is empty. */
public boolean empty() {
return A.isEmpty() && B.isEmpty();
}
}
class MyQueue {
Stack<Integer> A = new Stack<>();
Stack<Integer> B = new Stack<>();
/** Initialize your data structure here. */
public MyQueue() {
}
/** Push element x to the back of queue. */
public void push(int x) {
// 入队列的时候, 需要先把 B 中的元素都倒腾到 A 中, 再往 A 中插入新元素
while (!B.isEmpty()) {
int tmp = B.pop();
A.push(tmp);
}
A.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (A.isEmpty() && B.isEmpty()) {
return 0;
}
// 先把 A 的所有元素都倒腾到 B 中, 然后再通过 B 进行 pop
while (!A.isEmpty()) {
int tmp = A.pop();
B.push(tmp);
}
// 删除 B 中的元素
return B.pop();
}
/** Get the front element. */
public int peek() {
if (A.isEmpty() && B.isEmpty()) {
return 0;
}
while (!A.isEmpty()) {
int tmp = A.pop();
B.push(tmp);
}
// 删除 B 中的元素
return B.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return A.isEmpty() && B.isEmpty();
}