堆棧
堆棧是一種特殊的線性表,堆棧的數據元素以及數據元素間的邏輯關系和線性表完全相同,其差別是線性表允許在任意位置進行插入和刪除操作,而堆棧只允許在固定一端進行插入和刪除操作。因此也被成為 LIFO(Last In, First Out, 后進先出),
堆棧中允許進行插入和刪除操作的一端稱為棧頂,另一端稱為棧底。堆棧的插入和刪除操作通常稱為進棧或入棧,堆棧的刪除操作通常稱為出?;蛲藯!?/p>
常見的實現方式包括數組實現,容器實現和鏈表實現。
基本算法
- 進棧(PUSH)算法
- 若 TOP > n 時,則給出溢出信息,作出錯處理(進棧前首先檢查棧是否已滿,滿則溢出;不滿則繼續)
- 置 TOP=TOP+1 (棧指針加 1,指向進棧地址)
- S(TOP)=X,結束( X 為新進棧的元素)
- 退棧(POP)算法
- 若TOP < 0,則給出下溢信息,作出錯處理(退棧前先檢查是否已為空棧, 空則下溢;不空則作②)
- X=S(TOP),(退棧后的元素賦給X)
- TOP=TOP-1,結束(棧指針減1,指向棧頂)
順序棧
順序存儲結構的堆棧稱作順序堆棧。其存儲結構示意圖如下圖所示:
數組實現
public class ArrayStack {
private int[] stack;
private int maxSize;
private int top;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
top = -1;
}
public void push(int data) {
if (isFull()) {
throw new StackOverflowError();
}
// 先讓 top + 1 后賦值
stack[++top] = data;
}
public int pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
// 先返回值,再 top - 1
return stack[top--];
}
public int peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack[top];
}
public boolean isEmpty() {
return top == -1;
}
public boolean isFull() {
return top == maxSize;
}
}
容器實現
public class ListStack<T> {
private List<T> stack;
public ListStack() {
stack = new ArrayList<>();
}
public boolean isEmpty() {
return stack.size() == 0;
}
public void push(T data) {
stack.add(data);
}
public T pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack.remove(stack.size() - 1);
}
public T peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return stack.get(stack.size() - 1);
}
}
鏈式堆棧
鏈式存儲結構的堆棧稱作鏈式堆棧。
與單鏈表相同,鏈式堆棧也是由一個個結點組成的,每個結點由兩個域組成,一個是存放數據元素的數據元素域 data,另一個是存放指向下一個結點的對象引用(即指針)域 next。
堆棧有兩端,插入數據元素和刪除數據元素的一端為棧頂,另一端為棧底。鏈式堆棧都設計成把靠近堆棧頭head的一端定義為棧頂。
依次向鏈式堆棧入棧數據元素a0, a1, a2, ..., an-1后,鏈式堆棧的示意圖如下圖所示:
public class LinkedStack<T> {
class Node {
T data;
Node next;
public Node() {
}
public Node(T data) {
this.data = data;
}
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
private Node top;
public void push(T data) {
top = new Node(data, top);
}
public T pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
T data = top.data;
top = top.next;
return data;
}
public T peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return top.data;
}
public boolean isEmpty() {
return null == top;
}
}
Java 中棧與堆的區別
棧(stack):(線程私有)
是一個先進后出的數據結構,通常用于保存方法(函數)中的參數,局部變量。在java中,所有基本類型和引用類型的引用都在棧中存儲。棧中數據的生存空間一般在當前scopes內(就是由{...}括起來的區域)。
堆(heap):(線程共享)
是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護),C中的malloc語句所產生的內存空間就在堆中。在java中,所有使用new xxx()構造出來的對象都在堆中存儲,當垃圾回收器檢測到某對象未被引用,則自動銷毀該對象。所以,理論上說java中對象的生存空間是沒有限制的,只要有引用類型指向它,則它就可以在任意地方被使用。
hashCode 與對象之間的關系
如果兩個對象的hashCode不相同,那么這兩個對象肯定也不同。
如果兩個對象的hashCode相同,那么這兩個對象有可能相同,也有可能不同。
總結一句:不同的對象可能會有相同的hashCode;但是如果hashCode不同,那肯定不是同一個對象。
public class StringTest {
public static void main(String[] args) {
//s1 和 s2 其實是同一個對象。對象的引用存放在棧中,對象存放在方法區的字符串常量池
String s1 = "china";
String s2 = "china";
//凡是用new關鍵創建的對象,都是在堆內存中分配空間。
String s3 = new String("china");
//凡是new出來的對象,絕對是不同的兩個對象。
String s4 = new String("china");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3);
System.out.println(s3 == s4);
System.out.println(s3.equals(s4));
System.out.println("\n-----------------\n");
/*String很特殊,重寫從父類繼承過來的hashCode方法,使得兩個
*如果字符串里面的內容相等,那么hashCode也相等。
**/
//hashCode相同
System.out.println(s3.hashCode()); //hashCode為94631255
System.out.println(s4.hashCode()); //hashCode為94631255
//identityHashCode方法用于獲取原始的hashCode
//如果原始的hashCode不同,表明確實是不同的對象
//原始hashCode不同
System.out.println(System.identityHashCode(s3)); //2104928456
System.out.println(System.identityHashCode(s4)); //2034442961
System.out.println("\n-----------------\n");
//hashCode相同
System.out.println(s1.hashCode()); //94631255
System.out.println(s2.hashCode()); //94631255
//原始hashCode相同:表明確實是同一個對象
System.out.println(System.identityHashCode(s1)); //648217993
System.out.println(System.identityHashCode(s2)); //648217993
}
}
上面的代碼中,注釋已經標明了運行的結果。通過運行結果我們可以看到,s3和s4的字符串內容相同,但他們是兩個不同的對象,由于String類重寫了hashCode方法,他們的hashCode相同,但原始的hashCode是不同的。