成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

數(shù)據(jù)結構與算法——鏈表

tracymac7 / 3400人閱讀

摘要:今天來看另一種很基礎的數(shù)據(jù)結構鏈表。其次是尾結點的指針,它指向了,表示鏈表結束。但是,如果我們要查找鏈表數(shù)據(jù)怎么辦呢鏈表的內存不是連續(xù)的,不能像數(shù)組那樣根據(jù)下標訪問,所以只能通過遍歷鏈表來查找,時間復雜度為。

1. 概述

前面說到了數(shù)組,利用連續(xù)的內存空間來存儲相同類型的數(shù)據(jù),其最大的特點是支持下標隨機訪問,但是刪除和插入的效率很低。今天來看另一種很基礎的數(shù)據(jù)結構——鏈表。鏈表不需要使用連續(xù)的內存空間,它使用指針將不連續(xù)的內存塊連接起來,形成一種鏈式結構。

2. 單鏈表

首先來看看單鏈表,存儲數(shù)據(jù)的內存塊叫做節(jié)點,每個節(jié)點保存了一個 next 指針,指向下一個節(jié)點的內存地址,結合下圖你就很容易看明白了:

其中有兩個節(jié)點指針比較的特殊,首先是鏈表頭節(jié)點的指針,它指向了鏈表的第一個節(jié)點的地址,利用它我們可以遍歷得到整個鏈表。其次是尾結點的指針,它指向了 null ,表示鏈表結束。

不難看出,單鏈表的最大特點便是使用指針來連接不連續(xù)的節(jié)點,這樣我們不用擔心擴容的問題了,并且,鏈表的插入和刪除操作也非常的高效,只需要改變指針的指向即可。

結合上圖不難理解,單鏈表能夠在 O(1) 復雜度內刪除和添加元素,這就比數(shù)組高效很多。但是,如果我們要查找鏈表數(shù)據(jù)怎么辦呢?鏈表的內存不是連續(xù)的,不能像數(shù)組那樣根據(jù)下標訪問,所以只能通過遍歷鏈表來查找,時間復雜度為 O(n)。下面是單鏈表的代碼示例:

public class SingleLinkedList {
    private Node head = null;//鏈表的頭節(jié)點

    //根據(jù)值查找節(jié)點
    public Node findByValue(int value) {
        Node p = head;
        while (p != null && p.getData() != value)
            p = p.next;
        return p;
    }

    //根據(jù)下標查找節(jié)點
    public Node findByIndex(int index) {
        Node p = head;
        int flag = 0;
        while (p != null){
            if (flag == index)
                return p;
            flag ++;
            p = p.next;
        }
        return null;
    }

    //插入節(jié)點到鏈表頭部
    public void insertToHead(Node node){
        if (head == null) head = node;
        else {
            node.next = head;
            head = node;
        }
    }
    public void insertToHead(int value){
        this.insertToHead(new Node(value));
    }

    //插入節(jié)點到鏈表末尾
    public void insert(Node node){
        if (head == null){
            head = node;
            return;
        }

        Node p = head;
        while (p.next != null) p = p.next;
        p.next = node;
    }
    public void insert(int value){
        this.insert(new Node(value));
    }

    //在某個節(jié)點之后插入節(jié)點
    public void insertAfter(Node p, Node newNode){
        if (p == null) return;
        newNode.next = p.next;
        p.next = newNode;
    }
    public void insertAfter(Node p, int value){
        this.insertAfter(p, new Node(value));
    }

    //在某個節(jié)點之前插入節(jié)點
    public void insertBefore(Node p, Node newNode){
        if (p == null) return;
        if (p == head){
            insertToHead(newNode);
            return;
        }
        //尋找節(jié)點p前面的節(jié)點
        Node pBefore = head;
        while (pBefore != null && pBefore.next != p){
            pBefore = pBefore.next;
        }

        if (pBefore == null) return;
        newNode.next = pBefore.next;
        pBefore.next = newNode;
    }
    public void insertBefore(Node p, int value){
        insertBefore(p, new Node(value));
    }

    //刪除節(jié)點
    public void deleteByNode(Node p){
        if (p == null || head == null) return;
        if (p == head){
            head = head.next;
            return;
        }
        Node pBefore = head;
        while (pBefore != null && pBefore.next != p){
            pBefore = pBefore.next;
        }

        if (pBefore == null) return;
        pBefore.next = pBefore.next.next;
    }

    //根據(jù)值刪除節(jié)點
    public void deleteByValue(int value){
        Node node = this.findByValue(value);
        if (node == null) return;
        this.deleteByNode(node);
    }

    //打印鏈表的所有節(jié)點值
    public void print(){
        Node p = head;
        while (p != null){
            System.out.print(p.getData() + "  ");
            p = p.next;
        }
        System.out.println();
    }
        //定義鏈表節(jié)點
    public static class Node{
        private int data;
        private Node next;

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

        public int getData() {
            return data;
        }
    }
}
3. 循環(huán)鏈表

循環(huán)鏈表和單鏈表的唯一區(qū)別便是鏈表的尾結點指針并不是指向 null,而是指向了頭節(jié)點,這樣便形成了一個環(huán)形的鏈表結構:

4. 雙向鏈表

雙向鏈表,顧名思義,就是鏈表不只是存儲了指向下一個節(jié)點的 next 指針,還存儲了一個指向前一個節(jié)點的 prev 指針,如下圖:

為什么要使用這種具有兩個指針的鏈表呢?主要是為了解決鏈表刪除和插入操作的效率問題。

在單鏈表中,要刪除一個節(jié)點,必須找到其前面的節(jié)點,這樣就要遍歷鏈表,時間開銷較高。但是在雙向鏈表中,由于每個節(jié)點都保存了指向前一個節(jié)點的指針,這樣我們能夠在 O(1) 的時間復雜度內刪除節(jié)點。

插入操作也類似,比如要在節(jié)點 p 之前插入一個節(jié)點,那么也必須遍歷單鏈表找到 p 節(jié)點前面的那個節(jié)點。但是雙向鏈表可以直接利用前驅指針 prev 找到前一個節(jié)點,非常的高效。

這也是雙向鏈表在實際開發(fā)中用的更多的原因,雖然每個節(jié)點存儲了兩個指針,空間開銷更大,這就是一種典型的用空間換時間的思想。

下面是雙向鏈表的代碼示例:

public class DoubleLinkedList {
    private Node head = null;//鏈表的頭節(jié)點

    //在某個節(jié)點之前插入節(jié)點,這里方能體現(xiàn)出雙向鏈表的優(yōu)勢
    public void insertBefore(Node p, Node newNode) {
        if (p == null) return;
        if(p == head) {
            this.insertToHead(newNode);
            return;
        }
        
        newNode.prev = p.prev;
        p.prev.next = newNode;
        
        newNode.next = p;
        p.prev = newNode;
    }
    public void insertBefore(Node p, int value) {
        this.insertBefore(p, new Node(value));
    }
    
    //刪除某個節(jié)點
    public void deleteByNode(Node node) {
        if(node == null || head == null) return;
        if (node == head) {
            head = head.next;
            if(head != null) head.prev = null;
            return;
        }
        Node prev = node.prev;
        Node next = node.next;
        prev.next = next;
        if(next != null) next.prev = prev;
    }
    
    //根據(jù)值刪除節(jié)點
    public void deleteByValue(int value) {
        Node node = this.findByValue(value);
        if (node == null) return;
        this.deleteByNode(node);
    }
   
    //定義鏈表節(jié)點
    public static class Node{
        private int data;
        private Node prev;//鏈表的前驅指針
        private Node next;//鏈表的后繼指針
        
        public Node(int data) {
            this.data = data;
            this.prev = null;
            this.next = null;
        }

        public int getData() {
            return data;
        }
    }
}

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/73690.html

相關文章

  • 學習數(shù)據(jù)結構算法鏈表

    摘要:本系列所有文章第一篇文章學習數(shù)據(jù)結構與算法之棧與隊列第二篇文章學習數(shù)據(jù)結構與算法之鏈表第三篇文章學習數(shù)據(jù)結構與算法之集合第四篇文章學習數(shù)據(jù)結構與算法之字典和散列表第五篇文章學習數(shù)據(jù)結構與算法之二叉搜索樹簡單介紹鏈表鏈表一種常見的數(shù)據(jù)結構,可 本系列所有文章:第一篇文章:學習數(shù)據(jù)結構與算法之棧與隊列第二篇文章:學習數(shù)據(jù)結構與算法之鏈表第三篇文章:學習數(shù)據(jù)結構與算法之集合第四篇文章:學習數(shù)...

    jerryloveemily 評論0 收藏0
  • 學習JavaScript數(shù)據(jù)結構算法(二):鏈表

    摘要:實現(xiàn)移除給定的元素要移除的元素返回值表示移除成功方法說明移除單向鏈表中某個位置的元素。的前端樂園原文鏈接寒假前端學習學習數(shù)據(jù)結構與算法二鏈表 本系列的第一篇文章: 學習JavaScript數(shù)據(jù)結構與算法(一),棧與隊列第二篇文章:學習JavaScript數(shù)據(jù)結構與算法(二):鏈表第三篇文章:學習JavaScript數(shù)據(jù)結構與算法(三):集合第四篇文章:學習JavaScript數(shù)據(jù)結構與...

    lolomaco 評論0 收藏0
  • Java數(shù)據(jù)結構算法——鏈表(面試)

    摘要:前言數(shù)據(jù)結構與算法專題會不定時更新,歡迎各位讀者監(jiān)督。指針反轉實現(xiàn)鏈表反轉代碼反轉鏈表獲取當前下下個元素測試代碼部分用到了上篇文章數(shù)據(jù)結構與算法鏈表的代碼段,請移步獲取。 聲明:碼字不易,轉載請注明出處,歡迎文章下方討論交流。 前言:Java數(shù)據(jù)結構與算法專題會不定時更新,歡迎各位讀者監(jiān)督。本文是上篇文章Java數(shù)據(jù)結構與算法——鏈表的擴展篇,介紹鏈表的特點,使用場景、鏈表的性能分析以...

    keke 評論0 收藏0
  • JS數(shù)據(jù)結構算法_鏈表

    摘要:上一篇數(shù)據(jù)結構與算法棧隊列下一篇數(shù)據(jù)結構與算法集合字典寫在前面說明數(shù)據(jù)結構與算法系列文章的代碼和示例均可在此找到上一篇博客發(fā)布以后,僅幾天的時間竟然成為了我寫博客以來點贊數(shù)最多的一篇博客。 上一篇:JS數(shù)據(jù)結構與算法_棧&隊列下一篇:JS數(shù)據(jù)結構與算法_集合&字典 寫在前面 說明:JS數(shù)據(jù)結構與算法 系列文章的代碼和示例均可在此找到 上一篇博客發(fā)布以后,僅幾天的時間竟然成為了我寫博客以...

    NeverSayNever 評論0 收藏0
  • 前端面試總結--數(shù)據(jù)結構算法

    摘要:鏈表前端的面試中,鏈表還是經常會被問到。這種數(shù)據(jù)結構非常方便,提供了便利店語法來訪問它的元素。參考書籍推薦一個找組件的輪子工廠前端面試總結數(shù)據(jù)結構與算法一前端面試總結數(shù)據(jù)結構與算法二前端面試總結數(shù)據(jù)結構與算法三 鏈表 前端的面試中,鏈表還是經常會被問到。所以熟悉鏈表的結果以及鏈表操作的方法還是很重要的。說道存儲多個元素,數(shù)組可能是最常用的數(shù)據(jù)結構。這種數(shù)據(jù)結構非常方便,提供了便利店[]...

    superPershing 評論0 收藏0
  • Java數(shù)據(jù)結構算法——鏈表

    摘要:前言數(shù)據(jù)結構與算法專題會不定時更新,歡迎各位讀者監(jiān)督。本文介紹另一種數(shù)據(jù)結構鏈表,包括鏈表的特點特點鏈表的創(chuàng)建刪除插入和輸出,文末給出代碼和一道常見的關于鏈表的面試題。 聲明:碼字不易,轉載請注明出處,歡迎文章下方討論交流。 前言:Java數(shù)據(jù)結構與算法專題會不定時更新,歡迎各位讀者監(jiān)督。本文介紹另一種數(shù)據(jù)結構——鏈表,包括鏈表的特點特點、鏈表的創(chuàng)建、刪除、插入和輸出,文末給出java...

    CKJOKER 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<