單向鏈表(單鏈表)是鏈表的一種,其特點是鏈表的鏈接方向是單向的,對鏈表的訪問要通過順序讀取從頭部開始。
鏈式存儲結構的線性表將采用一組任意的存儲單元存放線性表中的數據元素。由於不需要按順序存儲,鏈表在插入、刪除數據元素時比順序存儲要快,但是在查找一個節點時則要比順序存儲要慢
使用鏈式存儲可以克服順序線性表需要預先知道數據大小的缺點,鏈表結構可以充分利用內存空間,實現靈活的內存動態管理。但是鏈式存儲失去了數組隨機存取的特點,同時增加了節點的指針域,空間開銷較大。
下圖就是最簡單最一般的單向鏈表:
新增節點:
將值為element的新節點插入到第index的位置上。
首先要先找到索引為index-1的節點,然后生成一個數據為element的新節點newNode,並令index-1處節點的next指向新節點,新節點的next指向原來index處的節點。
刪除節點:
刪除第index個節點,第index節點是由index-1出的節點引用的,因此刪除index的節點要先獲取index-1處的節點,然后讓index-1出節點的next引用到原index+1處的節點,並釋放index處節點即可。
單向鏈表的Java實現
下面的程序分別實現了線性表的初始化、獲取線性表長度、獲取指定索引處元素、根據值查找、插入、刪除、清空等操作。
package com.liuhao.algorithm; public class LinkList<T> { // 定義一個內部類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 header;// 保存頭結點 private Node tail;// 保存尾節點 private int size;// 保存已含有的節點數 // 創建空鏈表 public LinkList() { header = null; tail = null; } // 已指定數據元素創建鏈表,只有一個元素 public LinkList(T element) { header = new Node(element, null); // 只有一個節點,header,tail都指向該節點 tail = header; size++; } // 返回鏈表的長度 public int length() { return size; } // 獲取指定索引處的元素 public T get(int index) { return this.getNodeByIndex(index).data; } //獲取指定位置的節點 private Node getNodeByIndex(int index){ if(index < 0 || index > size-1){ throw new IndexOutOfBoundsException("索引超出線性表范圍"); } Node current = header;//從header開始遍歷 for(int i=0; i<size && current!=null; i++,current=current.next){ if(i == index){ return current; } } return null; } //按值查找所在位置 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; } //指定位置插入元素 public void insert(T element, int index){ if(index < 0 || index > size){ throw new IndexOutOfBoundsException("索引超出線性表范圍"); } //如果是空鏈表 if(header == null){ add(element); } else{ //當index為0時,即在鏈表頭處插入 if(0 == index){ addAtHead(element); } else{ Node prev = getNodeByIndex(index - 1);//獲取前一個節點 //讓prev的next指向新節點,新節點的next指向原來prev的下一個節點 prev.next = new Node(element, prev.next); size++; } } } //在尾部插入元素 public void add(T element) { //如果鏈表是空的 if(header == null){ header = new Node(element, null); //只有一個節點,headwe,tail都該指向該節點 tail = header; } else{ Node newNode = new Node(element, null);//創建新節點 tail.next = newNode;//尾節點的next指向新節點 tail = newNode;//將新節點作為尾節點 } size++; } //頭部插入 public void addAtHead(T element){ //創建新節點,讓新節點的next指向header //並以新節點作為新的header Node newNode = new Node(element, null); newNode.next = header; header = newNode; //若插入前是空表 if(tail == null){ tail = header; } size++; } //刪除指定索引處的元素 public T delete(int index){ if(index < 0 || index > size-1){ throw new IndexOutOfBoundsException("索引超出線性表范圍"); } Node del = null; //若要刪除的是頭節點 if(index == 0){ del = header; header = header.next; } else{ Node prev = getNodeByIndex(index - 1);//獲取待刪除節點的前一個節點 del = prev.next;//獲取待刪除節點 prev.next = del.next; del.next = null;//將被刪除節點的next引用置為空 } size--; return del.data; } //刪除最后一個元素 public T remove(){ return delete(size - 1); } //判斷線性表是否為空 public boolean isEmpty(){ return size == 0; } //清空線性表 public void clear(){ //將header,tail置為null header = null; tail = null; size = 0; } public String toString(){ if(isEmpty()){ 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(); } } }
測試代碼
package com.liuhao.test; import org.junit.Test; import com.liuhao.algorithm.LinkList; public class LinkListTest { @Test public void test() { // 測試構造函數 LinkList<String> list = new LinkList("好"); System.out.println(list); // 測試添加元素 list.add("ni"); list.add("沒"); System.out.println(list); // 在頭部添加 list.addAtHead("五月"); System.out.println(list); // 在指定位置添加 list.insert("摩卡", 2); System.out.println(list); // 獲取指定位置處的元素 System.out.println("第2個元素是(從0開始計數):" + list.get(2)); // 返回元素索引 System.out.println("摩卡在的位置是:" + list.locate("摩卡")); System.out.println("moka所在的位置:" + list.locate("moka")); // 獲取長度 System.out.println("當前線性表的長度:" + list.length()); // 判斷是否為空 System.out.println(list.isEmpty()); // 刪除最后一個元素 list.remove(); System.out.println("調用remove()后:" + list); // 獲取長度 System.out.println("當前線性表的長度:" + list.length()); // 刪除指定位置處元素 list.delete(3); System.out.println("刪除第4個元素后:" + list); // 獲取長度 System.out.println("當前線性表的長度:" + list.length()); // 清空 list.clear(); System.out.println(list); // 判斷是否為空 System.out.println(list.isEmpty()); } }