【資料結構】堆疊、佇列的原理及java實現
一、堆是一個執行時資料區,通過new等指令建立,不需要程式程式碼顯式釋放
<1>優點:
可動態分配記憶體大小,生存週期不必事先告訴編譯器,Java垃圾回收自動回收不需要的資料;
<2>缺點:
執行時需動態分配記憶體,資料存取速度較慢。
如:
String str = new String(“abc”);
String str2 = new String(“abc”);
二、棧(stack)又名堆疊,它是一種先進後出(FILO)的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;從一個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。
<1>優點:
存取速度比堆快,僅次於暫存器,棧資料可以共享;
<2>缺點:
存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。
String str = “abc”;
String str2 = “abc”;
表的含義如下圖所示:
下面介紹兩種棧的實現方式:
1.採用連結串列中節點的方式實現:
public class MyStackNode<E> {
Node<E> top = null;
public boolean isEmpty() {
return top == null;
}
public void push(E data) {
Node<E> newNode = new Node<E>(data);
newNode.next = top;
top = newNode;
}
public E pop() {
if (this.isEmpty())
return null;
E data = top.data;
top = top.next;
return data;
}
public E peek() {
if (isEmpty()) {
return null;
}
return top.data;
}
}
2.採用陣列的方式實現:
public class MyStackArry<E > {
private Object[] stack;
private int size;
public MyStackArry() {
stack = new Object[10];
}
public boolean isEmpty() {
return size == 0;
}
public E peek() {
if (isEmpty())
return null;
return (E) stack[size - 1];// 如果有元素就返回最後一個
}
/**
* 移除棧頂部元素移除
*
* @return
*/
public E pop() {
E e = peek();// 儲存最後一個元素的備份
stack[size - 1] = null;// 給陣列最後一個元素賦null
size--;
return e;
}
/**
* 把項壓入棧頂部
*
* @param item
* @return
*/
public E push(E item) {
ensureCapacity(size + 1);
stack[size++] = item;
return item;
}
/**
* 檢查容量是否足夠,不夠再原有的陣列基礎建立新的陣列
*
* @param size
*/
public void ensureCapacity(int size) {
int len = stack.length;
if (size > len) {
// int newLen = 10;
// stack = Arrays.copyOf(stack, newLen);
// 如果棧滿,則建立空間為當前棧空間兩倍的棧
Object[] temp = stack;
stack = new Object[2 * stack.length];
System.arraycopy(temp, 0, stack, 0, temp.length);
}
}
/**
* 返回物件在堆疊中的位置,以1 為基數
*
* @param o
* @return
*/
public int search(Object o) {
int index = lastIndexOf(o);
return index == -1 ? index : size - index;
}
/**
* 查詢下標的方法
*
* @param o
* @return
*/
private int lastIndexOf(Object o) {
if (isEmpty()) {
throw new EmptyStackException(); // 如果陣列為空,就丟擲一個自定義異常
}
// 當傳進來的元素為空時
if (o == null) {
for (int i = size - 1; i >= 0; i--) {
if (stack[i] == null) {
return i;
}
}
// 不為空時
} else {
for (int i = size - 1; i >= 0; i--) {
if (o.equals(stack[i])) {
return i;
}
}
}
return -1; // 沒有找到,返回-1
}
// 自定義異常
private static class EmptyStackException extends RuntimeException {
public EmptyStackException() {
super("堆疊為空");
}
}
三、佇列 是一種先進先出的線性表。其限制僅在表的一端(尾端)進行插入,另一端(首端)進行刪除的線性表,先進先出FIFO。
Quene類尚不在java集合框架中,因此它有很大的靈活性,為了能夠進行程式碼重用,我們試著通過繼承(is-a關係)或者合成(have-a關係)一些實現了List介面的類來定義Queue類。直觀的選擇是ArrayList和LinkedList。
1.第一種實現方式:LinkedList插入刪除效率比較高,可以實現佇列的尾部插入和頭部刪除只需常量次呼叫,我們首先選擇LinkedList來實現Queue。
這裡我們採用採用合成(have-a關係)的方法,通過包含一個LinkedList欄位來定義一個Queue類。
實現方式如下:
public class MyQueueLink<E> {
private LinkedList<E> list;
public MyQueueLink() {
list = new LinkedList<E>();
}
//入隊
public void put(E e) {
list.addLast(e);
}
//出隊
public E pop() {
return list.removeFirst();
}
public boolean isEmpty() {
return list.isEmpty();
}
public int size() {
return list.size();
}
//獲得佇列的第一個元素
public E front() {
return list.getFirst();
}
}
2.第二種可選擇的實現方式,用陣列的方式實現
public class MyQueneArry {
protected Object[] data;
protected int size, head, tail;
public MyQueneArry() {
final int INTTIAN_LENGTH = 100;
data = new Object[INTTIAN_LENGTH];
size = 0;
head = 0;
tail = -1;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public Object front() {
if (size == 0)
throw new NoSuchElementException();
return data[head];
}
//入隊
public void enquene(Object element) {
if (size == data.length) {
Object[] oldData = data;
data = new Object[data.length * 2];
System.arraycopy(oldData, head, data, 0, oldData.length - head);
if (head > 0)
System.arraycopy(oldData, 0, data, head + 1, tail - 1);
head = 0;
tail = oldData.length - 1;
}
tail = (tail + 1) % data.length;
size++;
data[tail] = element;
}
//出隊
public Object dequene() {
if (size == 0)
throw new NoSuchElementException();
Object element = data[head];
head = (head + 1) % data.length;
return element;
}
public static void main(String[] args) {
MyQueneArry q = new MyQueneArry();
for (int i = 0; i < 18; i++) {
q.enquene(i);
}
System.out.println("佇列長度:" + q.size());
System.out.println("佇列首元素:" + q.dequene());
System.out.println("佇列首元素:" + q.dequene());
}
}