一問讀懂鏈表結(jié)構(gòu),及應(yīng)用場景

Java中的鏈表結(jié)構(gòu)是指,將一組數(shù)據(jù)按照指定規(guī)則連接起來的數(shù)據(jù)結(jié)構(gòu)。它由多個(gè)節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)包含一個(gè)數(shù)據(jù)元素和一個(gè)指向下一個(gè)節(jié)點(diǎn)的引用。在Java中,這種數(shù)據(jù)結(jié)構(gòu)被封裝成了一個(gè)鏈表類,常見的有單向鏈表、雙向鏈表和循環(huán)鏈表等。

一、單向鏈表

單向鏈表是最簡單的一種鏈表結(jié)構(gòu),節(jié)點(diǎn)只包含一個(gè)數(shù)據(jù)元素和一個(gè)指向下一個(gè)節(jié)點(diǎn)的引用。它的優(yōu)點(diǎn)是刪除和插入節(jié)點(diǎn)非常方便,但是定位元素較為困難。

  1. 鏈表的節(jié)點(diǎn)定義

    public class Node {
        // 節(jié)點(diǎn)的數(shù)據(jù)元素
        private int data;
        // 指向下一個(gè)節(jié)點(diǎn)的引用
        private Node next;
    
        // 構(gòu)造函數(shù)
        public Node(int data) {
            this.data = data;
            this.next = null;
        }
    
        // 獲得數(shù)據(jù)元素
        public int getData() {
            return data;
        }
    
        // 設(shè)置數(shù)據(jù)元素
        public void setData(int data) {
            this.data = data;
        }
    
        // 獲得下一個(gè)節(jié)點(diǎn)的引用
        public Node getNext() {
            return next;
        }
    
        // 設(shè)置下一個(gè)節(jié)點(diǎn)的引用
        public void setNext(Node next) {
            this.next = next;
        }
    }
    
  2. 鏈表的操作方法

    • 添加節(jié)點(diǎn):向鏈表的末尾添加一個(gè)新節(jié)點(diǎn)

      public void addNode(int data) {
          Node newNode = new Node(data);
          if (head == null) {
              head = newNode;
          } else {
              Node currentNode = head;
              while (currentNode.getNext() != null) {
                  currentNode = currentNode.getNext();
              }
              currentNode.setNext(newNode);
          }
          size++;
      }
      
    • 刪除節(jié)點(diǎn):刪除鏈表中指定元素的節(jié)點(diǎn)

      public boolean removeNode(int data) {
          if (head == null) {
              return false;
          }
          if (head.getData() == data) {
              head = head.getNext();
              size--;
              return true;
          }
          Node currentNode = head;
          while (currentNode.getNext() != null) {
              if (currentNode.getNext().getData() == data) {
                  currentNode.setNext(currentNode.getNext().getNext());
                  size--;
                  return true;
              }
              currentNode = currentNode.getNext();
          }
          return false;
      }
      
    • 查找節(jié)點(diǎn):根據(jù)元素值查找節(jié)點(diǎn)

      public Node findNode(int data) {
          if (head == null) {
              return null;
          }
          Node currentNode = head;
          while (currentNode != null) {
              if (currentNode.getData() == data) {
                  return currentNode;
              }
              currentNode = currentNode.getNext();
          }
          return null;
      }
      

二、雙向鏈表

雙向鏈表是在單向鏈表基礎(chǔ)上增加了一個(gè)指向前一個(gè)節(jié)點(diǎn)的引用,這樣可以方便地實(shí)現(xiàn)反向遍歷和插入、刪除操作。但是相應(yīng)地,它需要占用更多的存儲(chǔ)空間。

  1. 鏈表的節(jié)點(diǎn)定義

    public class Node {
        // 節(jié)點(diǎn)的數(shù)據(jù)元素
        private int data;
        // 指向下一個(gè)節(jié)點(diǎn)的引用
        private Node next;
        // 指向前一個(gè)節(jié)點(diǎn)的引用
        private Node prev;
    
        // 構(gòu)造函數(shù)
        public Node(int data) {
            this.data = data;
            this.next = null;
            this.prev = null;
        }
    
        // 獲得數(shù)據(jù)元素
        public int getData() {
            return data;
        }
    
        // 設(shè)置數(shù)據(jù)元素
        public void setData(int data) {
            this.data = data;
        }
    
        // 獲得下一個(gè)節(jié)點(diǎn)的引用
        public Node getNext() {
            return next;
        }
    
        // 設(shè)置下一個(gè)節(jié)點(diǎn)的引用
        public void setNext(Node next) {
            this.next = next;
        }
    
        // 獲得前一個(gè)節(jié)點(diǎn)的引用
        public Node getPrev() {
            return prev;
        }
    
        // 設(shè)置前一個(gè)節(jié)點(diǎn)的引用
        public void setPrev(Node prev) {
            this.prev = prev;
        }
    }
    
  2. 鏈表的操作方法

    • 添加節(jié)點(diǎn):向鏈表的末尾添加一個(gè)新節(jié)點(diǎn)

      public void addNode(int data) {
          Node newNode = new Node(data);
          if (head == null) {
              head = newNode;
          } else {
              Node currentNode = head;
              while (currentNode.getNext() != null) {
                  currentNode = currentNode.getNext();
              }
              currentNode.setNext(newNode);
              newNode.setPrev(currentNode);
          }
          size++;
      }
      
    • 刪除節(jié)點(diǎn):刪除鏈表中指定元素的節(jié)點(diǎn)

      public boolean removeNode(int data) {
          if (head == null) {
              return false;
          }
          if (head.getData() == data) {
              head = head.getNext();
              if (head != null) {
                  head.setPrev(null);
              }
              size--;
              return true;
          }
          Node currentNode = head;
          while (currentNode != null) {
              if (currentNode.getData() == data) {
                  currentNode.getPrev().setNext(currentNode.getNext());
                  if (currentNode.getNext() != null) {
                      currentNode.getNext().setPrev(currentNode.getPrev());
                  }
                  size--;
                  return true;
              }
              currentNode = currentNode.getNext();
          }
          return false;
      }
      
    • 查找節(jié)點(diǎn):根據(jù)元素值查找節(jié)點(diǎn)

      public Node findNode(int data) {
          if (head == null) {
              return null;
          }
          Node currentNode = head;
          while (currentNode != null) {
              if (currentNode.getData() == data) {
                  return currentNode;
              }
              currentNode = currentNode.getNext();
          }
          return null;
      }
      

三、循環(huán)鏈表

循環(huán)鏈表是一種特殊的鏈表結(jié)構(gòu),它的最后一個(gè)節(jié)點(diǎn)的指針不是指向null,而是指向第一個(gè)節(jié)點(diǎn),形成一個(gè)環(huán)狀結(jié)構(gòu)。這樣在循環(huán)遍歷時(shí)可以省去邊界判斷,但是插入和刪除操作需要特殊考慮。

  1. 鏈表的節(jié)點(diǎn)定義

    public class Node {
        // 節(jié)點(diǎn)的數(shù)據(jù)元素
        private int data;
        // 指向下一個(gè)節(jié)點(diǎn)的引用
        private Node next;
    
        // 構(gòu)造函數(shù)
        public Node(int data) {
            this.data = data;
            this.next = null;
        }
    
        // 獲得數(shù)據(jù)元素
        public int getData() {
            return data;
        }
    
        // 設(shè)置數(shù)據(jù)元素
        public void setData(int data) {
            this.data = data;
        }
    
        // 獲得下一個(gè)節(jié)點(diǎn)的引用
        public Node getNext() {
            return next;
        }
    
        // 設(shè)置下一個(gè)節(jié)點(diǎn)的引用
        public void setNext(Node next) {
            this.next = next;
        }
    }
    
  2. 鏈表的操作方法

    • 添加節(jié)點(diǎn):向鏈表的末尾添加一個(gè)新節(jié)點(diǎn)

      public void addNode(int data) {
          Node newNode = new Node(data);
          if (head == null) {
              head = newNode;
              head.setNext(head);
          } else {
              Node currentNode = head;
              while (currentNode.getNext() != head) {
                  currentNode = currentNode.getNext();
              }
              currentNode.setNext(newNode);
              newNode.setNext(head);
          }
          size++;
      }
      
    • 刪除節(jié)點(diǎn):刪除鏈表中指定元素的節(jié)點(diǎn)

      public boolean removeNode(int data) {
          if (head == null) {
              return false;
          }
          if (head.getData() == data) {
              Node currentNode = head;
              while (currentNode.getNext() != head) {
                  currentNode = currentNode.getNext();
              }
              head = head.getNext();
              currentNode.setNext(head);
              size--;
              return true;
          }
          Node currentNode = head;
          while (currentNode.getNext() != head) {
              if (currentNode.getNext().getData() == data) {
                  currentNode.setNext(currentNode.getNext().getNext());
                  size--;
                  return true;
              }
              currentNode = currentNode.getNext();
          }
          return false;
      }
      
    • 查找節(jié)點(diǎn):根據(jù)元素值查找節(jié)點(diǎn)

      public Node findNode(int data) {
          if (head == null) {
              return null;
          }
          Node currentNode = head;
          while (currentNode.getNext() != head) {
              if (currentNode.getData() == data) {
                  return currentNode;
              }
              currentNode = currentNode.getNext();
          }
          if (currentNode.getData() == data) {
              return currentNode;
          }
          return null;
      }
      

四、鏈表的時(shí)間復(fù)雜度分析

鏈表結(jié)構(gòu)相比于數(shù)組具有更靈活的插入和刪除操作,但是它也存在一些缺點(diǎn)。在訪問隨機(jī)元素時(shí),需要從頭開始遍歷整個(gè)鏈表,時(shí)間復(fù)雜度為O(N);而在數(shù)組中可以通過索引直接訪問元素,時(shí)間復(fù)雜度為O(1)。下面給出單向鏈表、雙向鏈表和循環(huán)鏈表的常見操作的時(shí)間復(fù)雜度:

操作 單向鏈表 雙向鏈表 循環(huán)鏈表
訪問元素 O(N) O(N) O(N)
插入元素 O(1) O(1) O(1)
刪除元素 O(N) O(N) O(N)

五、鏈表的應(yīng)用舉例

鏈表結(jié)構(gòu)常見于計(jì)算機(jī)科學(xué)領(lǐng)域中的各種數(shù)據(jù)結(jié)構(gòu)與算法,例如棧、隊(duì)列、哈希表和圖等。下面以一個(gè)簡單的應(yīng)用場景為例,說明鏈表的應(yīng)用。

  1. 題目描述

    給定一個(gè)鏈表,判斷它是否為回文鏈表。回文鏈表是指正序和倒序讀取的結(jié)果相同的鏈表。

  2. 解題思路

    可以使用快慢指針將鏈表分成兩半,然后將后半部分反轉(zhuǎn),最后比較前半部分和反轉(zhuǎn)后的后半部分是否相同。

  3. 代碼實(shí)現(xiàn)

    public boolean isPalindrome(Node head) {
        if (head == null || head.getNext() == null) {
            return true;
        }
        Node slow = head, fast = head;
        while (fast.getNext() != null && fast.getNext().getNext() != null) {
            slow = slow.getNext();
            fast = fast.getNext().getNext();
        }
        Node secondHalf = reverseList(slow.getNext());
        Node currentNode = head;
        while (secondHalf != null) {
            if (currentNode.getData() != secondHalf.getData()) {
                return false;
            }
            currentNode = currentNode.getNext();
            secondHalf = secondHalf.getNext();
        }
        return true;
    }
    
    private Node reverseList(Node head) {
        if (head == null || head.getNext() == null) {
            return head;
        }
        Node prev = null, current = head;
        while (current != null) {
            Node next = current.getNext();
            current.setNext(prev);
            prev = current;
            current = next;
        }
        return prev;
    }
    

六、使用場景

使用場景,常見的應(yīng)用包括:

  1. 實(shí)現(xiàn)棧和隊(duì)列:鏈表作為?;蜿?duì)列的底層數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)入棧、出棧、入隊(duì)、出隊(duì)等操作時(shí)具有高效性和靈活性。
  2. 實(shí)現(xiàn)哈希表:在哈希表中,每個(gè)關(guān)鍵字會(huì)被映射到一個(gè)桶中,而這個(gè)桶則是一個(gè)鏈表,其中存放相同關(guān)鍵字的元素。
  3. 作為緩存的數(shù)據(jù)結(jié)構(gòu):在緩存中,經(jīng)常需要在數(shù)據(jù)集中插入、刪除、移動(dòng)元素。鏈表結(jié)構(gòu)提供了一種高效的方式來操作這些數(shù)據(jù)。
  4. 瀏覽器歷史記錄和撤銷功能:瀏覽器歷史記錄和撤銷功能等需要保留一系列操作或狀態(tài)的應(yīng)用場景都可以使用鏈表來實(shí)現(xiàn)。
  5. 實(shí)現(xiàn)圖論算法:在圖論中,使用鏈表結(jié)構(gòu)可以方便地表示鄰接表,保存每個(gè)節(jié)點(diǎn)的所有鄰居節(jié)點(diǎn)。
  6. 數(shù)據(jù)庫管理系統(tǒng):在數(shù)據(jù)庫管理系統(tǒng)中,可以使用鏈表來優(yōu)化查詢操作的效率。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容