轉載請標明出處:
https://blog.csdn.net/xmxkf/article/details/82465726
本文出自:【openXu的博客】
從數據結構的定義看,棧和隊列也是一種線性表。其不同之處在於棧和隊列的相關運算具有特殊性,只是線性表相關運算的一個子集。更準確的說,一般線性表的插入、刪除運算不受限制,而棧和隊列上的插入刪除運算均受某種特殊限制。因此,棧和隊列也稱作操作受限的線性表。
1、棧
1.1 棧的定義
棧(Stack)是一種只能在一端進行插入或刪除操作的線性表。表中允許進行插入、刪除操作的一端稱爲棧頂(Top)。棧頂的當前位置是動態的,棧頂的當前位置是由一個稱爲棧頂指針的位置指示器指示。表的另一端稱爲棧底(Bottom)。當棧中沒有數據元素時稱爲空棧。棧的插入操作稱爲進棧或入棧(Push),刪除操作稱爲退棧或出棧(Pop)。
棧的主要特點是“後進先出”,即後進棧的元素先彈出。每次進棧的數據元素都放在原當前棧頂元素之前成爲新的棧頂元素,每次出棧的數據元素都是當前棧頂元素。棧也稱爲後進先出表。
1.2 棧的順序存儲結構實現
通常棧可以用順序的方式存儲,分配一塊連續的存儲區域存放棧中的元素,並用一個變量指向當前的棧頂。採用順序存儲的棧稱爲順序棧,Java util包下的Stack就是順序棧。
順序棧的操作示意圖如下:
順序棧的實現如下:
public class StackByArray<T>{
private int top = -1; //棧頂指針,-1代表空棧
private int capacity = 10; //默認容量
private int capacityIncrement = 5; //容量增量
private T[] datas; //元素容器
public StackByArray(int capacity){
datas = (T[])new Object[capacity];
}
public StackByArray(){
datas = (T[])new Object[capacity];
}
/**溢出擴容 */
private void ensureCapacity() {
int oldCapacity = datas.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
datas = Arrays.copyOf(datas, newCapacity);
}
/**進棧,將元素添加到棧頂*/
public synchronized void push(T item) {
//容量不足時擴容
if(top>=datas.length-1)
ensureCapacity();
datas[++top] = item;
}
/**出棧,將棧頂的元素移除並返回該元素*/
public synchronized T pop() {
if(top<0)
new EmptyStackException();
T t = datas[top];
datas[top] = null;
top--;
return t;
}
/**清空棧*/
public synchronized void clear() {
if(top<0)
return;
for(int i = top; i>=0; i--)
datas[i] = null;
top = -1;
}
/**返回棧頂元素*/
public T peek() {
if(top<0)
new EmptyStackException();
return datas[top];
}
/**獲取棧中元素個數*/
public int getLength() {
return top+1;
}
/**棧是否爲空棧*/
public boolean empty() {
return top<0;
}
/**獲取元素到棧頂的距離 */
public int search(T data) {
int index = -1;
if(empty())
return index;
if (data == null) {
for (int i = top; i >= 0; i--)
if (datas[i]==null){
index = i;
break;
}
} else {
for (int i = top; i >= 0; i--)
if (data.equals(datas[i])) {
index = i;
break;
}
}
if (index >= 0) {
return top - index;
}
return index;
}
@Override
public String toString() {
if(empty())
return "[]";
StringBuffer buffer = new StringBuffer();
buffer.append("[");
for(int i = 0; i<=top; i++){
buffer.append(datas[i]+", ");
}
return buffer.subSequence(0, buffer.lastIndexOf(", "))+"]";
}
}
1.3 棧的鏈式存儲結構實現
採用鏈式存儲結構的棧稱爲鏈棧,在鏈棧中規定所有操作都是在鏈表表頭進行的,鏈棧的優點是不存在棧滿上溢的情況。上述順序棧的實現過程中增加了擴容的功能,所以也能實現不上溢。
鏈棧的操作示意圖如下:
鏈棧的實現如下:
public class StackByLink<T>{
public LNode<T> top; //棧頂指針,指向棧頂結點
private int size = 0;
/**進棧,將元素添加到棧頂*/
public synchronized void push(T item) {
LNode temp = new LNode();
temp.next = top; //添加的結點的指針域指向當前棧頂結點
top = temp; //更新棧頂結點
size ++;
}
/**出棧,將棧頂的元素移除並返回該元素*/
public synchronized T pop() {
if(top==null)
new EmptyStackException();
LNode < T> next = top.next;
T data = top.data;
top.next = null;
top.data=null;
top = next;
size--;
return data;
}
/**清空棧*/
public synchronized void clear() {
LNode<T> next;
while(top!=null){
next = top.next;
top.data = null;
top.next = null;
top = next;
}
size = 0;
}
/**返回棧頂元素*/
public T peek() {
if(top==null)
new EmptyStackException();
return top.data;
}
/**獲取棧中元素個數*/
public int getLength() {
return size;
}
/**棧是否爲空棧*/
public boolean empty() {
return top==null;
}
/**獲取元素到棧頂的距離 */
public int search(T data) {
int dis = -1;
if(empty())
return dis;
LNode<T> node = top;
if (data == null) {
while(node!=null){
dis ++;
if (node.data == null)
return dis;
}
} else {
while(node!=null){
dis ++;
if (node.data.equals(data))
return dis;
}
}
return dis;
}
@Override
public String toString() {
if(empty())
return "[]";
LNode node = top;
StringBuffer buffer = new StringBuffer();
buffer.append("[");
while(node != null){
buffer.append(node.data+", ");
node = node.next;
}
return buffer.subSequence(0, buffer.lastIndexOf(", "))+"]";
}
}
1.4 兩種棧的效率分析
順序棧和鏈式棧中,主要的操作算法push(T item)、pop()、 peek()的時間複雜度和空間複雜度都是O(1),從效率上都是一樣的,因爲棧只對一端進行操作。順序棧和鏈式棧實現起來最大的區別就是鏈式棧不用考慮容量問題,而順序棧會有上溢的情況,如果加上自動擴容,則push(T item)的時間複雜度將變爲O(n)。所以鏈式棧相比會更加靈活一點。
1.5 棧的應用
棧是一種最常用也是最重要的數據結構質疑,用途十分廣泛。在二叉樹的各種算法中大量地使用棧,將遞歸算法轉換成非遞歸算法時也常常用到棧;接下來我們用破解迷宮的算法示例棧的應用。
破解迷宮
給定一個M * N的迷宮圖,求一條從指定入口到出口的路徑。假設迷宮圖如下圖所示,白色表示通道,深色表示牆。所求路徑必須是簡單路徑,即路徑中不能重複出現同一通道塊。爲了表示迷宮,設置一個二維數組maze,其中每個元素表示一個方塊的狀態,0表示是通道,1表示該方塊不可走。
在求解時,通常使用“窮舉求解”的方法,即從入口出發,順某一方向向前試探,若能走通,則繼續往前走,否則眼原路退回,換一個方向在繼續試探,直到所有可能的通道都試探完爲止。
爲了保證在任何爲止上都能沿原路退回,需要用一個後進先出的棧來保存從入口到當前位置的路徑。每次取棧頂的方塊,試探這個方塊下一個可走的方向(上方、右方、下方、左方),如果找到可走的方向,則將該方向下一個方塊入棧,如果沒有可走的下一個方塊,說明該路死了,需要將當前棧頂元素出棧。爲了保證試探的可走相鄰方塊不是已走路徑上的方塊,比如(i,j)已經入棧,在試探(i+1, j)的下一可走方塊時又試探到(i,j),這可能會引起死循環,爲此,在一個方塊入棧後,將對應數組元素值改爲-1(只有0纔是可走),當出棧時,再將其恢復至0。
算法實現如下:
public static StackByArray<MazeElem> getMazePathByArrayStack(int[][] maze, int startX,
int startY, int endX, int endY){
int x = startX, y = startY; //用於查找下一個可走方位的座標
boolean find; //是否找到棧頂元素下一個可走 的 方塊
StackByArray<MazeElem> stack = new StackByArray();
//入口元素入棧
MazeElem elem = new MazeElem(startX, startY, -1);
stack.push(elem);
maze[x][y] = -1; //將入口元素置爲-1,避免死循環
//棧不爲空時循環
while(!stack.empty()){
elem = stack.peek(); //取棧頂元素
if(elem.x == endX && elem.y == endY){
//棧頂元素與出口座標一樣時,說明找到出口了
Log.e(TAG, "迷宮路徑如下:\n"+stack);
return stack;
}
find = false;
//遍歷棧頂元素的四個方向 ,找 棧頂 元素 下一個可走 方塊
while(elem.di < 4 && !find){
elem.di++;
switch ((elem.di)){
case 0: //上方
x = elem.x-1; y = elem.y;
break;
case 1: //右方
x = elem.x; y = elem.y+1;
break;
case 2: //下方
x = elem.x+1; y = elem.y;
break;
case 3: //左方
x = elem.x; y = elem.y-1;
break;
}
//避免OutOfIndex
if(x>=0 && y>=0 && x<maze.length && y<maze[0].length)
find = maze[x][y]==0;
}
if(find){ //找到了下一個可走方向
stack.push(new MazeElem(x, y, -1)); //下一個可走方向入棧
maze[x][y] = -1; //入棧後置爲-1,避免死循環
}else{
//棧頂元素沒有下一個可走結點,則出棧,並將該方塊置爲0(可走)
elem = stack.pop();
maze[elem.x][elem.y] = 0;
}
}
return null;
}
2、隊列
2.1 隊列的定義
隊列簡稱隊,它也是一種操作受限的線性表,其限制爲僅允許在表的一端進行插入,而在表的另一端進行刪除。把進行插入的一端稱做隊尾(rear),進行刪除的一端稱做隊首或隊頭(front)。向隊列中插入新元素稱爲進隊或入隊,新元素進入後就成爲新的隊尾元素;從隊列中刪除元素稱爲出隊或離隊,元素出隊後,其直接後繼元素就成爲隊首元素。
由於隊列的插入和刪除操作分別是在各自的一端進行的,每個元素必然按照進入的次序出隊,所以又把隊列稱爲先進先出表。
2.2 隊的順序存儲結構實現
隊列的順序存儲結構需要使用一個數組和兩個整數型變量來實現, 利用數組順序存儲隊列中的所有元素,利用兩個整數變量分別存儲隊首元素和隊尾元素的下標位置,分別稱爲隊首指針和隊尾指針。
假設隊列元素個數最大不超過MaxSize,當隊列爲初始狀態有front==rear,該條件可作爲隊列空的條件。那麼能不能用rear==MaxSize-1作爲隊滿的條件呢?顯然不能,如果繼續往上圖的隊中添加元素時出現“上溢出”,這種溢出並不是真正的溢出,因爲數組中還存在空位置,所以這是一種假溢出。
爲了能充分的使用數組中存儲空間,把數組的前端和後端連接起來,形成一個環形的順序表,即把存儲隊列元素的表從邏輯上看成一個環,稱爲環形隊列。如下如所示:
上圖對應步驟解析如下:
- 初始化隊列時,隊首front和隊尾rear都指向0;
- a入隊時,隊尾rear=rear+1指向1;
- bc入隊後,rear=3,此時隊已滿。其實此時隊中還有1個空位,如果這個空位繼續放入一個元素,則rear=front=0,這和rear=front時隊爲空衝突,所以爲了算法設計方便,此處空出一個位。所以判斷隊滿的條件是(rear+1)%MaxSize == front
- ab出隊,隊首front=2;
- de入隊,此時rear=1;滿足(rear+1)%MaxSize == front,所以隊滿
- cde出隊,此時rear=front=1,隊空
通過上述分析,我們可以得出,出隊和入隊操作會使得隊首front隊尾rear指向新的索引,由於數組爲環狀,可通過求餘%運算來實現:
入隊(隊尾指針進1):rear = (rear+1)%MaxSize
出隊(隊首指針進1):front = (front+1)%MaxSize
當滿足 rear==front時,隊列爲空,我們可以在出隊操作後,判斷此條件,如果滿足則說明隊列爲空了,可以將rear和front重新指向0;
當需要入隊操作時,首先通過(rear+1)%MaxSize == front判斷是否隊滿,如果隊滿,則需要空充容量,否則會溢出。對於環形隊列的講解就到這裏,下面是實現代碼:
public class QueueByArray<T>{
private int front = 0; //隊首指針(出隊)
private int rear = 0; //隊尾指針(入隊)
private int size; //元素個數
private int capacity = 10; //默認容量
private int capacityIncrement = 5; //容量增量
private T[] datas; //元素容器
public QueueByArray(int capacity){
datas = (T[])new Object[capacity];
}
public QueueByArray(){
datas = (T[])new Object[capacity];
}
public int getFront() {
return front;
}
public void setFront(int front) {
this.front = front;
}
public int getRear() {
return rear;
}
public void setRear(int rear) {
this.rear = rear;
}
/**獲取隊中元素個數*/
public int getSize() {
return rear - front;
// return size;
}
/**是否爲空隊*/
public boolean isEmpty() {
return rear==front;
}
/**入隊*/
public synchronized boolean enQueue(T item) {
if(item==null)
throw new NullPointerException("item data is null");
//判斷是否滿隊
if((rear+1) % datas.length == front){
//滿隊時擴容
ensureCapacity();
}
//添加data
datas[rear] = item;
//更新rear指向下一個空元素的位置
rear = (rear+1) % datas.length;
size++;
return true;
}
/**雙端隊列 從隊首入隊*/
public synchronized boolean enQueueFront(T item) {
if(item==null)
throw new NullPointerException("item data is null");
//判斷是否滿隊
if((rear+1) % datas.length == front){
//滿隊時擴容
ensureCapacity();
}
//使隊首指針指向上一個空位
front = (front-1+datas.length)%datas.length;
//添加data
datas[front] = item;
size++;
return true;
}
/**溢出擴容 */
private void ensureCapacity() {
int oldCapacity = datas.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
T[] oldDatas = datas;
datas = (T[])new Object[newCapacity];
int j = 0;
//將原數組中元素拷貝到新數組
for (int i = front; i!=this.rear ; i = (i+1) % datas.length) {
datas[j++] = oldDatas[i];
}
//front指向新數組0的位置
front = 0;
//rear指向新數組最後一個元素位置
rear = j;
}
/**出隊*/
public synchronized T deQueue() {
if(isEmpty())
return null;
T t = datas[front];
datas[front] = null;
//front指向新的隊首元素
front = (front+1) % datas.length;
size --;
return t;
}
/**雙端隊列 從隊尾出隊*/
public synchronized T deQueueRear() {
if(isEmpty())
return null;
//隊尾指針指向上一個元素位置
rear = (rear-1+datas.length)%datas.length;
T t = datas[rear];
datas[rear] = null;
size --;
return t;
}
/**清空隊列*/
public synchronized void clear() {
while(!isEmpty()){
deQueue();
}
front = rear = 0;
}
/**返回隊首元素*/
public T peek() {
if(isEmpty())
return null;
return datas[front];
}
public T getElement(int index) {
if(isEmpty() || index>=datas.length)
return null;
T t = datas[index];
return t;
}
@Override
public String toString() {
if(isEmpty())
return "[]";
StringBuffer buffer = new StringBuffer();
buffer.append("[");
int mFront = front;
while(mFront!=rear){
buffer.append(datas[mFront]+", ");
mFront = (mFront+1) % datas.length;
}
return buffer.subSequence(0, buffer.lastIndexOf(", "))+"]";
}
}
2.3 雙端隊列
如果允許在環形隊列的兩端都可以進行插入和刪除操作,這樣的隊列稱爲雙端隊列。上面的環形隊列中,只能從隊尾入隊,隊首出隊,在雙端隊列中,隊首和隊尾都能出隊入隊。相關算法如下:
/**雙端隊列 從隊首入隊*/
public synchronized boolean enQueueFront(T item) {
if(item==null)
throw new NullPointerException("item data is null");
//判斷是否滿隊
if((rear+1) % datas.length == front){
//滿隊時擴容
ensureCapacity();
}
//使隊首指針指向上一個空位
front = (front-1+datas.length)%datas.length;
//添加data
datas[front] = item;
size++;
return true;
}
/**雙端隊列 從隊尾出隊*/
public synchronized T deQueueRear() {
if(isEmpty())
return null;
//隊尾指針指向上一個元素位置
rear = (rear-1+datas.length)%datas.length;
T t = datas[rear];
datas[rear] = null;
size --;
return t;
}
2.4 隊列的鏈式存儲結構實現
鏈式存儲結構有單鏈表和雙鏈表,對於鏈隊的實現,使用單鏈表就足以滿足需求(雙鏈表由於多了前驅指針,存儲密度不如單鏈表,造成空間浪費)。下面我們使用單鏈表實現鏈隊,操作示意圖如下:
從上圖中,我們可以知道如下幾點:
- 使用front指向隊首結點,rear指向隊尾結點;
- 當隊爲空時,front = rear = null,並約定以此作爲隊列爲空的判斷條件;
- 入隊時,使當前隊尾結點的後繼指針指向新結點rear.next = newNode,然後使隊尾指針rear指向新結點
- 出隊時,刪除隊首結點,使front = front.next
- 鏈隊不會出現溢出的情況,這比使用數組實現的順序隊列更加靈活
鏈隊的分析就到這裏,接下來我們實現鏈隊的基本操作算法:
public class QueueByLink<T>{
private LNode<T> front; //隊首指針
private LNode<T> rear; //隊尾指針
private int size; //元素個數
/**獲取隊中元素個數*/
public int getSize() {
return size;
}
/**隊列是否爲空*/
public boolean isEmpty() {
return front==null && rear==null;
}
/**入隊*/
public synchronized boolean enQueue(T item) {
if(item==null)
throw new NullPointerException("item data is null");
LNode<T> newNode = new LNode();
newNode.data = item;
if (front == null) {
//向空隊中插入,需要將front指針指向第一個結點
front = newNode;
} else {
//非空隊列,隊尾結點的後繼指針指向新結點
rear.next = newNode;
}
rear = newNode; //隊尾指針指向新結點
size ++;
return true;
}
/**出隊*/
public synchronized T deQueue() {
if(isEmpty())
return null;
T t = front.data;
front = front.next;
if (front == null) //如果出隊後隊列爲空,重置rear
rear = null;
size--;
return t;
}
/**返回隊首元素*/
public T peek() {
return isEmpty()? null:front.data;
}
/**清空隊列*/
public synchronized void clear() {
while(!isEmpty()){
deQueue();
}
front = rear = null;
size = 0;
}
@Override
public String toString() {
if(isEmpty())
return "[]";
StringBuffer buffer = new StringBuffer();
buffer.append("[");
LNode mFront = front;
while(mFront!=null){
buffer.append(mFront.data+", ");
mFront = mFront.next;
}
return buffer.subSequence(0, buffer.lastIndexOf(", "))+"]";
}
}
2.5 隊列的應用
隊列的使用也是非常廣泛的,比如android中MessageQueue就是利用隊列實現的。這裏我們繼續使用1.5中迷宮破解問題示例隊列的應用。
破解迷宮
在1.5中使用棧來破解迷宮,是利用窮舉的思想,嘗試沿着一條路走下去,走不通回退,繼續嘗試下一條路,直到找到出口。通過這種方式求出的路徑不一定是最短路徑,這跟嘗試的方向有關係(上左下右),如果向上走時能一直走到出口,則會忽略掉左下右等方向的更短的路徑。
這裏我們使用隊列來求解,假設當前點位爲(x, y),在隊列中的索引爲front,遍歷該位置的四個方位,如果方位可走則入隊,並記錄這個方位元素的前驅爲front。如下圖所示,當前點位上方的點位不可走,不入隊;右方可走,入隊;下方可走入隊;左方可走入隊;然後將front++,這時候當前點位變成(x, y+1),繼續遍歷它的四個方位,淘汰掉不可走的,可走的方位都會入隊……。這樣一層一層向外擴展可走的點,所有可走的點位各個方向都會嘗試,而且機會相等,直到找到出口爲止,這個方法稱爲“廣度優先搜索方法”。然後我們從出口反向找其上一個方塊的下標,直到下標爲0,這個反向過程就能找到最短路徑。由於此處需要通過索引獲取隊列元素,所以使用順序隊列來實現,因爲鏈式存儲結構查找不方便。
當然最短路徑可能不止一條,具體求得的是那一條也是跟方位遍歷順序有關係的,這裏就不做討論。下面看看算法的實現:
public static QueueByArray<MazeElem> getMazePathByArrayQueue(int[][] maze, int startX,
int startY, int endX, int endY){
int x, y, di;
QueueByArray<MazeElem> queue = new QueueByArray();
//入口元素進隊
MazeElem elem = new MazeElem(startX, startY, 0,-1);
queue.enQueue(elem);
maze[startX][startY] = -1; //將入口元素置爲-1,避免回過來重複搜索
int front = 0; //記錄當前操作的可走方塊在隊列中的索引
//隊列不爲空且未找到路徑時循環
while(front<=queue.getRear()){
x = queue.getElement(front).x;
y = queue.getElement(front).y;
if(x == endX && y == endY){ //找到了出口
int k = front, j;
//反向找到最短路徑,將該路徑上的方塊的pre設置爲-1
do{
j = k;
k = queue.getElement(k).pre; //上一個可走方塊索引
queue.getElement(j).pre = -1; //將路徑上的元素的pre值爲-1
}while(k!=0);
//返回隊列,隊列中pre爲-1的元素,構成最短路徑
return queue;
}
for(di = 0; di<4; di++){ //循環掃描每個方位,把每個可走的方塊插入隊列中
switch (di){
case 0: //上
x = queue.getElement(front).x-1;
y = queue.getElement(front).y;
break;
case 1: //右
x = queue.getElement(front).x;
y = queue.getElement(front).y+1;
break;
case 2: //下
x = queue.getElement(front).x+1;
y = queue.getElement(front).y;
break;
case 3: //左
x = queue.getElement(front).x;
y = queue.getElement(front).y-1;
break;
}
if(x>=0 && y>=0 && x<maze.length && y<maze[0].length){
if(maze[x][y] == 0){
//將該相鄰方塊插入隊列
queue.enQueue(new MazeElem(x, y, 0, front));
maze[x][y] = -1; //賦值爲-1,避免回過來重複搜索
}
}
}
front ++;
}
return null;
}
3、優先級隊列
優先級隊列是一種特殊的隊列,其元素具有優先級別,在遵循普通隊列“先進先出”原則的同時,還有優先級高的元素先出隊的調度機制。比如操作系統中進程調度管理就是利用優先隊列的思想,在同級進程中按照“先來先處理”的原則,對於某些高優先級別的特殊進程則可插隊,優先級高的先處理。這就像火車站取票一樣,首先得排隊,先來的先取票,但是某些人的票離發車時間很近了,他的緊急程度很高,即使來的晚,他們可以說說好話插個隊。
優先級隊列可分爲“降序優先級隊列”和“升序優先級隊列”。“降序優先級隊列”優先級高的先出隊;“升序優先級隊列”則是優先級底的先出隊。像進程調度管理、火車站取票等都是降序優先級隊列,一般情況下我們所說的優先隊列都是指降序優先級隊列。
優先級隊列的實現和普通隊列一樣,可以使用順序存儲結構實現,也可以使用鏈式存儲結構實現等等。我們只需要簡單的修改普通隊列的算法就能實現優先級隊列,比如在入隊的時候,將新元素插入到對應優先級的位置;或者在出隊的時候,查找隊列中優先級高的先出。對比兩種方案,發現使用第一種方案入隊時就將結點插入到合適的位置更加方便,由於需要插入,採用鏈式存儲結構將會更加方便。下面我們簡單的利用鏈式存儲結構實現優先級隊列。
/**
* autour : openXu
* date : 2018/7/31 10:03
* className : PriorityQueue
* version : 1.0
* description : 優先級隊列
*
* ———————————————
* front(隊首)<<< 高 (優先級) 低 <<< (隊尾)rear
* ———————————————
*
*/
public class PriorityQueue<T extends Comparable<T>> extends DLinkList<T> {
private ODER_TYPE oderType = ODER_TYPE.desc; //默認降序優先隊列
public enum ODER_TYPE{
desc, //降序
asc, //升序
}
public PriorityQueue(ODER_TYPE oderType) {
this.oderType = oderType;
}
/**入隊*/
public synchronized boolean enQueue(T item) {
if(item==null)
throw new NullPointerException("item data is null");
DNode<T> newNode = new DNode<>();
newNode.data = item;
//如果隊列爲空,或者添加的元素優先級比隊尾元素還低,則直接添加到隊尾
if(isEmpty() || item.compareTo(last.data) <= 0) {
if (last == null) //空表
first = newNode;
else {
last.next = newNode;
newNode.prior = last;
}
last = newNode;
size++;
return true;
}
DNode<T> p = first;
//查找插入點 , p爲從隊首開始第一個小於插入值的元素,需要插入到p前面
while (p != null && item.compareTo(p.data) <= 0)
p = p.next;
newNode.next = p;
newNode.prior = p.prior;
if(p.prior==null){ //p爲隊首結點
first = newNode;
}else{
p.prior.next = newNode;
}
p.prior = newNode;
size++;
return true;
}
/**出隊*/
public synchronized T deQueue() {
if(isEmpty())
return null;
return oderType == ODER_TYPE.desc ? remove(0):remove(length()-1);
}
/**返回隊首元素,不執行刪除操作*/
public T peek() {
return isEmpty()? null:
oderType == ODER_TYPE.desc ? get(0):get(length()-1);
}
@Override
public String toString() {
if(isEmpty())
return "[]";
StringBuffer buffer = new StringBuffer();
buffer.append("[");
DNode mFirst = first;
while(mFirst!=null){
buffer.append(mFirst.data+", ");
mFirst = mFirst.next;
}
return buffer.subSequence(0, buffer.lastIndexOf(", "))+"]";
}
}