java實現數據結構-鏈表(單向,循環,雙向)

1. 什麼是鏈表

鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是經過鏈表中的指針鏈接次序實現的。 每個鏈表都包含多個節點,節點又包含兩個部分,一個是數據域(儲存節點含有的信息),一個是引用域(儲存下一個節點或者上一個節點的地址)。 node

圖片描述

2. 鏈表的特色

  • 獲取數據麻煩,須要遍歷查找,比數組慢
  • 方便插入、刪除

3. 單向鏈表

單向鏈表是一種簡單的數據結構,在單向鏈表中每一個節點中都會有一個引用域指向下一個節點的地址.數組

3.1 單向鏈表的代碼實現

//單向鏈表實現
public class ListNode<T> {

    private Node head;
    private int size = 0;

    public class Node{
        T data;
        Node next;

        public Node(T data){
            this.data = data;
            next = null;
        }

    }

    //若是鏈表沒有頭結點,新結點直接成爲頭結點;不然新結點的next直接指向當前頭結點,並讓新結點成爲新的頭結點
    public void addHeadNode(T value) {
        Node newNode = new Node(value);
        //頭結點不存在,新結點成爲頭結點
        if (head == null) {
            head = newNode;
            size ++;
            return;
        }
        //新結點next直接指向當前頭結點
        newNode.next = head;
        //新結點成爲新的頭結點
        head = newNode;
        size ++;
    }

    //果鏈表沒有頭結點,新結點直接成爲頭結點;不然須要先找到鏈表當前的尾結點,並將新結點插入到鏈表尾部。
    public void addTailNode(T value) {
        Node newNode = new Node(value);
        //頭結點不存在,新結點成爲頭結點
        if (head == null) {
            head = newNode;
            size ++;
            return;
        }
        //找到最後一個結點
        Node last = head;
        while (last.next != null) {
            last = last.next;
        }
        //新結點插入到鏈表尾部
        last.next = newNode;
        size ++;
    }

    public void addNodeAtIndex(T value, int index) {
        if (index < 0 || index > size) { //注意index是能夠等於size()的
            throw new IndexOutOfBoundsException("IndexOutOfBoundsException");
        }
        if (index == 0) {  //插入到頭部
            addHeadNode(value);
        } else if (index == size) {  //插入到尾部
            addTailNode(value);
        } else {  //插到某個中間位置
            Node newNode = new Node(value);
            int position = 0;
            Node cur = head;  //標記當前結點
            Node pre = null;  //記錄前置結點
            while (cur != null) {
                if (position == index) {
                    newNode.next = cur;
                    pre.next = newNode;
                    size++;
                    return;
                }
                pre = cur;
                cur = cur.next;
                position++;
            }
        }
    }

    public void deleteNodeAtIndex(int index) {
        if (index < 0 || index > size - 1) {
            throw new IndexOutOfBoundsException("IndexOutOfBoundsException");
        }
        if (index == 0) { //刪除頭
            head = head.next;
            return;
        }
        int position = 0;  //記錄當前位置
        Node cur = head;  //標記當前結點
        Node pre = null;  //記錄前置結點
        while (cur != null) {
            if (position == index) {
                pre.next = cur.next;
                cur.next = null;  //斷開cur與鏈表的鏈接
                return;
            }
            pre = cur;
            cur = cur.next;
            position++;
        }
    }

    public void get(){
        Node curNode = head;
        while(curNode !=null){
            System.out.print(curNode.data+" ");
            curNode = curNode.next;
        }
        System.out.println();
    }

    public int len(){
        return this.size;
    }

}
複製代碼

測試類bash

public static void main(String[] args) {
        ListNode listNode = new ListNode();
        listNode.addHeadNode(1);
        listNode.addHeadNode(2);
        listNode.addHeadNode(3);
        listNode.addHeadNode(4);
        listNode.addHeadNode(5);

        listNode.addTailNode(6);

        listNode.addNodeAtIndex(7,1);

        listNode.get();

        System.out.println(listNode.len());

        listNode.deleteNodeAtIndex(1);

        listNode.get();
    }
複製代碼

打印結構 數據結構

圖片描述

4. 循環鏈表

單向鏈表中的尾節點指向的下一個地址爲null,在單鏈表中是作不到從非頭節點出發訪問到鏈表的全部的節點. 將單鏈表中終端節點的指針端由空指針改成指向頭結點,就使整個單鏈表造成一個環,這種頭尾相接的單鏈表成爲單循環鏈表,簡稱循環鏈表.測試

4.1 循環鏈表的代碼實現

循環鏈表與單向鏈表比較相似,只是尾部終端節點的下一個地址指向頭節點便可.ui

//循環鏈表實現
public class CycleListNode<T> {

    private Node head;
    private int size = 0;

    public class Node{
        T data;
        Node next;

        public Node(T data){
            this.data = data;
            next = null;
        }

    }

    public boolean add(T data) {//添加節點
        boolean isSuccessful=false;
        if(head==null){//處理空表
            Node node = new Node(data);
            head=node;
            node.next=head;
            isSuccessful=true;
        }else{//不爲空時
            Node node= head;
            while(true){
                if (node.next==head){
                    break;
                }
                node=node.next;
            }
            Node newNode = new Node(data);
            node.next=newNode;
            newNode.next=head;
            isSuccessful=true;

        }
        return isSuccessful;

    }

    public Node delete(int index) {//刪除制定節點
        Node outputNode = null;
        Node node= head; //指向第一個節點
        if ((index)==0) {//查到該節點的前一個節點
            outputNode = node;
            head=null;
            head = node.next;//刪除語句
            return outputNode;
        }
        int location=0;//計時器
        while (true){  //
            if (isEmpty()) {//若是鏈表爲空中斷
                break;
            }else  if (location==index-1){//查到該節點的前一個節點
                outputNode=node.next;
                node.next=node.next.next;//刪除語句

                break;
            }
            if (node.next==null){
                break;
            }
            location++;//計時器加加
            node=node.next;//節點後移
        }
        return outputNode;

    }

    public void display() {//遍歷整個鏈表

        Node node = head;
        while (node != null) {
            System.out.println(node.data.toString());

            if (node.next == head) {
                break;
            }
            node = node.next;

        }
    }

    public boolean isEmpty() {
        return head==null;
    }
}
複製代碼

測試this

public static void main(String[] args) {
        CycleListNode cycleListNode = new CycleListNode();
        cycleListNode.add(1);
        cycleListNode.add(2);
        cycleListNode.add(3);
        cycleListNode.add(4);
        cycleListNode.add(5);

        cycleListNode.display();
    }
複製代碼

5. 雙向鏈表

雙向鏈表是在單鏈表的每個節點中再設置一個指向前驅節點的指針域.因此在雙向鏈表中的節點都有兩個指針域,一個指向後繼,一個指向前驅. spa

圖片描述

5.1 雙向鏈表的代碼實現

//雙向鏈表
public class DoublyLinkedList<T> {

    private Node frist;
    private Node last;

    class Node<T>{
        T data;
        Node next;
        Node pre;

        public Node(T data){
            this.data = data;
        }

        public void displayCurrentNode() {
            System.out.print(data + " ");
        }
    }

    public DoublyLinkedList(){
        frist = null;
        last = null;
    }

    public boolean isEmpty(){
        return frist == null;
    }

    //添加到首部
    public void addFrist(T dataue){
        Node<T> newNode= new Node(dataue);
        if(isEmpty()){ // 若是鏈表爲空
            last = newNode; //last -> newNode
        }else {
            frist.pre = newNode; // frist.pre -> newNode
        }
        newNode.next = frist; // newNode -> frist
        frist = newNode; // frist -> newNode
    }

    public void addLast(T dataue){
        Node<T> newNode= new Node(dataue);
        if(isEmpty()){ // 若是鏈表爲空
            frist = newNode; // 表頭指針直接指向新節點
        }else {
            last.next = newNode; //last指向的節點指向新節點
            newNode.pre = last; //新節點的前驅指向last指針
        }
        last = newNode; // last指向新節點
    }

    public boolean addBefore(T key,T dataue){

        Node<T> cur = frist;
        if(frist.next.data == key){
            addFrist(dataue);
            return true;
        }else {
            while (cur.next.data != key) {
                cur = cur.next;
                if(cur == null){
                    return false;
                }
            }
            Node<T> newNode= new Node(dataue);
            newNode.next = cur.next;
            cur.next.pre = newNode;
            newNode.pre = cur;
            cur.next = newNode;
            return true;
        }
    }

    public void addAfter(T key,T dataue)throws RuntimeException{
        Node<T> cur = frist;
        while(cur.data!=key){ //通過循環,cur指針指向指定節點
            cur = cur.next;
            if(cur == null){ // 找不到該節點
                throw new RuntimeException("Node is not exists");
            }
        }
        Node<T> newNode = new Node(dataue);
        if (cur == last){ // 若是當前結點是尾節點
            newNode.next = null; // 新節點指向null
            last =newNode; // last指針指向新節點
        }else {
            newNode.next = cur.next; //新節點next指針,指向當前結點的next
            cur.next.pre = newNode; //當前結點的前驅指向新節點
        }
        newNode.pre = cur;//當前結點的前驅指向當前結點
        cur.next = newNode; //當前結點的後繼指向新節點
    }

    public void deleteFrist(){
        if(frist.next == null){
            last = null;
        }else {
            frist.next.pre = null;
        }
        frist = frist.next;
    }

    public void deleteLast(T key){
        if(frist.next == null){
            frist = null;
        }else {
            last.pre.next = null;
        }
        last = last.pre;
    }

    public void deleteKey(T key)throws RuntimeException{
        Node<T> cur = frist;
        while(cur.data!= key){
            cur = cur.next;
            if(cur == null){ //不存在該節點
                throw new RuntimeException("Node is not exists");
            }
        }
        if(cur == frist){ // 若是frist指向的節點
            frist = cur.next; //frist指針後移
        }else {
            cur.pre.next = cur.next;//前面節點的後繼指向當前節點的後一個節點
        }
        if(cur == last){ // 若是當前節點是尾節點
            last = cur.pre; // 尾節點的前驅前移
        }else {
            cur.next.pre = cur.pre; //後面節點的前驅指向當前節點的前一個節點
        }
    }

    public T queryPre(T dataue)throws RuntimeException{
        Node<T> cur = frist;
        if(frist.data == dataue){
            throw new RuntimeException("Not find "+dataue+"pre");
        }
        while(cur.next.data!=dataue){
            cur = cur.next;
            if(cur.next == null){
                throw new RuntimeException(dataue +"pre is not exeist!");
            }
        }

        return cur.data;
    }

    public void displayForward(){
        Node<T> cur = frist;
        while(cur!=null){
            cur.displayCurrentNode();
            cur = cur.next;
        }
        System.out.println();

    }
    public void displayBackward(){
        Node<T> cur = last;
        while(cur!=null){
            cur.displayCurrentNode();
            cur = cur.pre;
        }
        System.out.println();
    }

}
複製代碼
相關文章
相關標籤/搜索