從這里開始將要進行Java數據結構的相關講解,Are you ready?Let's go~~
java中的數據結構模型可以分為一下幾部分:
1.線性結構
2.樹形結構
3.圖形或者網狀結構
接下來的幾張,我們將會分別講解這幾種數據結構,主要也是通過Java代碼的方式來講解相應的數據結構。
今天要講解的是:Java線性結構
Java數據結構之線性結構
說到線性結構的話,我們可以根據其實現方式分為兩類:
1)順序結構的線性表
2)鏈式結構的線性表
3)棧和隊列的線性表
對于1)和2)的講解,請參考下面的地址:http://www.cnblogs.com/xiohao/p/4353910.html
下面主要講解線性結構中的棧和隊列。
? 1.線性結構之棧的講解
??? 所謂棧是一種特殊的線性結構,它的特點在于只允許我們在線性表的尾端進行insert和remove操作。可以理解為是一種受限的
??? 線性表。往線性表中加入一個元素我們稱為入棧,從線性表中移除一個元素我們稱為出棧。實在不懂,百度一下你就知道了。
??? 在Java的jdk中的實現以Stack(底層繼承的是Vector類)和LinkedList(里面同樣實現了push,pop,peek等操作)為主,還是那句話,感興趣的
??? 自己查看源代碼即可。
??? 下面我們進行相關模仿,
??? 首先通過數組來模仿入棧和出棧操作:
????
package com.yonyou.test;import java.util.Arrays;/*** 測試類* @author 小浩* @創建日期 2015-3-20*/
public class Test{ public static void main(String[] args) {SequenceStack<String> stack=new SequenceStack<String>();System.out.println("順序棧的初始化長度為:"+stack.length());stack.push("Hello");stack.push("World");stack.push("天下太平");System.out.println("當前stack中的元素為:"+stack);System.out.println("當前stack.peek();中的元素為:"+stack.peek());System.out.println("當前元素線性表是否為空:"+stack.empty());}}/*** 創建一個線性棧* 注意這個類是線程不安全的,在多線程下不要使用* @author 小浩* @創建日期 2015-3-20* @param <T>*/
class SequenceStack<T>
{//線性棧的默認長度為10private 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];}/*** 以一個初始化元素來創建順序棧* @param element*/public SequenceStack(T element){this();elementData[0] = element;size++;}/*** 以指定長度的數組來創建順序棧* @param element 指定順序棧中第一個元素* @param initSize 指定順序棧底層數組的長度*/public SequenceStack(T element , int initSize){this.capacity = initSize;elementData = new Object[capacity];elementData[0] = element;size++;}/*** 以指定長度的數組來創建順序棧* @param element 指定順序棧中第一個元素* @param initSize 指定順序棧底層數組的長度* @param capacityIncrement 指定當順序棧的底層數組的長度不夠時,底層數組每次增加的長度*/public SequenceStack(T element , int initSize, int capacityIncrement){this.capacity = initSize;this.capacityIncrement = capacityIncrement;elementData = new Object[capacity];elementData[0] = element;size++;}/*** 獲取順序棧的大小* @return*/public int length(){return size;}/*** 入棧* @param element*/public void push(T element){ensureCapacity(size + 1);elementData[size++] = element;}/*** 很麻煩,而且性能很差* @param minCapacity*/private void ensureCapacity(int minCapacity){// 如果數組的原有長度小于目前所需的長度if (minCapacity > capacity){if (capacityIncrement > 0){while (capacity < minCapacity){// 不斷地將capacity長度加capacityIncrement,// 直到capacity大于minCapacity為止capacity += capacityIncrement;}}else{// 不斷地將capacity * 2,直到capacity大于minCapacity為止while (capacity < minCapacity){capacity <<= 1;}}elementData = Arrays.copyOf(elementData , capacity);}}/*** 出棧* @return*/@SuppressWarnings("unchecked")public T pop(){T oldValue = (T)elementData[size - 1];// 釋放棧頂元素elementData[--size] = null;return oldValue;}/*** 返回棧頂元素,但不刪除棧頂元素* @return*/@SuppressWarnings("unchecked")public T peek(){return (T)elementData[size - 1];}/*** 判斷順序棧是否為空棧* @return*/public boolean empty(){return size == 0;}/*** 清空順序棧*/public void clear(){// 將底層數組所有元素賦為nullArrays.fill(elementData , null);size = 0;}/*** 重寫toString*/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.delete(len - 2 , len).append("]").toString();}}
}
?
?? 其次通過鏈式存儲來模仿入棧和出棧操作,具體內容可以看下面的代碼:
??
package com.yonyou.test;/*** 測試類* @author 小浩* @創建日期 2015-3-20*/
public class Test{ public static void main(String[] args) {LinkStack<String> stack=new LinkStack<String>();System.out.println("順序棧的初始化長度為:"+stack.length());stack.push("Hello");stack.push("World");stack.push("天下太平");System.out.println("當前stack中的元素為:"+stack);System.out.println("當前stack.peek();中的元素為:"+stack.peek());System.out.println("當前元素線性表是否為空:"+stack.empty());}}/*** 創建一個鏈式存儲的線性棧* 注意這個類是線程不安全的,在多線程下不要使用* @author 小浩* @創建日期 2015-3-20* @param <T>*/
class LinkStack<T>
{// 定義一個內部類Node,Node實例代表鏈棧的節點。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 top;// 保存該鏈棧中已包含的節點數private int size;/*** 創建空鏈棧*/public LinkStack(){// 空鏈棧,top的值為nulltop = null;}/*** 以指定數據元素來創建鏈棧,該鏈棧只有一個元素* @param element*/public LinkStack(T element){top = new Node(element , null);size++;}/*** 返回鏈棧的長度* @return*/public int length(){return size;}/*** 進棧* @param element*/public void push(T element){// 讓top指向新創建的元素,新元素的next引用指向原來的棧頂元素top = new Node(element , top);size++;}/*** 出棧* @return*/public T pop(){Node oldTop = top;// 讓top引用指向原棧頂元素的下一個元素top = top.next;// 釋放原棧頂元素的next引用oldTop.next = null;size--;return oldTop.data;}/*** 訪問棧頂元素,但不刪除棧頂元素* @return*/public T peek(){return top.data;}/*** 判斷鏈棧是否為空棧* @return*/public boolean empty(){return size == 0;}/*** 清空鏈棧*/public void clear(){// 將底層數組所有元素賦為nulltop = null;size = 0;}/*** 重寫toString方法*/public String toString(){// 鏈棧為空鏈棧時if (empty()){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.delete(len - 2 , len).append("]").toString();}}
}
?
? 2.線性結構之隊列的講解
??? 隊列也是一種被限制過的線性結構,它使用固定的一端來插入元素(隊尾),在另一端刪除相關的元素(隊頭)。
??? 其基本特征為“先進先出”,而棧的基本特點為“先進后出”。
??? 在Java的jdk中主要的實現類為Dueue接口的實現類ArrayDeque(線性)和LinkedList(鏈式),
??? 其中Dueue接口是一個雙端隊列,它繼承了隊列根接口Queue,同時Queue接口有實現隊列的6個根本方法
???
? | 拋出異常的版本 | 不拋出異常的版本(返回null) |
插入 | 1、 boolean add(E e); | ?2、 boolean offer(E e); |
移除 | 3、 E remove(); | ?4、 E poll(); |
訪問 | 5、 E element(); | ?6、 E peek(); |
???
???
????如果感興趣的話,請查相關的源代碼。
??? 首先講解的是隊列的順序存儲:
??? 具體內容請看相關代碼:
???
package com.yonyou.test;import java.util.Arrays;/*** 測試類* @author 小浩* @創建日期 2015-3-20*/
public class Test{ public static void main(String[] args) {SequenceQueue<String> queue=new SequenceQueue<String>();System.out.println("隊列的初始化長度為:"+queue.length());queue.add("Hello");queue.add("World");queue.add("天下太平");System.out.println("當前stack中的元素為:"+queue);queue.remove();System.out.println("當前stack中的元素為:"+queue);System.out.println("當前元素線性表是否為空:"+queue.empty());}}/*** 創建一個存儲的線性隊列* 注意這個類是線程不安全的,在多線程下不要使用* @author 小浩* @創建日期 2015-3-20* @param <T>*/
class SequenceQueue<T>
{//線性隊列的默認長度private int DEFAULT_SIZE = 16;// 保存數組的長度。private int capacity;// 定義一個數組用于保存順序隊列的元素private Object[] elementData;// 保存順序隊列中元素的當前個數private int front = 0;private int rear = 0;/*** 以默認數組長度創建空順序隊列*/public SequenceQueue(){capacity = DEFAULT_SIZE;elementData = new Object[capacity];}/*** 以一個初始化元素來創建順序隊列* @param element*/public SequenceQueue(T element){this();elementData[0] = element;rear++;}/*** 以指定長度的數組來創建順序隊列* @param element 指定順序隊列中第一個元素* @param initSize 指定順序隊列底層數組的長度*/public SequenceQueue(T element , int initSize){this.capacity = initSize;elementData = new Object[capacity];elementData[0] = element;rear++;}/*** 獲取順序隊列的大小* @return*/public int length(){return rear - front;}/*** 插入隊列* @param element*/public void add(T element){if (rear > capacity - 1){throw new IndexOutOfBoundsException("隊列已滿的異常");}elementData[rear++] = element;}/*** 移出隊列* @return*/@SuppressWarnings("unchecked")public T remove(){if (empty()){throw new IndexOutOfBoundsException("空隊列異常");}// 保留隊列的front端的元素的值T oldValue = (T)elementData[front];// 釋放隊列的front端的元素elementData[front++] = null;return oldValue;}/*** 返回隊列頂元素,但不刪除隊列頂元素* @return*/@SuppressWarnings("unchecked")public T element(){if (empty()){throw new IndexOutOfBoundsException("空隊列異常");}return (T)elementData[front];}/*** 判斷順序隊列是否為空隊列* @return*/public boolean empty(){return rear == front;}/**清空順序隊列* */public void clear(){//將底層數組所有元素賦為nullArrays.fill(elementData , null);front = 0;rear = 0;}/*** 重寫toString方法*/public String toString(){if (empty()){return "[]";}else{StringBuilder sb = new StringBuilder("[");for (int i = front ; i < rear ; i++ ){sb.append(elementData[i].toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}}
}
?
其次講解的是隊列的線性存儲的循環組成:
???? 對于上面的非循環存儲可能會非常大的浪費空間,下面我們將要創建一個對應的循環鏈表的概念,這樣的話可能會有效的節約相應的空間。
???? 因為循環鏈表可以有效的消除假滿的現象哦。
?????廢話不在多說,請看代碼:
?????
package com.yonyou.test;import java.util.Arrays;/*** 測試類* @author 小浩* @創建日期 2015-3-20*/
public class Test{ public static void main(String[] args) {LoopQueue<String> queue=new LoopQueue<String>();System.out.println("隊列的初始化長度為:"+queue.length());queue.add("Hello");queue.add("World");queue.add("天下太平");System.out.println("當前stack中的元素為:"+queue);queue.remove();System.out.println("當前stack中的元素為:"+queue);System.out.println("當前元素線性表是否為空:"+queue.empty());}}/*** 創建一個順序存儲的循環線性隊列* 注意這個類是線程不安全的,在多線程下不要使用* @author 小浩* @創建日期 2015-3-20* @param <T>*/
class LoopQueue<T>
{//循環隊列的默認長度為16private int DEFAULT_SIZE = 16;// 保存數組的長度。private int capacity;// 定義一個數組用于保存循環隊列的元素private Object[] elementData;// 保存循環隊列中元素的當前個數private int front = 0;private int rear = 0;/*** 以默認數組長度創建空循環隊列*/public LoopQueue(){capacity = DEFAULT_SIZE;elementData = new Object[capacity];}/*** 以一個初始化元素來創建循環隊列* @param element*/public LoopQueue(T element){this();elementData[0] = element;rear++;}/*** 以指定長度的數組來創建循環隊列* @param element 指定循環隊列中第一個元素* @param initSize 指定循環隊列底層數組的長度*/public LoopQueue(T element , int initSize){this.capacity = initSize;elementData = new Object[capacity];elementData[0] = element;rear++;}/*** 獲取循環隊列的大小* @return*/public int length(){if (empty()){return 0;}return rear > front ? rear - front: capacity - (front - rear);}/*** 插入隊列* @param element*/public void add(T element){if (rear == front&& elementData[front] != null){throw new IndexOutOfBoundsException("隊列已滿的異常");}elementData[rear++] = element;// 如果rear已經到頭,那就轉頭rear = rear == capacity ? 0 : rear;}/*** 移出隊列* @return*/@SuppressWarnings("unchecked")public T remove(){if (empty()){throw new IndexOutOfBoundsException("空隊列異常");}// 保留隊列的front端的元素的值T oldValue = (T)elementData[front];// 釋放隊列的front端的元素elementData[front++] = null;// 如果front已經到頭,那就轉頭front = front == capacity ? 0 : front;return oldValue;}/*** 返回隊列頂元素,但不刪除隊列頂元素* @return*/@SuppressWarnings("unchecked")public T element(){if (empty()){throw new IndexOutOfBoundsException("空隊列異常");}return (T)elementData[front];}/*** 判斷循環隊列是否為空隊列* @return*/public boolean empty(){//rear==front且rear處的元素為nullreturn rear == front&& elementData[rear] == null;}/*** 清空循環隊列*/public void clear(){// 將底層數組所有元素賦為nullArrays.fill(elementData , null);front = 0;rear = 0;}/*** 重寫toString方法*/public String toString(){if (empty()){return "[]";}else{// 如果front < rear,有效元素就是front到rear之間的元素if (front < rear){StringBuilder sb = new StringBuilder("[");for (int i = front ; i < rear ; i++ ){sb.append(elementData[i].toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}// 如果front >= rear,有效元素為front->capacity之間、// 和0->front之間的元素else{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.delete(len - 2 , len).append("]").toString();}}}
}
?
最后要說的隊列的鏈式存儲,具體的實現方式還是請看代碼吧。
??????
package com.yonyou.test;import java.util.Arrays;/*** 測試類* @author 小浩* @創建日期 2015-3-20*/
public class Test{ public static void main(String[] args) {LinkQueue<String> queue=new LinkQueue<String>();System.out.println("隊列的初始化長度為:"+queue.length());queue.add("Hello");queue.add("World");queue.add("天下太平");System.out.println("當前stack中的元素為:"+queue);queue.remove();System.out.println("當前stack中的元素為:"+queue);System.out.println("當前元素線性表是否為空:"+queue.empty());}}/*** 創建一個鏈式存儲的線性隊列* 注意這個類是線程不安全的,在多線程下不要使用* @author 小浩* @創建日期 2015-3-20* @param <T>*/
//定義一個內部類Node,Node實例代表鏈隊列的節點。
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和rear都是nullfront = null;rear = null;}/*** 以指定數據元素來創建鏈隊列,該鏈隊列只有一個元素* @param element*/public LinkQueue(T element){front = new Node(element , null);// 只有一個節點,front、rear都指向該節點rear = front;size++;}/*** 返回鏈隊列的長度* @return*/public int length(){return size;}/*** 將新元素加入隊列* @param element*/public void add(T element){// 如果該鏈隊列還是空鏈隊列if (front == null){front = new Node(element , null);// 只有一個節點,front、rear都指向該節點rear = front;}else{// 創建新節點Node newNode = new Node(element , null);// 讓尾節點的next指向新增的節點rear.next = newNode;// 以新節點作為新的尾節點rear = newNode;}size++;}/*** 刪除隊列front端的元素* @return*/public T remove(){Node oldFront = front;front = front.next;oldFront.next = null;size--;return oldFront.data;}/*** 訪問鏈式隊列中最后一個元素* @return*/public T element(){return rear.data;}/*** 判斷鏈式隊列是否為空隊列* @return*/public boolean empty(){return size == 0;}/*** 清空鏈隊列*/public void clear(){// 將front、rear兩個節點賦為nullfront = null;rear = null;size = 0;}/*** 重寫toString方法*/public String toString(){// 鏈隊列為空鏈隊列時if (empty()){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.delete(len - 2 , len).append("]").toString();}}
}
??
?? 這里還是補充一下吧,除了以上介紹的隊列外,我們還可以經用到的一個隊列是雙端隊列。
? ?所謂雙端隊列指的是我們可以在隊列的兩端進行插入和刪除操作。如果我們只允許在隊列的一端進行插入和刪除的操作,那么隊列也就成為
?? 我們之前看到的棧了,是不是很有意思,沒錯就是這樣的。其實棧,隊列其本質都是一種受限制的線性表,只好不過限制的情況不同而已。
?? 還需要說的jdk中Deque接口就是一個雙端隊列的實用接口。它可以理解為Queue和Stack的一個中和體。雖然上面的棧提到了類Stack,
?? 但是現在已經不推薦使用了,一般情況,我們應該使用Deque,因為它的功能更加強大。
?? 在jdk中雙端隊列接口Deque有兩個實現類ArrayDeque(順序存儲的雙端隊列)和LinkedList(鏈式存儲的雙端隊列)
? 是不是發現了LinkedList的功能太強大了。沒錯,它就是這么任性,沒辦法。
? 下面的看一下它的部分源代碼:
?
* @since 1.2* @param <E> the type of elements held in this collection*/public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
?
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
???? 看到紅色字體了了吧,剩下你懂的~~
??? 好吧,今天就先到這里吧~~~
?
?
?
??????
????
?
???
?
??
?
???
???
???
???