赞
踩
目录
栈栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 入栈 push:栈的插入操作叫做进栈/压栈/入栈,入栈数据在栈顶。 出栈 pop:栈的删除操作叫做出栈。出栈数据在栈顶。
1. 利用顺序表实现,即使用尾插 + 尾删的方式实现 2. 利用链表实现,则头尾皆可
相对来说,顺序表的实现上要更为简单一些,所以我们优先用顺序表实现栈。
- package stack_queue.stack;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.NoSuchElementException;
-
- //基于数组的顺序表实现 栈
- public class MyStack<E> {
- //当前栈的数据个数
- private int size;
- //实际存储数据的动态数组 - ArrayList
- private List<E> data = new ArrayList<>();
-
- /**
- * 将val入栈
- * @param val
- */
- public void push(E val){
- //默认尾插
- data.add(val);
- size++;
- }
-
- /**
- * 弹出栈顶元素
- * 返回栈顶的值
- * @return
- */
- public E pop(){
- if (isEmpty()){
- //栈为空
- throw new NoSuchElementException("stack is empty!cannot pop!");
- }
- //删除栈顶元素
- E val = data.remove(size - 1);
- size--;
- return val;
- //等同于 return data.remove(--size);
- }
-
- /**
- * 返回栈顶元素但不出栈
- * @return
- */
- public E peek(){
- if (isEmpty()){
- throw new NoSuchElementException("stack is empty!cannot peek");
- }
- return data.get(size-1);
- }
-
- /**
- * 判断当前栈是否为空
- * @return
- */
- private boolean isEmpty() {
- return size == 0;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("[");
- for (int i = 0; i < size; i++) {
- sb.append(data.get(i));
- if (i!=size-1){
- //此时还未到栈顶,还没到数组末尾
- sb.append(",");
- }
- }
- sb.append("] top");
- return sb.toString();
- }
- }
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)
- package stack_queue.queue.impl;
-
- import java.util.NoSuchElementException;
-
- /**
- * 基于链表实现的基础队列
- */
- public class MyQueue<E> implements Queue<E> {
- //链表的每个节点
- private class Node{
- E val;
- Node next;
- public Node(E val){
- this.val = val;
- }
- }
-
- //当前队列中的元素个数
- private int size;
- //队首
- private Node head;
- //队尾
- private Node tail;
-
- //入队
- @Override
- public void offer(E val) {
- Node node = new Node(val);
- if (head == null){
- //此时链表为空
- head = tail = node;
- }else {
- tail.next = node;
- tail = node;
- }
- size++;
- }
-
- //出队
- @Override
- public E poll() {
- if (isEmpty()){
- throw new NoSuchElementException("queue is empty!cannot poll!");
- }
- E val = head.val;
- Node node = head;
- head = head.next;
- //将原来头节点脱钩
- node.next = null;
- size--;
- return val;
- }
-
- //返回队首元素
- @Override
- public E peek() {
- if (isEmpty()){
- throw new NoSuchElementException("queue is empty!cannot peek!");
- }
- return head.val;
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("front [");
- //链表的遍历
- for (Node x =head; x != null;x = x.next){
- sb.append(x.val);
- if (x.next != null){
- //还没走到链表尾部
- sb.append(",");
- }
- }
- sb.append("]");
- return sb.toString();
- }
- }
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。 环形队列通常使用数组实现。
数组下标循环的小技巧
1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length
- package stack_queue.queue.impl;
-
- import java.util.Arrays;
- import java.util.NoSuchElementException;
-
- //基于整形的循环队列
- public class LoopQueue implements Queue<Integer> {
- private Integer[] data;
- //指向当前循环队列的队首元素
- private int head;
- //指向当前循环队列的队尾元素的下一个位置
- private int tail;
- //当前队列中元素个数
- private int size;
- //n为希望保存的元素个数
- public LoopQueue(int n){
- //在循环队列中浪费一个空间不能存储元素,来判断是否已满
- data = new Integer[n + 1];
- }
-
- @Override
- public void offer(Integer val) {
- if (isFull()){
- throw new ArrayIndexOutOfBoundsException("queue is full!cannot offer new val");
- }
- data[tail] = val;
- tail = (tail + 1) % data.length;
- size++;
- }
-
- @Override
- public Integer poll() {
- if (isEmpty()){
- throw new NoSuchElementException("queue is empty!cannot poll");
- }
- Integer val = data[head];
- head = (head + 1) % data.length;
- size--;
- return val;
- }
-
- @Override
- public Integer peek() {
- if (isEmpty()){
- throw new NoSuchElementException("queue is empty!cannot peek");
- }
- return data[head];
- }
-
- @Override
- public boolean isEmpty() {
- return tail == head;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("front [");
- //取得最后一个元素下标
- int lastIndex = tail == 0 ? data.length - 1 : tail - 1;
- for (int i = head;i != tail;){
- sb.append(data[i]);
- if (i!=lastIndex){
- sb.append(", ");
- }
- i = (i + 1) % data.length;
- }
- sb.append("] tail");
- return sb.toString();
- }
-
- public boolean isFull(){
- return (tail + 1) % data.length == head;
- }
- }
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。 那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
- package stack_queue.queue;
-
- import java.util.ArrayDeque;
- import java.util.Deque;
-
- //双端队列
- public class DequeTest {
- public static void main(String[] args) {
- Deque<Integer> deque = new ArrayDeque<>();
- deque.addLast(1);
- deque.addLast(3);
- deque.addLast(5);
- //尾插尾删或者头插头删就是栈
- while (!deque.isEmpty()){
- System.out.println(deque.pollLast());//5 3 1
- }
-
- // //头插尾删或者尾插头删就是队列
- // while (!deque.isEmpty()){
- // System.out.println(deque.pollFirst());//1 3 5
- // }
-
- }
- }
本节完^_^
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。