數據的存儲通常分線性存儲結構和鏈式存儲結構兩種。前者是一種順序的存儲方式,在內存中用一塊連續的內存空間存儲數據,即邏輯上相連的物理位置相鄰,比較常見的就是數組;後者是一種鏈式存儲方式,不保證順序性,邏輯上相鄰的元素之間用指針所指定,它不是用一塊連續的內存存儲,邏輯上相連的物理位置不必定相鄰。本篇主要介紹鏈式存儲結構基於鏈表的實現,使用的語言爲Java。html
鏈表是一種遞歸的數據結構,它要麼爲空(null),要麼指向是指向一個結點(node)的引用,該節點含有一個泛型元素(該泛型元素能夠是任意數據類型),和一個指向另外一個鏈表的引用。鏈表有不少種,下面主要介紹單向鏈表、雙端鏈表、有序鏈表、雙向鏈表。java
單向鏈表:單向鏈表是最簡單、最基礎的鏈表,它的一個結點(node)分兩部分,第一部分存儲結點的數據信息(data),第二部分存儲指向下一結點的地址(next)信息。最後一個結點(鏈尾)指向一個空地址(null)。單向鏈表通常只在鏈表表頭(鏈頭)結點的位置插入元素,這樣每次新加入的元素都會在鏈頭位置,而最早加入的元素會在鏈尾位置。刪除操做時,若是在鏈頭位置刪除,只須要把頭結點指向其下一個結點便可;若是是在中間位置刪除,只須要將其前一個結點指向其下一個結點便可。單向鏈表示意圖以下圖所示:node
單向鏈表的Java代碼實現:數組
import java.util.Stack; public class LinkedListOnePoint { private Node head; //頭結點 private int size; //鏈表長度,即鏈表中結點數量 public LinkedListOnePoint(){ head = null; size = 0; } //私有內部類,表明鏈表每一個結點 private class Node{ private Object data; //鏈表結點的值 private Node next; //指向的下一個結點 public Node(Object data){ this.data = data; } } //判斷鏈表是否爲空 public boolean isEmpty(){ return size==0?true:false; } //返回鏈表長度 public int size(){ return size; } //查看鏈表頭結點,不移除 public Object headNode(){ if(size == 0) return null; return head.data; } //在鏈表表頭插入一個結點(入棧) public void insertInHead(Object obj){ Node newNode = new Node(obj); if(size == 0){ head = newNode; }else{ newNode.next = head; head = newNode; } size++; } //刪除鏈表表頭結點(出棧) public Object deleteHeadNode(){ if(size == 0) return null; Object obj = head.data; if(head.next == null){ head = null; //只有一個結點 }else{ head = head.next; } size--; return obj; } //鏈表查找:判斷鏈表中是否包含某個元素 public boolean containObject(Object obj){ if(size == 0) return false; Node n = head; while(n != null){ if(n.data == obj) return true; else n = n.next; } return false; } //刪除鏈表中的指定結點(若是包含多個指定結點,只會刪除第一個) public boolean deleteNode(Object obj){ if(size == 0){ System.out.println("鏈表爲空!"); return false; } //先在鏈表中查詢是否包含該結點,找到以後獲取該結點和其前一個結點 Node previous = null; //前一個結點 Node current = head; //當前結點 while(current.data != obj){ if(current.next == null){ System.out.println("沒有找到該結點!"); return false; } previous = current; current = current.next; } if(current == head){ this.deleteHeadNode(); }else{ previous.next = current.next; size--; } return true; } //正向打印鏈表 public void display(){ if(size == 0) System.out.println("鏈表爲空!"); Node n = head; while(n != null){ System.out.print("<-"+n.data); n = n.next; } System.out.println(); } //反向打印鏈表(用棧) public void printListFromTailToHead(Node node){ if(node == null) System.out.println("鏈表爲空!"); Stack<Integer> sta = new Stack<Integer>(); while(node != null){ sta.push((Integer) node.data); //先將鏈表壓入棧中 node = node.next; } while(!sta.empty()){ System.out.print(sta.pop()+"<-"); //出棧 } System.out.println(); } //反向打印鏈表(遞歸) public void printListFromTailToHeadDiGui(Node node){ if(node == null){ System.out.println("鏈表爲空!"); }else{ if(node.next != null){ printListFromTailToHeadDiGui(node.next); } System.out.print(node.data+"<-"); } } public static void main(String[] args) { LinkedListOnePoint list = new LinkedListOnePoint(); System.out.println(list.isEmpty()); //true System.out.println(list.size()); //0 list.display(); //鏈表爲空! list.printListFromTailToHead(list.head); //鏈表爲空! list.insertInHead(0); list.insertInHead(1); list.insertInHead(2); list.insertInHead(3); list.display(); //<-3<-2<-1<-0 list.printListFromTailToHead(list.head); //0<-1<-2<-3<- list.printListFromTailToHeadDiGui(list.head); //0<-1<-2<-3<- System.out.println(list.isEmpty()); //false System.out.println(list.size()); //4 System.out.println(list.containObject(1)); //true } }
咱們知道,棧是一種「後進先出」的數據結構,對棧的插入和刪除操做都是在棧頭位置進行的,這與在單向鏈表的表頭插入和刪除元素的原理相似,所以能夠用單向鏈表實現棧。微信
單向鏈表實現棧的Java代碼:數據結構
/* * 單鏈表實現棧 */ public class LinkedListToStack { private LinkedListOnePoint linkedlist; public LinkedListToStack(){ linkedlist = new LinkedListOnePoint(); } //棧大小 public int size(){ return linkedlist.size(); } //是否爲空棧 public boolean isEmpty(){ return linkedlist.isEmpty(); } //入棧 public void push(Object obj){ linkedlist.insertInHead(obj); } //出棧 public Object pop(){ if(this.isEmpty()) return null; return linkedlist.deleteHeadNode(); } //查看棧頂元素 public Object peek(){ if(this.isEmpty()) return null; return linkedlist.headNode(); } //打印棧中元素 public void display(){ linkedlist.display(); } public static void main(String[] args) { LinkedListToStack stack = new LinkedListToStack(); stack.push(0); stack.push(1); stack.push(2); stack.push(3); stack.display(); //<-3<-2<-1<-0 System.out.println(stack.peek()); //3 System.out.println(stack.pop()); //3 System.out.println(stack.pop()); //2 System.out.println(stack.pop()); //1 System.out.println(stack.pop()); //0 System.out.println(stack.pop()); //null } }
雙端鏈表:雙端鏈表和單向鏈表大致上是同樣的,不一樣的是,單向鏈表在表尾部分插入元素時,須要從頭結點一直遍歷到尾結點才能進行插入操做,這樣不免有些繁瑣。所以若是加入一個對尾結點的引用,這樣就能夠很方便地在尾結點進行插入操做,這就是雙端鏈表。除了有一個頭結點(head),還有一個尾結點(tail)。注意它和後面雙向鏈表的區別!ide
雙端鏈表的Java代碼實現:學習
import java.util.Stack; /* * 雙端鏈表,比單鏈表多了個尾結點 */ public class LinkedListTwoPoint { private Node head; //頭結點 private Node tail; //尾結點 private int size; //鏈表長度,即鏈表中結點數量 public LinkedListTwoPoint(){ head = null; //頭結點 tail = null; //尾結點 size = 0; //鏈表長度,即鏈表中結點數量 } //私有內部類,表明鏈表每一個結點 private class Node{ private Object data; //鏈表結點的值 private Node next; //指向的下一個結點 public Node(Object data){ this.data = data; } } //判斷鏈表是否爲空 public boolean isEmpty(){ return size==0?true:false; } //返回鏈表長度 public int size(){ return size; } //查看鏈表頭結點,不移除 public Object headNode(){ if(size == 0) return null; return head.data; } //查看鏈表尾結點,不移除 public Object tailNode(){ if(size == 0) return null; return tail.data; } //在鏈表表頭插入一個結點 public void insertInHead(Object obj){ Node newNode = new Node(obj); if(size == 0){ head = newNode; tail = newNode; }else{ newNode.next = head; head = newNode; } size++; } //在鏈表表尾插入一個結點 public void insertInTail(Object obj){ Node newNode = new Node(obj); if(size == 0){ head = newNode; tail = newNode; }else{ tail.next = newNode; tail = newNode; } size++; } //刪除鏈表表頭結點 public Object deleteHeadNode(){ if(size == 0) return null; Object obj = head.data; if(head.next == null){ //只有一個結點 head = null; tail = null; }else{ head = head.next; } size--; return obj; } //鏈表查找:判斷鏈表中是否包含某個元素 public boolean containObject(Object obj){ if(size == 0) return false; Node n = head; while(n != null){ if(n.data == obj) return true; else n = n.next; } return false; } //刪除鏈表中的指定結點(若是包含多個指定結點,只會刪除第一個) public boolean deleteNode(Object obj){ if(size == 0){ System.out.println("鏈表爲空!"); return false; } //先在鏈表中查詢是否包含該結點,找到以後獲取該結點和其前一個結點 Node previous = null; //前一個結點 Node current = head; //當前結點 while(current.data != obj){ if(current.next == null){ System.out.println("沒有找到該結點!"); return false; } previous = current; current = current.next; } if(current == head){ this.deleteHeadNode(); }else{ previous.next = current.next; size--; } return true; } //正向打印鏈表 public void display(){ if(size == 0) System.out.println("鏈表爲空!"); Node n = head; while(n != null){ System.out.print(n.data + "<-"); n = n.next; } System.out.println(); } //反向打印鏈表(用棧) public void printListFromTailToHead(Node node){ if(node == null) System.out.println("鏈表爲空!"); Stack<Integer> sta = new Stack<Integer>(); while(node != null){ sta.push((Integer) node.data); //先將鏈表壓入棧中 node = node.next; } while(!sta.empty()){ System.out.print(sta.pop()+"<-"); //出棧 } System.out.println(); } //反向打印鏈表(遞歸) public void printListFromTailToHeadDiGui(Node node){ if(node == null){ System.out.println("鏈表爲空!"); }else{ if(node.next != null){ printListFromTailToHeadDiGui(node.next); } System.out.print(node.data+"<-"); } } public static void main(String[] args) { LinkedListTwoPoint list = new LinkedListTwoPoint(); System.out.println(list.isEmpty()); //true System.out.println(list.size()); //0 list.display(); //鏈表爲空! list.printListFromTailToHead(list.head); //鏈表爲空! list.insertInHead(0); list.insertInHead(1); list.insertInHead(2); list.insertInHead(3); list.display(); //3<-2<-1<-0<- list.printListFromTailToHead(list.head); //0<-1<-2<-3<- list.printListFromTailToHeadDiGui(list.head); //0<-1<-2<-3<- System.out.println(list.isEmpty()); //false System.out.println(list.size()); //4 System.out.println(list.containObject(1)); //true } }
咱們知道,隊列是一種「先進先出」的數據結構,隊列的插入操做是在隊尾進行的,而刪除操做是在隊頭進行的,這與在雙端鏈表的表尾插入和在表頭刪除操做是相似的,所以能夠用雙端鏈表實現隊列。ui
雙端鏈表實現隊列的Java代碼:this
/* * 雙端鏈表實現隊列 */ public class LinkedListToQueue { private LinkedListTwoPoint linkedlist; public LinkedListToQueue(){ linkedlist = new LinkedListTwoPoint(); } //隊列大小 public int size(){ return linkedlist.size(); } //是否爲空隊列 public boolean isEmpty(){ return linkedlist.isEmpty(); } //入列,在鏈表表尾插入節點 public void add(Object obj){ linkedlist.insertInTail(obj); } //出列,在鏈表表頭刪除結點 public Object poll(){ if(this.isEmpty()) return null; return linkedlist.deleteHeadNode(); } //查看隊列頭元素 public Object peekHead(){ if(this.isEmpty()) return null; return linkedlist.headNode(); } //查看隊列尾元素 public Object peekTail(){ if(this.isEmpty()) return null; return linkedlist.tailNode(); } //打印隊列元素 public void display(){ linkedlist.display(); } public static void main(String[] args) { LinkedListToQueue stack = new LinkedListToQueue(); stack.add(0); stack.add(1); stack.add(2); stack.add(3); stack.display(); //0<-1<-2<-3<- System.out.println(stack.peekHead()); //0 System.out.println(stack.peekTail()); //3 System.out.println(stack.poll()); //0 System.out.println(stack.poll()); //1 System.out.println(stack.poll()); //2 System.out.println(stack.poll()); //3 System.out.println(stack.poll()); //null } }
有序鏈表:鏈表自己是一種無序的數據結構,元素的插入和刪除不能保證順序性,可是有沒有有序的鏈表呢?答案是確定的,咱們在單向鏈表中插入元素時,只須要將插入的元素與頭結點及其後面的結點比較,從而找到合適的位置插入便可。通常在大多數須要使用有序數組的場合也可使用有序鏈表,有序鏈表在插入時由於不須要移動元素,所以插入速度比數組快不少,另外鏈表能夠擴展到所有有效的使用內存,而數組只能侷限於一個固定的大小中。
有序鏈表的Java代碼實現:
/* * 有序鏈表 */ public class LinkedListInOrder { private Node head; //頭結點 private int size; //鏈表長度,即鏈表中結點數量 public LinkedListInOrder(){ head = null; size = 0; } //私有內部類,表明鏈表每一個結點 private class Node{ private Integer data; //鏈表結點的值 private Node next; //指向的下一個結點 public Node(Integer data){ this.data = data; } } //判斷鏈表是否爲空 public boolean isEmpty(){ return size==0?true:false; } //返回鏈表長度 public int size(){ return size; } //在鏈表中插入一個結點,保持鏈表有序性(頭結點最小,尾結點最大) public void insertNode(Integer obj){ Node newNode = new Node(obj); if(size == 0){ //空鏈表直接放入頭結點 head = newNode; }else{ Node previous = null; //插入位置前一個結點 Node current = head; //插入位置後一個結點(當前結點) while(current.data<obj && current.next != null){ //找到插入位置 previous = current; current = current.next; } if(current.next == null){ //若是插入的結點大於鏈表中全部結點,則放到鏈尾 current.next = newNode; }else{ previous.next = newNode; //在合適位置插入 newNode.next = current; } } size++; } //刪除鏈表表頭結點 public Object deleteHeadNode(){ if(size == 0) return null; Object obj = head.data; if(head.next == null){ head = null; //只有一個結點 }else{ head = head.next; } size--; return obj; } //正向打印鏈表 public void display(){ if(size == 0) System.out.println("鏈表爲空!"); Node n = head; while(n != null){ System.out.print("<-"+n.data); n = n.next; } System.out.println(); } public static void main(String[] args) { LinkedListInOrder list = new LinkedListInOrder(); System.out.println(list.isEmpty()); //true System.out.println(list.size()); //0 list.display(); //鏈表爲空! list.insertNode(0); list.insertNode(1); list.insertNode(2); list.insertNode(3); list.insertNode(2); list.insertNode(4); list.display(); //<-0<-1<-2<-2<-3<-4 System.out.println(list.isEmpty()); //false System.out.println(list.size()); //6 System.out.println("*****************************"); } }
雙向鏈表:前面的幾種鏈表只能從頭結點遍歷到尾結點這一個方向,每一個結點都只能指向其下一個結點。而雙向鏈表的每一個結點既能指向下一個結點,又能指向前一個結點,雙向鏈表既能從頭結點向尾結點遍歷,又能從尾結點向頭結點遍歷,既有一個頭結點,又有一個尾結點。
雙向鏈表的Java代碼實現:
/* * 雙向鏈表 * 單向鏈表只可向一個方向遍歷,通常查找一個結點的時候須要從第一個結點開始每次訪問下一個結點,一直訪問到須要的位置。 * 雙向鏈表的每一個結點都有指向的前一個結點和後一個節點,既有鏈表表頭又有表尾,便可從鏈頭向鏈尾遍歷,又可從鏈尾向鏈頭遍歷。 * LinkedList中的私有靜態內部類Node實際上就是一個雙向鏈表,<E>表明泛型,指明結點的數據類型 * private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } */ public class LinkedListTwoDirections { private Node head; //頭結點 private Node tail; //尾結點 private int size; //鏈表長度,即鏈表中結點數量 public LinkedListTwoDirections(){ head = null; tail = null; size = 0; } //私有內部類,表明鏈表每一個結點 private class Node{ private Object data; //鏈表結點的值 private Node previous; //當前結點指向的前一個結點 private Node next; //當前結點指向的下一個結點 public Node(Object data){ this.data = data; } } //判斷鏈表是否爲空 public boolean isEmpty(){ return size==0?true:false; } //返回鏈表長度 public int size(){ return size; } //查看鏈表頭結點,不移除 public Object headNode(){ if(size == 0) return null; return head.data; } //查看鏈表尾結點,不移除 public Object tailNode(){ if(size == 0) return null; return tail.data; } //在鏈表表頭插入一個結點 public void insertInHead(Object obj){ Node newNode = new Node(obj); if(size == 0){ head = newNode; tail = newNode; }else{ newNode.next = head; head.previous = newNode; head = newNode; } size++; } //在鏈表表尾插入一個結點 public void insertInTail(Object obj){ Node newNode = new Node(obj); if(size == 0){ head = newNode; tail = newNode; }else{ newNode.previous = tail; tail.next = newNode; tail = newNode; } size++; } //刪除鏈表表頭結點 public Object deleteHeadNode(){ if(size == 0) return null; Object obj = head.data; if(head.next == null){ //只有一個結點 head = null; tail = null; }else{ head = head.next; head.previous = null; } size--; return obj; } //刪除鏈表表尾結點 public Object deleteTailNode(){ if(size == 0) return null; Object obj = tail.data; if(tail.previous == null){ //只有一個結點 head = null; tail = null; }else{ tail = tail.previous; tail.next = null; } size--; return obj; } //正向打印鏈表 public void display(){ if(size == 0) System.out.println("鏈表爲空!"); Node n = head; while(n != null){ System.out.print("<-"+n.data); n = n.next; } System.out.println(); } public static void main(String[] args) { LinkedListTwoDirections list = new LinkedListTwoDirections(); System.out.println(list.isEmpty()); //true System.out.println(list.size()); //0 list.display(); //鏈表爲空! list.insertInHead(0); list.insertInHead(1); list.insertInHead(2); list.insertInHead(3); list.display(); //<-3<-2<-1<-0 System.out.println(list.deleteHeadNode()); //3 list.insertInTail(1); list.insertInTail(2); list.display(); //<-2<-1<-0<-1<-2 } }
轉載請註明出處 http://www.cnblogs.com/Y-oung/p/8886142.html
工做、學習、交流或有任何疑問,請聯繫郵箱:yy1340128046@163.com 微信:yy1340128046