標籤:
從這裡開始將要進行Java資料結構的相關講解,Are you ready?Let‘s go~~
java中的資料結構模型可以分為一下幾部分:
1.線性結構
2.樹形結構
3.圖形或者網狀結構
接下來的幾張,我們將會分別講解這幾種資料結構,主要也是通過Java代碼的方式來講解相應的資料結構。
今天要講解的是:Java線性結構
Java資料結構之線性結構
說到線性結構的話,我們可以根據其實現方式分為三類:
1)順序結構的線性表
2)鏈式結構的線性表
3)棧和隊列的線性表
1、順序結構的線性表
所謂順序儲存,指的是兩個元素在物理上的儲存地址和邏輯上的儲存地址是一致的,邏輯上相鄰的兩個元素,它們在物理中儲存的地址
也是相鄰的。對於jdk中典型的應用就是List介面的實作類別ArrayList和Vector(它們兩個的區別在於是否是線程同步的)。
如果感興趣可以查看它們的原始碼,下面我們以數組為例,來模仿一下它們...
package com.yonyou.test;import java.util.Arrays;/** * 測試類別 * @author 小浩 * @建立日期 2015-3-20 */public class Test { public static void main(String[] args) {SequenceList<String> list=new SequenceList<String>();System.out.println("線性表的初始化長度為:"+list.length());list.add("Hello");list.add("World");list.add("天下太平");System.out.println("當前list中的元素為:"+list);list.remove();System.out.println("當前list中的元素為:"+list);System.out.println("當前元素線性表是否為空白:"+list.empty());} }/** * 建立一個線性鏈表 * 注意這個類是線程不安全的,在多線程下不要使用 * @author 小浩 * @建立日期 2015-3-20 * @param <T> */class SequenceList<T>{//數組的預設初始化長度private int DEFAULT_SIZE = 10;// 儲存數組的長度。private int capacity;// 定義一個數組用於儲存順序線性表的元素private Object[] elementData;// 儲存順序表中元素的當前個數private int size = 0;// 以預設數組長度建立空順序線性表public SequenceList(){capacity = DEFAULT_SIZE;elementData = new Object[capacity];}// 以一個初始化元素來建立順序線性表public SequenceList(T element){this();elementData[0] = element;size++;}/** * 以指定長度的數組來建立順序線性表 * @param element 指定順序線性表中第一個元素 * @param initSize 指定順序線性表底層數組的長度 */public SequenceList(T element , int initSize){capacity = 1;// 把capacity設為大於initSize的最小的2的n次方while (capacity < initSize){capacity <<= 1;}elementData = new Object[capacity];elementData[0] = element;size++;}/** * 擷取順序線性表的大小 * @return */public int length(){return size;}/** * 擷取順序線性表中索引為i處的元素 * @param i * @return */@SuppressWarnings("unchecked")public T get(int i){if (i < 0 || i > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}return (T)elementData[i];}/** * 尋找順序線性表中指定元素的索引 * @param element * @return */public int locate(T element){for (int i = 0 ; i < size ; i++){if (elementData[i].equals(element)){return i;}}return -1;}/** * 向順序線性表的指定位置插入一個元素。 * @param element * @param index */private void insert(T element , int index){if (index < 0 || index > size){throw new IndexOutOfBoundsException("線性表索引越界");}ensureCapacity(size + 1);// 將index處以後所有元素向後移動一格。System.arraycopy(elementData , index , elementData , index + 1 , size - index);elementData[index] = element;size++;}/** * 線上性順序表的開始處添加一個元素。 * @param element */public void add(T element){insert(element , size);}/** * 擴充底層數組長度,很麻煩,而且效能很差 * @param minCapacity */private void ensureCapacity(int minCapacity){// 如果數組的原有長度小於目前所需的長度if (minCapacity > capacity){// 不斷地將capacity * 2,直到capacity大於minCapacity為止while (capacity < minCapacity){capacity <<= 1;}elementData = Arrays.copyOf(elementData , capacity);}}/** * 刪除順序線性表中指定索引處的元素 * @param index * @return */@SuppressWarnings("unchecked")private T delete(int index){if (index < 0 || index > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}T oldValue = (T)elementData[index];int numMoved = size - index - 1;if (numMoved > 0){System.arraycopy(elementData , index+1, elementData, index , numMoved);}// 清空最後一個元素elementData[--size] = null;return oldValue;}/** * 刪除順序線性表中最後一個元素 * @return */public T remove(){return delete(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 = 0 ; i < size ; i++ ){sb.append(elementData[i].toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}}}
具體的講解這裡就不說了,還是那句話,如果感興趣可以查看相關原始碼。
2、鏈式結構的線性表
鏈式儲存結構的線性表是相對於順序結構的線性表而言的。對於鏈式儲存的線性表,其中各個元素的物理地址和邏輯地址是不確定的。
也就是說邏輯上相鄰的兩個元素,他們在物理上不一定是相鄰的。
在Java的jdk中典型的應用就是List介面下面的LinkedList(線程不安全,多線程下不要使用),如果感興趣的可以查看相關原始碼。
這裡僅僅簡單的模仿一下。
首先模仿的是單鏈表:
package com.yonyou.test;/** * 測試類別 * @author 小浩 * @建立日期 2015-3-20 */public class Test { public static void main(String[] args) {LinkList<String> list=new LinkList<String>();System.out.println("線性表的初始化長度為:"+list.length());list.add("Hello");list.add("World");list.add("天下太平");System.out.println("當前list中的元素為:"+list);list.remove();System.out.println("當前list中的元素為:"+list);System.out.println("當前元素線性表是否為空白:"+list.empty());} }/** * 建立一個鏈式線性表 * 注意這個類是線程不安全的,在多線程下不要使用 * @author 小浩 * @建立日期 2015-3-20 * @param <T> */class LinkList<T>{/**定義一個內部類Node,Node執行個體代錶鏈表的節點。 * * @author 小浩 * @建立日期 2015-3-20 */private class Node{// 儲存節點的資料private T data;// 指向下個節點的引用private Node next;// 無參數的構造器@SuppressWarnings("unused")public Node(){}// 初始化全部屬性的構造器public Node(T data , Node next){this.data = data;this.next = next;}}// 儲存該鏈表的前端節點private Node header;// 儲存該鏈表的尾節點private Node tail;//儲存該鏈表中已包含的節點數private int size;/** * 建立空鏈表 */public LinkList(){// 空鏈表,header和tail都是nullheader = null;tail = null;}/** * 以指定資料元素來建立鏈表,該鏈表只有一個元素 * @param element */public LinkList(T element){header = new Node(element , null);// 只有一個節點,header、tail都指向該節點tail = header;size++;}/** * 返回鏈表的長度 * @return */public int length(){return size;}/** * 擷取鏈式線性表中索引為index處的元素 * @param index * @return */public T get(int index){return getNodeByIndex(index).data;}// 根據索引index擷取指定位置的節點private Node getNodeByIndex(int index){if (index < 0 || index > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}// 從header節點開始Node current = header;for (int i = 0 ; i < size && current != null; i++ , current = current.next){if (i == index){return current;}}return null;}/** * 尋找鏈式線性表中指定元素的索引 * @param element * @return */public int locate(T element){// 從前端節點開始搜尋Node current = header;for (int i = 0 ; i < size && current != null; i++ , current = current.next){if (current.data.equals(element)){return i;}}return -1;} /** 向線性鏈式表的指定位置插入一個元素。 * * @param element * @param index */public void insert(T element , int index){if (index < 0 || index > size){throw new IndexOutOfBoundsException("線性表索引越界");}// 如果還是空鏈表if (header == null){add(element);}else{// 當index為0時,也就是在鏈表頭處插入if (index == 0){addAtHeader(element);}else{// 擷取插入點的前一個節點Node prev = getNodeByIndex(index - 1);// 讓prev的next指向新節點,// 讓新節點的next引用指向原來prev的下一個節點。prev.next = new Node(element , prev.next);size++;}}}/** * 採用尾插法為鏈表添加新節點。 * @param element */public void add(T element){// 如果該鏈表還是空鏈表if (header == null){header = new Node(element , null);// 只有一個節點,header、tail都指向該節點tail = header;}else{// 建立新節點Node newNode = new Node(element , null);// 讓尾節點的next指向新增的節點tail.next = newNode;// 以新節點作為新的尾節點tail = newNode;}size++;}/**採用頭插法為鏈表添加新節點。 * * @param element */private void addAtHeader(T element){// 建立新節點,讓新節點的next指向原來的header// 並以新節點作為新的headerheader = new Node(element , header);// 如果插入之前是空鏈表if (tail == null){tail = header;}size++;}/** * 刪除鏈式線性表中指定索引處的元素 * @param index * @return */public T delete(int index){if (index < 0 || index > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}Node del = null;// 如果被刪除的是header節點if (index == 0){del = header;header = header.next;}else{// 擷取刪除點的前一個節點Node prev = getNodeByIndex(index - 1);// 擷取將要被刪除的節點del = prev.next;// 讓被刪除節點的next指向被刪除節點的下一個節點。prev.next = del.next;// 將被刪除節點的next引用賦為null.del.next = null;}size--;return del.data;}/** * 刪除鏈式線性表中最後一個元素 * @return */public T remove(){return delete(size - 1);}/** * 判斷鏈式線性表是否為空白表 * @return */public boolean empty(){return size == 0;}/** * 清空線性表 */public void clear(){// header、tail賦為nullheader = null;tail = null;size = 0;}/** * 重寫toString方法 */public String toString(){// 鏈表為空白鏈表時if (empty()){return "[]";}else{StringBuilder sb = new StringBuilder("[");for (Node current = header ; current != null; current = current.next ){sb.append(current.data.toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}}}
其次模仿的是迴圈鏈表(即將鏈表的尾節點的next指向前端節點)
具體代碼就不寫了,感興趣的可以自己寫寫看,也不難...主要就是注意設定和維護下面這條語句
tail.next=header;
最後要講解的雙向鏈表的內容(相對於單鏈表而言,每個節點不僅有一個next,而且有一個指向前一個元素的pre)
具體的內容請參考下面的代碼:
package com.yonyou.test;/** * 測試類別 * @author 小浩 * @建立日期 2015-3-20 */public class Test { public static void main(String[] args) {DoubleLinkList<String> list=new DoubleLinkList<String>();System.out.println("線性表的初始化長度為:"+list.length());list.add("Hello");list.add("World");list.add("天下太平");System.out.println("當前list中的元素為:"+list);list.remove();System.out.println("當前list中的元素為:"+list);System.out.println("當前元素線性表是否為空白:"+list.empty());} }/** * 建立一個雙向鏈式線性表 * 注意這個類是線程不安全的,在多線程下不要使用 * @author 小浩 * @建立日期 2015-3-20 * @param <T> */class DoubleLinkList<T>{// 定義一個內部類Node,Node執行個體代錶鏈表的節點。private class Node{// 儲存節點的資料private T data;// 指向上個節點的引用private Node prev;// 指向下個節點的引用private Node next;// 無參數的構造器public Node(){}// 初始化全部屬性的構造器public Node(T data , Node prev , Node next){this.data = data;this.prev = prev;this.next = next;}}// 儲存該鏈表的前端節點private Node header;// 儲存該鏈表的尾節點private Node tail;// 儲存該鏈表中已包含的節點數private int size;/** * 建立空鏈表 */public DoubleLinkList(){// 空鏈表,header和tail都是nullheader = null;tail = null;}/** * 以指定資料元素來建立鏈表,該鏈表只有一個元素 * @param element */public DoubleLinkList(T element){header = new Node(element , null , null);// 只有一個節點,header、tail都指向該節點tail = header;size++;}/** * 返回鏈表的長度 * @return */public int length(){return size;}/** * 擷取鏈式線性表中索引為index處的元素 * @param index * @return */public T get(int index){return getNodeByIndex(index).data;}/**根據索引index擷取指定位置的節點 * * @param index * @return */private Node getNodeByIndex(int index){if (index < 0 || index > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}if (index <= size / 2){// 從header節點開始Node current = header;for (int i = 0 ; i <= size / 2 && current != null; i++ , current = current.next){if (i == index){return current;}}}else{// 從tail節點開始搜尋Node current = tail;for (int i = size - 1 ; i > size / 2 && current != null; i++ , current = current.prev){if (i == index){return current;}}}return null;}/** * 尋找鏈式線性表中指定元素的索引 * @param element * @return */public int locate(T element){// 從前端節點開始搜尋Node current = header;for (int i = 0 ; i < size && current != null; i++ , current = current.next){if (current.data.equals(element)){return i;}}return -1;}/** * 向線性鏈式表的指定位置插入一個元素。 * @param element * @param index */public void insert(T element , int index){if (index < 0 || index > size){throw new IndexOutOfBoundsException("線性表索引越界");}// 如果還是空鏈表if (header == null){add(element);}else{// 當index為0時,也就是在鏈表頭處插入if (index == 0){addAtHeader(element);}else{// 擷取插入點的前一個節點Node prev = getNodeByIndex(index - 1);// 擷取插入點的節點Node next = prev.next;// 讓新節點的next引用指向next節點,prev引用指向prev節點Node newNode = new Node(element , prev , next);// 讓prev的next指向新節點。prev.next = newNode;// 讓prev的下一個節點的prev指向新節點next.prev = newNode;size++;}}}/** * 採用尾插法為鏈表添加新節點。 * @param element */public void add(T element){// 如果該鏈表還是空鏈表if (header == null){header = new Node(element , null , null);// 只有一個節點,header、tail都指向該節點tail = header;}else{// 建立新節點,新節點的pre引用指向原tail節點Node newNode = new Node(element , tail , null);// 讓尾節點的next指向新增的節點tail.next = newNode;// 以新節點作為新的尾節點tail = newNode;}size++;}/** * 採用頭插法為鏈表添加新節點。 * @param element */public void addAtHeader(T element){// 建立新節點,讓新節點的next指向原來的header// 並以新節點作為新的headerheader = new Node(element , null , header);// 如果插入之前是空鏈表if (tail == null){tail = header;}size++;}/** * 刪除鏈式線性表中指定索引處的元素 * @param index * @return */public T delete(int index){if (index < 0 || index > size - 1){throw new IndexOutOfBoundsException("線性表索引越界");}Node del = null;// 如果被刪除的是header節點if (index == 0){del = header;header = header.next;// 釋放新的header節點的prev引用header.prev = null;}else{// 擷取刪除點的前一個節點Node prev = getNodeByIndex(index - 1);// 擷取將要被刪除的節點del = prev.next;// 讓被刪除節點的next指向被刪除節點的下一個節點。prev.next = del.next;// 讓被刪除節點的下一個節點的prev指向prev節點。if (del.next != null){del.next.prev = prev;}// 將被刪除節點的prev、next引用賦為null.del.prev = null;del.next = null;}size--;return del.data;}/** * 刪除鏈式線性表中最後一個元素 * @return */public T remove(){return delete(size - 1);}/** * 判斷鏈式線性表是否為空白鏈表 * @return */public boolean empty(){return size == 0;}/** * 清空線性表 */public void clear(){// 將底層數組所有元素賦為nullheader = null;tail = null;size = 0;}/** * 重寫toString方法 */public String toString(){// 鏈表為空白鏈表時if (empty()){return "[]";}else{StringBuilder sb = new StringBuilder("[");for (Node current = header ; current != null; current = current.next ){sb.append(current.data.toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}}public String reverseToString(){// 鏈表為空白鏈表時if (empty()){return "[]";}else{StringBuilder sb = new StringBuilder("[");for (Node current = tail ; current != null; current = current.prev ){sb.append(current.data.toString() + ", ");}int len = sb.length();return sb.delete(len - 2 , len).append("]").toString();}}}
由於篇幅的限制,對於線性結構中的棧和隊列的講解,請看下一篇部落格~~~
Java資料結構之線性表