标签:
栈和队列----功能弱化的线性表(功能受到限制,比方说增删只能在首尾)
(广度优先遍历的时候用到队列 保存节点)
(实现递归---栈)
---------------------------------------------------------------------------------------------------------------------------
Stack(栈--栈顶插入删除)
1. pop出栈、push入栈、LIFO
2. 常用操作:
a. 初始化:构造器中,创建空栈
b. 返回栈的长度:返回栈中元素的个数
c. 入栈:栈顶插入一个元素,长度+1
d. 出栈:栈顶删除一个元素,长度-1
e. 访问栈顶元素:返回栈顶元素但是不删除元素
f. 判断栈是否为空
g. 清空栈
3. 顺序栈(当插入元素满了之后,要考虑数组容量重新定义)
import java.util.Arrays; public class SequenceStack<T> //顺序栈 { private int DEFAULT_SIZE=10; private int capacity; //数组容量 private int capacityIncrement = 0; //程序每次增加的数组长度 private Object[] elementData; private int size=0; //当前元素的个数 public SequenceStack() //创建一个空栈 { capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } public SequenceStack(T element) //用一个元素来初始化栈 { this(); elementData[0] = element; size++; } public SequenceStack(T element,int initSize, int capacityIncrement) //指定增长容量 { this.capacity = initSize; this.capacityIncrement = capacityIncrement; elementData = new Object[capacity]; elementData[0] = element; size++; } public int length() //获取顺序栈的大小 { return size; } public void push(T element) //入栈 { ensureCapacity(size+1); //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() //出栈:1.获取栈顶元素 2.栈元素的数量-1 3.原来栈顶引用为空 { T oldValue = (T) elementData[size-1]; elementData[--size] = null;//这1步可写成2步:--size; elementData[size]=null return oldValue; } public T peek() //取出栈顶元素 { return (T)elementData[size-1]; } public boolean isEmpty() //判断栈是否为空 { 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.replace(len-2, len, "]").toString(); } } }
4. 链栈(top保存栈顶元素的引用,size记录当前栈中保存元素的个数)
入栈---头插法,size+1
出栈---释放引用,size-1
(头插法实现了FILO)
public class LinkStack<T> { private class Node { private T data; private Node next; //指向下一个节点 public Node(){} public Node(T data, LinkStack<T>.Node next) { super(); 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; //原来top的指向改变 oldTop.next = null; size--; return oldTop.data; } public T peek() //取栈顶元素 { return top.data; } public boolean isEmpty() //判断是否为空 { return size==0; } public void clear() { top = null; size = 0; } public String toString() { if(isEmpty()) { 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.replace(len - 2, len, "] ").toString(); } } }
(整体而言,栈基本用不到顺序结构的随机访问功能,所以一般采用链栈)
(Java中Vector实现了List接口,Stack继承了Vector,所以Stack是线程安全的)
(Java.util.LinkedList提供了pop()、push()、peek(),所以它可以当做栈来使用)
队列:
Queue
(前端插入/后端删除 FIFO)
(前端:front;后端:rear)
常用操作:
1. 初始化队列(构造器中创建一个空队列)
2. 返回队列的长度
3. 加入元素(向队列的后端插入一个元素,队列的长度+1)(rear后移)
4. 删除元素(向队列的前端删除一个元素,队列的长度-1)(front后移)
5. 返回队列前端出队元素
6. 判断队列是否为空 (rear == front)
7. 清空队列
顺序队列:
front总是保存着队列中将要出队的元素,rear总是保存着下一个即将进入队列的元素的索引。
顺序队列实现:(front和rear用数字索引即可,元素个数为rear-front)
import java.util.Arrays; 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(isEmpty()) { throw new IndexOutOfBoundsException("队列已经空了..."); } T oldValue = (T) elementData[front]; elementData[front++] = null; return oldValue; } public T element() //获取队首元素 { if(isEmpty()) { throw new IndexOutOfBoundsException("空队列异常..."); } return (T) elementData[front]; } public boolean isEmpty() //判断是否为空 { return rear==front; } public void clear() //清空队列 { Arrays.fill(elementData,null); front = 0; rear = 0; } public String toString() { if(isEmpty()) { return "[]"; } else { StringBuilder sb = new StringBuilder("["); for(int i=front;i<rear;i++) { sb.append(elementData[i].toString() + ", "); } int len = sb.length(); return sb.replace(len-2,len,"]").toString(); } } }
可能出现“假满”("假溢出")
1. 新创建的队列----长度为n的空队列 rear和front都为0;
2. 不停的删除元素,front不停的后移,导致rear==front
解决办法
1. 保持出队时front不往后移,即每次出队,把所有元素整体往front端移动(front始终指向a0不变,当元素入列rear++,元素出列rear--)
2. 使用循环队列(front 和 rear到达底层数组的capacity-1时,就变成0)
循环队列
注意 (rear永远指向最后一个元素之后的下一位)
1. 队列满:rear == front;
2. 队列空----此时 elementData[front] == null;rear == front。
队列的长度:
rear > front的时候: rear - front
front > rear的时候: capacity-(front-rear)
顺序结构实现循环队列:
import java.util.Arrays; public class CircularQueue<T> { private int DEFAULT_SIZE = 10; private int capacity; private Object[] elementData; private int front = 0; private int rear = 0; public CircularQueue() //创建一个空队列 { capacity = DEFAULT_SIZE; elementData = new Object[capacity]; } public CircularQueue(T element) //以指定元素创建队列 { this(); elementData[0] = element; rear++; } public CircularQueue(T element,int initSize) //指定初始大小 { this.capacity = initSize; elementData = new Object[capacity]; elementData[0] = element; rear++; } public int length() { if(isEmpty()) { return 0; } else { return rear>front?rear-front:capacity-(front-rear); } } public void add(T element)//插入队列 rear++ { if(rear==front &&elementData[front]!=null)//队列已经满了 { throw new IndexOutOfBoundsException("队列已经满了..."); } elementData[rear++] = element; //队列的索引是从0到capacity-1;如果到达capacity说明回到了0位置 rear = rear==capacity ? 0 : rear; } public T remove() //出队 { if(isEmpty()) { throw new IndexOutOfBoundsException("空队列异常..."); } T oldValue = (T) elementData[front]; elementData[front++]=null; front= front==capacity ? 0 : front; return oldValue; } public T element() { if(isEmpty()) { throw new IndexOutOfBoundsException("空队列异常"); } return (T) elementData[front]; } public boolean isEmpty() { //队列的为空的情况 return rear==front && elementData[rear]==null; } public void clear() { Arrays.fill(elementData, null); front = 0; rear = 0; } public String toString() { if(isEmpty()) { return "[]"; } else { if(front<rear) //有效区间 front-->rear { StringBuilder sb = new StringBuilder("["); for(int i =front;i<rear;i++) { sb.append(elementData[i].toString()+", "); } int len = sb.length(); return sb.replace(len-2, len, "]").toString(); } else //有效区间 front-->capacity以及0-->front { 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.replace(len-2, len, "]").toString(); } } } }
小结:实际上循环队列所做的只不过是在普通队列的基础上加上了front/rear到达capacity就回到0
队列的链式存储:(链队列)
(不用担心溢出)
public class LinkQueue<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 front; //保存该链队列的头结点 private Node rear; //保存该链队列的尾节点 private int size; //保存链队列中的节点个数 public LinkQueue() //构建一个空队列 { front = null; rear = null; } public LinkQueue(T element) //创建一个只有一个元素的链队列 { front = new Node(element,null); rear = front; size++; } public int length() { return size; } public void add(T element)//入列,在rear后面加上一个节点 { if(front==null) { front = new Node(element,null); rear = front; } else { Node newNode = new Node(element,null); rear.next = newNode; rear = newNode; } size++; } public T remove() //删除队列front端的元素 { Node oldNode = front; front = front.next; oldNode.next = null; size--; return oldNode.data; //oldNode是局部变量不用主动置为null } public boolean isEmpty() //判断队列是否为空 { return size==0; } public void clear() //清空队列 { front = null; rear = null; size = 0; } public String toString() { if(isEmpty()) { return "[]"; } else { StringBuilder sb = new StringBuilder("["); for(Node current=front;current!=null;current=current.next) { sb.append(current.data.toString()+", "); } int len = sb.length(); return sb.replace(len-2, len, "]").toString(); } } }
小结:
链队列解决了很多问题。
顺序表一定要解决“假溢出”问题,方法之一就是“循环列表”,即超过了capacity就置为0.(当然队列的范围也做了相应的改变)
JDK中的队列:
Queue接口
N/A |
抛异常 |
返回特殊值 |
插入 |
add(e) |
offer(e) |
移除 |
remove() |
poll() |
检查 |
element() |
peek() |
Queue接口的实现类:(下面列举的均是线程安全的)
ArrayBlockingQueue ------高并发下,性能好
LinkedBlockingQueue ------吞吐量大
ConcurrentLinkedQueue -----wait-free队列算法实现
双向队列:Deque
(两端均可进行插入或者删除-----可以当做队列,一端插入一端删除;可以当做栈,插入删除均在一边)
LinkedList既可以当做队列,也可以当做栈、还可以当做线性表,所以功能强大。(线程不安全,需要外同步)
对于大部分程序而言: ArrayList、ArrayDeque的性能还是好过 LinkedList
外同步:
List list = Collections.synchronizedList(new LinkedList(...));
标签:
原文地址:http://www.cnblogs.com/bluechip/p/4176196.html