栈和队列
栈和队列
对线性表增加一些额外的限制和约束,例如,去除普通线性表中通过索引访问数据元素的功能,去除可以在任意位置添加、删除元素的功能,改成只允许在线性表某段添加、删除元素,这时候普通线性表就会变为两种特殊的线性表:栈和队列。
栈
Stack,一种特殊的线性表,这种线性表只能在固定一端(通常认为是线性表的尾端)进行插入、删除操作。
对于栈而言,允许进行插入、删除操作的一端被称为栈顶(top),另一端则被称为栈底(bottom)
如果一个栈不包含任何元素,那么这个栈就被称为空栈
从栈顶插入一个元素被称为进栈,对应的英文说法为push
从栈顶删除一个元素被称为出栈,对应的英文说法为pop
归纳:栈就是一种先进后出的线性表

栈的常用操作:
栈是一种被限制过的线性表,通常不应该提供线性表中的如下方法:
获取指定索引处的元素
按值查找数据元素的位置
向指定索引处插入数据元素
删除指定索引处的元素
也就是说,栈不应该提供从中间任意位置访问元素的方法。也就是说,栈允许在栈顶插入、删除元素
栈的常用操作如下:
初始化:
返回栈的长度:
入栈:
出栈:
访问栈顶元素
判断栈是否为空
清空栈
栈同样可以采用顺序结构来存储栈内元素,也可采用链式结构来存储栈内元素
栈的顺序存储结构以及实现:


package com.test_one; import java.util.Arrays; /** * Created by Administrator on 2017/3/7. */ public class SequenceStack<T> { private int DEFAULT_SIZE = 10; private int capacity; private int capacityIncrement = 0; private Object[] elementData; private int size; public SequenceStack(){ capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } public SequenceStack(T element){ this(); elementData[0] = element; size++; } public SequenceStack(T element , int initSize){ this.capacity = initSize; elementData = new Object[capacity]; elementData[0] = element; size++; } public SequenceStack(T element , int initSize , int capacityIncrement){ this(element , initSize); this.capacityIncrement = capacityIncrement; } public int length(){ return size; } public void push(T element){ ensureCapacity(size + 1); elementData[size++] = element; } private void ensureCapacity(int minCapacity){ if(minCapacity > capacity){ if(capacityIncrement > 0){ while(capacity < minCapacity){ capacity += capacityIncrement; } }else{ while(capacity < minCapacity){ capacity <<= 1; } } elementData = Arrays.copyOf(elementData , capacity); } } public T pop(){ T oldValue = (T)elementData[--size]; elementData[size] = null; return oldValue; } public T peek(){ return (T)elementData[size - 1]; } public boolean empty(){ return size == 0; } public void clear(){ Arrays.fill(elementData , null); size = 0; } public String toString(){ if(size == 0){ return "[]"; }else{ StringBuilder sb = new StringBuilder("["); for(int i = size - 1; i >-1 ; i--){ sb.append(elementData[i].toString() + ", "); } int len = sb.length(); return sb.delete(len - 2 , len).append("]").toString(); } } } public class SequenceStackTest { public static void main(String[] args){ SequenceStack<String> stack = new SequenceStack<String>(); stack.push("aaaa"); stack.push("bbbb"); stack.push("cccc"); stack.push("dddd"); System.out.println(stack); System.out.println("访问栈顶元素:" + stack.peek()); System.out.println("第一次弹出栈顶元素:" + stack.pop()); System.out.println("第二次弹出栈顶元素:" + stack.pop()); System.out.println("两次pop之后的栈:" + stack); } }
栈的链式存储结构以及实现:


package com.test_one; /** * Created by Administrator on 2017/3/7. */ public class LinkStack<T> { private class Node{ private T data; private Node next; public Node(){ } public Node(T data , Node next){ this.data = data; this.next = next; } } private Node top; private int size; public LinkStack(){ top = null; } public LinkStack(T element){ top = new Node(element , null); size++; } public int length(){ return size; } public void push(T element){ top = new Node(element , top); size++; } public T pop(){ Node oldTop = top; top = top.next; oldTop.next = null; size--; return oldTop.data; } public T peek(){ return top.data; } public boolean empty(){ return size == 0; } public void clear(){ top = null; size = 0; } public String toString(){ if(empty()){ return "[]"; }else{ StringBuilder sb = new StringBuilder("["); for(Node current = top; current != null; current = current.next){ sb.append(current.data.toString() + ", "); } int len = sb.length(); return sb.delete(len - 2 , len).append("]").toString(); } } } public class LinkStackTest { public static void main(String[] args){ LinkStack<String> stack = new LinkStack<String>(); stack.push("aaaa"); stack.push("bbbb"); stack.push("cccc"); stack.push("dddd"); System.out.println(stack); System.out.println("访问栈顶元素:" + stack.peek()); System.out.println("第一次弹出栈顶元素:" + stack.pop()); System.out.println("第二次弹出栈顶元素:" + stack.pop()); System.out.println("两次pop之后的栈:" + stack); } }
队列
队列是一种特殊的线性表,只允许在表的前端进行删除操作,只允许在表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列先进先出

队列的顺序存储结构及实现
顺序队列的front总是存着队列中即将出队列的元素的索引;顺序队列中的rear总是保存着下一个进行队列的元素的索引。队列中元素的个数为rear-front.
顺序队列使用数组实现,每个队列元素在数组中的位置是固定不变的,变的只是rear和front两个整形变量。但有元素进入队列时,rear变量的值+1;当有元素从队列中移除时,front变量的值+1.

package com.test_one; import java.util.Arrays; /** * Created by Administrator on 2017/3/10. */ public class SequenceQueue<T> { private int DEFAULT_SIZE = 10; private int capacity; private Object[] elementData; private int front = 0; private int rear = 0; public SequenceQueue(){ capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } public SequenceQueue(T element){ this(); elementData[0] = element; rear++; } public SequenceQueue(T element , int initSize){ this.capacity = initSize; elementData = new Object[capacity]; elementData[0] = element; rear++; } public int length(){ return rear - front; } public void add(T element){ if(rear > capacity - 1) { throw new IndexOutOfBoundsException("队列已满的异常"); } elementData[rear++] = element; } public T remove(){ if(empty()){ throw new IndexOutOfBoundsException("空队列异常"); } T oldValue = (T)elementData[front]; elementData[front++] = null; return oldValue; } public T element(){ if(empty()){ throw new IndexOutOfBoundsException("空队列异常"); } return (T)elementData[front]; } public boolean empty(){ return rear == front; } public void clear(){ Arrays.fill(elementData , null); front = 0; rear = 0; } public String toString(){ if(empty()){ return "[]"; }else{ StringBuilder sb = new StringBuilder("["); for(int i = front; i < rear; i++){ sb.append(elementData[i].toString() + ", "); } int len = sb.length(); return sb.delete(len - 2 , len).append("]").toString(); } } } package com.test_one; /** * Created by Administrator on 2017/3/10. */ public class SequenceQueueTest { public static void main(String[] args){ SequenceQueue<String> queue = new SequenceQueue<String>(); queue.add("aaaa"); queue.add("bbbb"); queue.add("cccc"); queue.add("dddd"); System.out.println(queue); System.out.println("访问队列的front端元素:" + queue.element()); System.out.println("移除队列的front端元素:" + queue.remove()); System.out.println("移除队列的front端元素:" + queue.remove()); System.out.println("两次调用remove方法后的队列:" + queue); } }
上面写的程序可能出现假满现象:

对于假满问题,程序有如下解决方法:
永远将元素移出队列时都将队列中的所有元素向front端移动一位,但这种方式front永远为0,元素插入队列时rear+1,元素移除队列时rear-1.但是这种方式非常浪费时间,因为每次将元素从队列移除都需要进行“整体搬家”
加那个数组存储区堪称一个首尾相接的环形区域,当存放到数组的最大地址后,rear的值再次变为0.次啊用这种技巧存储的队列称为循环队列。
循环队列:
对于右边的循环队列,不管队列是空还是满,都会出现一个情况:front ==rear。如果底层数组中elementData[front] == null表明此时队列为空,否则表明此队列已满。

package com.test_one; import java.util.Arrays; /** * Created by Administrator on 2017/3/10. */ public class LoopQueue<T> { private int DEFAULT_SIZE = 10; private int capacity; private Object[] elementData; private int front = 0; private int rear = 0; public LoopQueue(){ capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } public LoopQueue(T element){ this(); elementData[0] = element; rear++; } public LoopQueue(T element , int initSize){ this.capacity = initSize; elementData = new Object[capacity]; elementData[0] = element; rear++; } public int length(){ if(empty()){ return 0; } return rear > front ? rear - front : capacity - (front - rear); } public void add(T element){ if(rear == front && elementData[front] != null){ throw new IndexOutOfBoundsException("队列已满的异常"); } elementData[rear++] = element; rear = rear == capacity ? 0 :rear; } public T remove(){ if(empty()){ throw new IndexOutOfBoundsException("空队列异常"); } T oldValue = (T) elementData[front]; elementData[front++] = null; front = front == capacity ? 0 :front; return oldValue; } public T element(){ if(empty()){ throw new IndexOutOfBoundsException("空队列异常"); } return (T)elementData[front]; } public boolean empty(){ return rear == front && elementData[rear] == null; } public void clear(){ Arrays.fill(elementData , null); front = 0; rear = 0; } public String toString(){ if(empty()){ return "[]"; }else{ if(front < rear){ StringBuilder sb = new StringBuilder("["); for(int i = front; i < rear; i++){ sb.append(elementData[i].toString() + ", "); } int len = sb.length(); return sb.delete(len - 2 , len).append("]").toString(); }else{ StringBuilder sb = new StringBuilder("["); for(int i = front; i < capacity ; i++){ sb.append(elementData[i].toString() + ", "); } for(int i = 0; i < rear; i++){ sb.append(elementData[i].toString() + ","); } int len = sb.length(); return sb.delete(len - 2 , len).append("]").toString(); } } } } package com.test_one; /** * Created by Administrator on 2017/3/10. */ public class LoopQueueTest { public static void main(String[] args){ LoopQueue<String> queue = new LoopQueue<String>("aaaa" , 3); queue.add("bbbb"); queue.add("cccc"); System.out.println(queue); queue.remove(); System.out.println("删除一个元素后的队列:" + queue); queue.add("dddd"); System.out.println(queue); System.out.println("队列满时的长度:" + queue.length()); queue.remove(); queue.add("eee"); System.out.println(queue); } }
队列的链式存储结构及实现
类似于使用链式结构保存线性表,也可以使用链式结构存储队列的各元素,采用链式存储的结构的队列也被称为链队列。
对于链表而言,程序从rear端添加元素,从front端移除元素,因此考虑对链队列增加front、rear两个引用变量,使它们分别指向链队列的头、尾两个节点。由于链队列采用链式结构保存劣种所有元素,因此不存在队列满问题。



java集合中的队列:
从JDK1.5开始,java的集合框架中提供了一个Queue接口,该接口代表了一个队列,实现该接口的类可以当成队列使用。JDK1.5还提供了Deque接口,这个接口代表另一种特殊的队列--双向队列。

双向队列:
可以在两端同时进行插入、删除操作


浙公网安备 33010602011771号