Java數據結構(一)——鏈表

Java中的數據結構又很多種,如棧,隊列,樹等,但是他們的形式歸根到底就是兩種:一個是數組,一個是鏈表,所有的結構都是對這兩個的變形。
數組和鏈表都屬於是線性表,那什麼是線性表:一個線性表是n個相同特性的數據元素的有序序列。各元素之間是一對一的關係。但這並不是說一個線性表除了第一個和最後一個其他都是首尾相接,因爲循環鏈表也是一種線性表。因爲這個線性關係針對的是邏輯上的。
數組,是物理上連續的一塊存儲空間,它便於根據索引直接取出數據和存入數據,但是搜索和查找的效率低下,插入和刪除的效率低下,並且數組一旦創建以後大小就固定,如果存儲的數據太少,空間就浪費,如果存儲的數據太多,那就不能滿足,於是有了鏈表的產生。
鏈表不能解決數據存儲的所有問題,一般需要頻繁使用下標查找訪問數據的地方都不使用鏈表。鏈表在內存中存儲的位置是隨機地,但是通過指針相互連接,形成一種線性關係,所以鏈表是一種線性表。
對於鏈表,無論是哪種形式,我們希望能實現這些基礎功能:

      //創建結點類
    	//添加節點(只能頭部插入)
    	//查找數據
    	//刪除數據
    	//打印數據
    	//計算長度

1.單向鏈表

一個單向鏈表的結點分成兩部分:數據+下一結點的地址。
特性:只能單向遍歷,只提供鏈表頭插入
在這裏插入圖片描述
單向鏈表的具體實現:

package com.lm.list1118;

public class SingleLinklist {
	//創建結點類
	public class Node{
		Object data;//存儲數據
		Node next;//指向下一節點的地址
		public Node(Object data){//構造函數
			this.data = data;
		}
	}
	//創建頭結點
	private Node head;
	
	//添加節點(只能頭部插入)
	public void addNode(Object data) {
		Node node = new Node(data);
		if(head == null) {//當鏈表爲空時
			head = node;
		}else {
			node.next = head;
			head = node;
		}
	}
	//查找指定數據
	public Node findNode(Object data) {
		Node tmp = head;
		while(tmp != null) {
			if(data.equals(tmp.data)) {
				return tmp;
			}
			tmp = tmp.next;
		}
		return null;
	}
	//刪除數據(刪除頭結點)
	public boolean deleteHead() {
		while(head.next != null) {
			head = head.next;
			return true;
		}
		return false;
	}
	//刪除數據(刪除指定元素)
	public boolean deleteNode(Object data) {
		if(data.equals(head.data)) {
			head = head.next;
			return true;
		}
		Node tmp = head.next;
		Node curNode = head;
		while(tmp != null) {
			if(data.equals(tmp.data)) {
				curNode.next = tmp.next;
				return true;
			}else {
				curNode = curNode.next;
				tmp = tmp.next;
			}
		}
		return false;
	}
	//打印數據
	public void printNode() {
		Node node = head;
		while(node != null) {
			System.out.println("元素:"+node.data);
			node = node.next;
		}
	}
	//計算長度
	public int length() {
		int len = 0;
		Node node = head;
		while(node != null) {
			len++;
			node = node.next;
		}
		return len;
	}
	public static void main(String[] args) {
		SingleLinklist sl = new SingleLinklist();
		sl.addNode(10);
		sl.addNode(7);
		sl.addNode(4);
		sl.addNode(5);
		System.out.println("生成的鏈表爲:");
		sl.printNode();
		sl.deleteHead();
		System.out.println("刪除頭結點後鏈表爲:");
		sl.printNode();
		sl.deleteNode(7);
		System.out.println("刪除數據4後鏈表爲:");
		sl.printNode();
		System.out.println("元素8是否存在?"+sl.findNode(8));
	}
}

上面的單向鏈表我將完整的代碼貼出,之後的只需要稍加改動。
在這裏插入圖片描述

2.雙端鏈表

對於單向鏈表,我們暫定是對頭結點部位進行插入操作,但是這遠遠是不能滿足我們的需求,如果想要在任意位置插入數據,或者直接在尾部插入,那麼我們必須從頭部一直遍歷到尾部,這樣的算法複雜度是O(n),如果我們適當的增加一個尾指針,用於指向尾結點,那麼複雜度會直接降到O(1)。
在這裏插入圖片描述
雙端鏈表的具體實現:

  //尾部添加節點
    	public void addTail(Object data) {
    		Node node = new Node(data);
    		if(tail == null) {
    			head = node;
    			tail = node;
    		}
    		tail.next = node;
    		tail = node;
    	}
基於雙端鏈表實現隊列:
    package com.lm.list1118;
/**
 * 雙端鏈表實現隊列
 * 先進先出(隊首出,隊尾進)
 * @author Administrator
 *
 */
public class QueueLinklist {
	private DoublePointLinklist dl;
	public QueueLinklist(){
		dl = new DoublePointLinklist();//創建雙端鏈表對象
	}
	//入隊列
	public void insert(Object data) {
		dl.addTail(data);
	}
	//出隊列
	public void delete() {
		dl.deleteHead();
	}
	//獲取隊列長度
	public int length(){
		return dl.length();
	}
	//判斷是否爲空
	public boolean isEmpty() {
		if(dl.length()==0) 
			return true;
		return false;
	}
	//顯示隊列元素
	public void print() {
		dl.printNode();
	}
}

3.有序鏈表

一般說到鏈表,我們都認爲他是無序的,這樣就不方便查找最大值或最小值,所以可以對鏈表創建的時候進行排序,形成有序鏈表,這樣可以通過O(1)的時間複雜度獲取最值和刪除最值(刪除表頭即可)。而且有序鏈表在插入的時候比有序數組速度更快,不需要進行元素的移動,而且不受固定大小的限制,所以有序鏈表有時候可以代替有序數組。
有序鏈表的具體實現:
插入函數:
在這裏插入圖片描述
在這裏插入圖片描述

4.有序鏈表優化無序數組的排序

我們知道冒泡排序,選擇排序和插入排序需要的時間複雜度是O(N2),如果我們將無序的數組一個個取出然後插入有序鏈表中進行排序,然後再將排完的鏈表中的數據一個個取出重新放進數組,這個實現的排序。大概進行N2/4次比較,優化了效率。但是不足的地方就是開闢了需要的兩倍的空間。
//插入結點,從小到大排列
public void insert(int data) {
Node node = new Node(data);
Node pre = null;
Node cur = head;
//我本來想着的是用兩個變量,一個表示當前指針,一個表示當前指針的下一個指針,但是邏輯上容易出現空指針的情況,情況太多,考慮不全
//但是如果將一個變量表示成當前指針的前一個就簡單多了,因爲如果當前指針不爲空,那麼前一個指針一定不爲空

while(cur != null && cur.data<data) {
			pre = cur;
			cur = cur.next;
		}
		if(pre == null) {//當鏈表爲空時
			head = node;
			head.next = cur;
		}else {//當鏈表不爲空,並且找到插入的位置
			pre.next = node;
			node.next = cur;
		}
	}

##5. 雙向鏈表

所謂的雙向鏈表,就是相對單向鏈表而言的,可以兩個方向遍歷。

在這裏插入圖片描述
借用網上的圖來說明一下雙向鏈表的插入和刪除操作
在這裏插入圖片描述
具體的代碼實現:
/

/表頭增加節點
	public void addHead(Object data) {
		Node node = new Node(data);
		if(size==0) {
			head = node;
			tail = node;
		}else {
			head.prev = node;//不要忘記對prev指針的操作
			node.next = head;
			head = node;
		}
		size++;
	}
	//表尾增加節點
	public void addTail(Object data) {
		Node node = new Node(data);
		if(size == 0) {
			head = node;
			tail = node;
		}else {
			tail.prev = node;
			node.next = tail;
			tail = node;
		}
		size++;
	}
	//刪除表頭
	public void deleteHead() {
		if(size != 0) {
			head = head.next;
			head.prev = null;
			size--;
		}
	}
	//刪除表尾
	public void deleteTail() {
		if(size!=0) {
			tail = tail.prev;
			tail.next = null;
			size--;
		}
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章