1. 程式人生 > >java資料結構與演算法——棧與佇列

java資料結構與演算法——棧與佇列

本節目標:

1.掌握棧的原理與應用

2.掌握佇列的原理與應用

棧和佇列都屬於線性表

棧又稱為堆疊,是一種運算受限的線性表,這是因為它僅允許線上性表的固定一端(表尾)進行插入、刪除和讀取元素等運算,不允許在其他任何位置進行運算。

相關名詞:棧頂、棧頂元素、棧底、進棧(壓棧)、出棧(退棧)

特點:後進先出

時間複雜度:O(1)

順序儲存結構

需要一個數組和整型變數,利用陣列來儲存元素,利用整型變數儲存棧頂元素的下標,通常用top表示,也叫棧頂指標。

top=-1時表示棧空;

壓棧時,首先將top增1,用來指代新的棧頂位置,然後把新元素賦值到棧頂位置上;

退棧時,先取出棧頂元素,然後top減1;

top指向最後一個下標位置時,棧滿。若再新增元素時,需要重新分配更大的陣列空間;


/**
 * 棧的基本基本功能
 * @author root
 */
public interface Stack {
//向棧頂插入一個元素
void push(Object obj);
//刪除棧頂元素並返回
Object pop();
//返回棧頂元素
Object peek();
//是否為空
boolean isEmpty();
//清空棧內元素
void clean();

}

/**
 * 棧基本功能實現
 * @author root
 */
public class SequenceStack implements Stack {
//棧的一維陣列初始長度
final int minSize = 10;
//棧儲存元素依賴於陣列
private Object[] stackArray;
//棧頂元素位置下標(相當於陣列下標),棧頂指標
private int top ;
/**
* 建構函式
*/
SequenceStack(){
top = -1;//-1表示初始化時棧頂沒有元素
stackArray = new Object[minSize];
}

SequenceStack(int n){//指定元素儲存空間
if (n<minSize) {
n = minSize;
}
top = -1;
stackArray = new Object[n];
}

/**
* 往棧中壓入元素
*/
@Override
public void push(Object obj) {
// 1.判斷陣列空間,若已滿,則建立2倍空間陣列,複製到新空間
// 2.棧頂指標+1
// 3.新元素寫到新棧頂位置
if (top == this.stackArray.length-1) {
Object[] b = new Object[2*top];
for (int i = 0; i <= top; i++) {
b[i] = this.stackArray[i];
}
stackArray = b;

}
top++;
stackArray[top] = obj;
}

/**
* 彈出棧頂元素
*/
@Override
public Object pop() {
//1.判斷棧是否為空,空時返回null
//2.棧頂指標-1
//3.返回棧頂元素

if (top == -1) {
return null;
}
top--;
return this.stackArray[top+1];
}

/**
* 檢視棧頂元素
*/
@Override
public Object peek() {
if (top == -1) {
return null;
}
return stackArray[top];
}

/**
* 棧是否為空
*/
@Override
public boolean isEmpty() {
return top == -1;
}
/**
* 清空棧
*/
@Override
public void clean() {
this.top = -1;
}

}

/**
 * 測試棧的基本功能
 * @author root
 */
public class SequenceStackTest {
public static void main(String[] args) {
Stack stack = new SequenceStack();
int[] a = {2,4,23,3,5};
for (int i = 0; i < a.length; i++) {
stack.push(a[i]);
}
System.out.println("stack.pop:"+stack.pop());//5
stack.push(1);
System.out.println(stack.peek());//1
stack.clean();
System.out.println(stack.isEmpty());//true
}

}

鏈式儲存結構

由結點構成的單鏈表實現,也成為鏈棧,即連結儲存的棧。表頭指標成為棧頂指標,由棧頂節點指向的元素結點稱為棧頂結點。

壓棧時,使新元素的結點指標域指向原先的棧頂結點,棧頂指標指向新元素結點;

退棧時,棧頂指標指向後繼結點;

空棧:top為null


/**
 * 節點類
 * @author root
 */
public class Node {
Object element;//值域
Node next;//指標域

Node(Node next){
this.next = next;
}
Node(Object element,Node next){
this.element = element;
this.next = next;
}

}

/**
 * 連結串列結構的棧
 * @author root
 *
 */
public class LinkStack implements Stack{
private Node top;//棧頂指標

LinkStack(){
this.top = null;//初始化為空棧
}

@Override
public void push(Object obj) {
top = new Node(obj, top);//相當於在單鏈表表頭插入結點

}

/**
* 彈出棧頂元素
*/
@Override
public Object pop() {
if (top == null) {
return null;
}
Node p = top;    //將棧頂引用儲存到p中
top = top.next;//棧頂指標指向下一個結點
return p.element;//彈出原先棧頂元素
}

@Override
public Object peek() {
if (top == null) {
return null;
}

return top.element;//返回棧頂元素的值
}

@Override
public boolean isEmpty() {
return top == null;
}

@Override
public void clean() {
top = null;
}
}

佇列

佇列簡稱隊,也是運算受限的線性表,僅允許在表的一段插入,另一端刪除。允許插入的一端做隊尾,進行刪除的一端做隊首。

相關名詞:進隊、出隊

特點:先進先出(FIFO)

時間複雜度:O(1)

順序儲存結構

需要一個數組和兩三個變數實現。陣列用來儲存所有元素;需要一個變數儲存隊首位置(通常儲存隊首元素的前一個位置),也即隊首指標;需要一個變數儲存隊尾位置,也即隊尾指標;或者再新增一個變數儲存元素個數。

進隊:先將隊尾指標後移一個位置,然後向這個位置寫入新元素。如果隊尾指標指向陣列空間的最後一個位置時,若隊首元素的前面仍有空閒位置,則表明佇列沒有佔滿整個陣列空間,那麼下一個儲存位置為下標為0的空閒位置,因此,要先將隊尾指標指向下標為0的位置,然後寫入。

通過this.rear= (this.rear+1)%this.queueArray.length可以使儲存佇列的整個陣列空間變為首尾相接的環,所以順序儲存的佇列又稱為迴圈佇列。在迴圈佇列中,儲存空間是首尾相接的,當rear指向最後一個儲存位置的時候,下一個位置自動轉為陣列空間的開始位置(即下標為0的位置)。

出隊:如果佇列不空,先將隊首指標後移,然後返回該元素的值。隊首後移也要採用取模運算,this.front= (this.front+1)%this.queueArray.length,實現首尾相接,迴圈利用。

佇列已滿的條件是佇列長度為this.queueArray.length-1,也就是說必須有一個位置是空閒的。這是因為如果使用全部的陣列空間,當隊首和隊尾指向同一個位置時,可能是空隊,也可能是滿隊。利用隊尾指標加1並對this.queueArray.length取模後是否等於隊首指標作為判斷滿隊的條件。


**

 * 佇列的基本功能
 * @author root
 *
 */
public interface Queue {
//進隊,在隊尾部插入新元素
void enter(Object obj);
//出隊,從隊首刪除並返回對首元素
Object leave();
//返回隊首元素
Object peek();
//佇列是否為空
boolean isEmpty();
//清空佇列
void clean();

}

/**
 * 順序佇列
 * @author root
 *
 */
public class SequenceQueue implements Queue {
//儲存佇列的一維陣列最小空間
final int minSize = 10;
//儲存佇列的陣列空間
private Object[] queueArray;
private int front;//隊首指標
private int rear;//隊尾指標
/**
* 建構函式
*/
SequenceQueue(){
this.front =0;
this.rear = 0;
queueArray = new Object[minSize];
}
SequenceQueue(int n){
this.front = 0;
this.rear = 0;
if (n<=minSize) {
n = minSize;
}
queueArray = new Object[n];
}


@Override
public void enter(Object obj) {
// 佇列已滿(有兩種情形),分配雙倍空間,並複製到新空間陣列內
if ((this.rear+1)%this.queueArray.length == this.front) {
Object[] p = new Object[2*this.queueArray.length];
//已滿,隊尾指標在後
if (this.rear == this.queueArray.length-1) {
for (int i = 1; i <=this.rear; i++) {
p[i] = this.queueArray[i];
}
}else{
//已滿,隊尾指標在前
//先複製佇列前面元素,後複製佇列後部分元素
int k = 1;
for (int i = this.front+1; i < this.queueArray.length-1; i++,k++) {
p[k] = this.queueArray[i];
}
for (int j = 0; j <= this.rear; j++,k++) {
p[k] = this.queueArray[j];
}
this.front = 0;
this.rear = this.queueArray.length-1; 
}
queueArray=p;
}
this.queueArray[(this.rear+1)%this.queueArray.length] = obj;
}

@Override
public Object leave() {
if (this.front == this.rear) {
return null;
}
this.front = (this.front+1)%this.queueArray.length;//將隊首指標指向下一個隊首元素位置
return queueArray[this.front];
}


@Override
public Object peek() {
if(this.front == this.rear){
return null;
}
 return queueArray[(this.front+1)%this.queueArray.length];
}


@Override
public boolean isEmpty() {
return this.front == this.rear;
}


@Override
public void clean() {
this.front = this.rear = 0;


}

}

鏈式儲存結構


/**
 * 節點類
 * @author root
 */
public class Node {
 Object element;//值域
 Node next;//指標域

Node(Node next){
this.next = next;
}
Node(Object element,Node next){
this.element = element;
this.next = next;
}

}

/**
 * 鏈式佇列
 * @author root
 */
public class LinkQueue implements Queue {

private Node front;
private Node rear;
LinkQueue(){
this.front = this.rear = null;
}

@Override
public void enter(Object obj) {
if (this.rear == null) {
this.front = this.rear = new Node(obj, null);
}else{
this.rear = this.rear.next = new Node(obj, null);
}
}


@Override
public Object leave() {
if (this.front == null) {
return null;
}
Node x = this.front;
this.front = this.front.next;
if (this.front == null) {
this.rear = null;
}
return x.element;
}


@Override
public Object peek() {
if (this.front == null) {
return null;
}
return this.front.element;
}


@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return this.front == null;
}


@Override
public void clean() {
this.front = this.rear = null;
}
}