算法是什么(二)手寫個鏈表(java)
liuyuhang原創,未經允許禁止轉載
目錄
很多語言的API中都提供了鏈表實現,或者擴展庫中實現了鏈表。
但是更多的情況下,Map(或hash)和List(非定容數組)的使用率更高。
這並非意味着鏈表不應該掌握或不使用了。
鏈表本質上是一種及其高等的數據結構展現,擴展性極強。
鏈表可輕松擴展成樹結構,二叉樹,環,棧,隊列,雙向隊列等。
很多種數據結構都是依據鏈表的形式擴展出來的,雖然我知道的並不多,但是我知道鏈表的重要性。
所以,手寫一個鏈表試試。
1、本質
鏈表的本質是Node(節點),其中保存着信息(info),前一個節點(prevNode),后一個節點(nextNode)。
和基礎操作API構成
2、特性
鏈表為了操作的方便性,多數會將鏈表做成雙向鏈表,即既包含next,又包含prev
3、基礎API
鏈表的操作,至少需要增刪改查,不然還算啥容器了:
增:默認從末尾添加,添加指定index,在首添加三種方式添加單一元素。
刪:刪除頭,刪除尾,刪除指定index的元素,刪除當前指針元素。
改:設置頭,設置尾,設置指定index的元素,設置當前指針元素。
查:獲取當前指針元素,獲取首元素,獲取尾元素,獲取下一個元素,獲取上一個元素,獲取指定index的元素
有些API還提供了更多的操作:
獲取當前容器的size
獲取當前指針的index
迭代器
排序工具
判空
克隆
轉數組
逆轉
去重復
序列化
等等。。。
鏈表可做的操作會比想象的多的多。
4、Java中的鏈表
Java中常用的鏈表是LinkedList,實現了List接口,繼承AbstractLinkedList。同時還有其他接口
同時,還有Queue大類,並非在Collection接口下,但是底層有些是使用鏈表實現的,功能有些是重復的。
對於需要作為原子操作的各種功能的隊列來說,可以考慮。
在擴展Java中的鏈表的時候,有幾種方式供選擇:
①繼承LinkedList,添加擴展算法;
②實現List,繼承AbstractLinkedList,同時擴展算法;
③使用裝飾模式,在構造器中調用鏈表的構造器,同時擴展算法;
④不受約束自己寫一個吧。。。
5、寫一個簡單的鏈表
我嘗試寫了一個簡單的鏈表,以前也大概看過C的鏈表和Java的鏈表,寫的過程中全憑記憶,
大約花了十個小時才寫完,哩哩啦啦好多天。
代碼拙劣,為了以后嘗試鏈表的其他算法(排序,轉換,反轉,環鏈表,擴展二叉樹)做准備。
代碼如下:
package com.FM.ArrayStudy; public class SimpleLinkedList<T> { private Node<T> pointer;// 當前指針節點 private Node<T> firstNode;// 首個節點 private Node<T> lastNode;// 末尾節點 private Integer index = 0;// 當前指針index private Integer size = 0;// 當前容量 /** * 獲取當前pointer的info * @return */ public T getThis() { if (null == pointer) { return firstNode.info; } else { return pointer.info; } } /** * 獲取下一個元素的內容,若沒有下一個元素,則返回null * * @return */ public T getNext() { if (index.equals(size - 1)) { return null; } else { if (null == pointer) { pointer = firstNode; pointer = pointer.next; T info = pointer.info; index++; return info; } else { pointer = pointer.next; T info = pointer.info; index++; return info; } } } /** * 修改指定index的元素的方法 * * @param index * @param t * @throws Exception */ public void set(Integer index, T t) throws Exception { if (index > -1 && index < size - 1) { Node<T> node = getNodeByIndex(index); node.info = t; } else { throw new Exception("get ele " + index + " out of index"); } } /** * 修改首元素 * * @param t */ public void setFirst(T t) { firstNode.info = t; } /** * 修改尾元素 * * @param t */ public void setLast(T t) { lastNode.info = t; } /** * 從指定index移除node的方法 * * @param index * @throws Exception */ public void remove(Integer index) throws Exception { if (index > -1 && index < size) { if (index.equals(0)) { Node<T> node = getNodeByIndex(1); firstNode = node; firstNode.prve = null; } else if (index.equals(size - 1)) { Node<T> node = getNodeByIndex(size - 2); lastNode = node; lastNode.next = null; } else { Node<T> node = getNodeByIndex(index); Node<T> nextNode = node.next; Node<T> prveNode = node.prve; prveNode.next = nextNode; nextNode.prve = prveNode; node = null; } size--; } else { throw new Exception("get ele " + index + " out of index"); } } /** * 獲取當前元素在鏈表中的位置 * * @return */ public int getIndex() { return index; } /** * 獲取當前鏈表size的方法 * * @return */ public Integer size() { return size; } /** * 判斷容器是否為空的方法,為空返回true * * @return */ public boolean isEmpty() { if (size.equals(0)) { return true; } else { return false; } } /** * 根據index查詢鏈表中元素的方法 * * @param index * @return * @throws Exception */ public T getByIndex(Integer index) throws Exception { if (index > -1 && index < size) { Node<T> nodeByIndex = getNodeByIndex(index); return nodeByIndex.info; } else { throw new Exception("get ele " + index + " out of index"); } } /** * 根據index獲取node的方法 * * @param index * @return */ private Node<T> getNodeByIndex(Integer index) { Node<T> temp = firstNode;// 取firstnode if (index != 0) {// 查看當前index,若index!=0,則遞歸直到index for (int i = 0; i < index; i++) { temp = temp.next; } } this.index = index;// 調整當前index return temp;// 返回節點 } /** * 向鏈表末尾默認添加一個元素的方法 * * @param t */ public void add(T t) { if (size == 0) {// 首次創建 Node<T> node = new Node<T>(); firstNode = node; lastNode = node; node.info = t; size++; } else { lastNode.next = new Node<T>(); lastNode.next.info = t; lastNode.next.prve = lastNode; lastNode = lastNode.next; size++; } } /** * 在首添加元素 * * @param t */ public void addFirst(T t) { if (size == 0) { add(t); } else { Node<T> node = new Node<T>(); node.info = t; node.next = firstNode; node.prve = null; firstNode.next = node; size++; } } /** * 在尾部添加元素 * * @param t */ public void addLast(T t) { add(t); } /** * Node節點 鏈表內部數據結構和指針 * * @author Liuyuhang * @param <T> */ private class Node<T> { T info;// 儲存info Node<T> next;// 下一個節點指針 Node<T> prve;// 上一個節點指針 @Override public String toString() {// 同時打印next和prev會導致無限引用,堆棧溢出 return "Node [info=" + info + ", next=" + next + "]"; } } @Override public String toString() {// 打印first節點會因為next引出整個鏈表的所有內容 return "SimpleLinkedList [node=" + firstNode + "]"; } }
測試可用,自己拿去玩吧。
以上!