2,常见数据结构-链表

基础知识
链表是一种物理存储单元上非连续的一种数据结构,看名字我们就知道他是一种链式的结构,就像一群人手牵着手一样。链表有单向的,双向的,还有环形的。

1,单向链表

我们先定义一个最简单的单向链表结点类

    class Node<E> {
        E data;
        Node<E> next;

        Node(E data, Node<E> next) {
            this.data = data;
            this.next = next;
        }
    }

这个类非常简单,他只有两个变量,一个是data值,一个是指向下一个结点的指针。我们先来看一下单向的链表是什么样的,


在这里插入图片描述

链表头是不存储任何数据的,他只有指向下一个结点的指针,当然如果我们不需要头结点,直接拿第一个结点当做头结点也是可以的,像下面这样


在这里插入图片描述

单向链表的增删

链表不像数组那样,可以通过索引来获取,单向链表查找的时候必须从头开始往后一个个找,而不能从中间找,也不能从后往前找。我们来看一下链表的添加

1,添加到尾结点

在这里插入图片描述

添加到尾结点比较简单,我们只需要找到之前链表的尾结点,然后让他的next指针指向新的结点即可。

2,添加到中间结点

在这里插入图片描述

添加到中间结点,分为两步,比如我们要在ab结点之间添加新结点n,第一步让新结点n的指针指向b,然后再让a的指针指向新结点n即可。

3,删除链表的尾结点

在这里插入图片描述

只需要让尾结点的上一个结点的指针指向null即可。

4,删除链表的中间结点

在这里插入图片描述

只需要把要删除结点的前一个结点的指针指向要删除结点的下一个结点即可,最好还要把要删除结点的数据清空,并且让他的指针指向null。

2,单向环形链表

环形链表一般分为两种,一种是单向环形链表,一种是双向环形链表。我们首先来看一下单向的环形链表是什么样的


在这里插入图片描述

他和单向非环形链表的区就是,单向非环形链表的尾结点的指针是指向null的,而环形的是指向头结点。关于他的增删和单向非环形的差不多,只不过在尾结点的时候会有点区别,我们主要来看下这两种

1,添加到尾结点

在这里插入图片描述

2,删除尾结点

在这里插入图片描述

3,双向链表

我们来定义一个双向链表结点的类

    class Node<E> {
        E data;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E data, Node<E> next) {
            this.data = data;
            this.next = next;
            this.prev = prev;
        }
    }

双向链表不光有指向下一个结点的指针,而且还有指向上一个结点的指针,他比单向链表多了一个指向前一个结点的指针,单向链表我们只能从前往后找,而双向链表我们不光可以从前往后找,而且还可以从后往前找。我们来看一下双向链表是什么样的。


在这里插入图片描述

双向链表我们可以从头到尾查找,也可以从尾到头查找,双向链表在代码中最常见的就是LinkedList了(java语言),双向链表的增删和单向链表的增删很类似,只不过双向链表不光要调整他指向的下一个结点,还要调整他指向的上一个结点。这里我们来结合图形和代码的方式来分析一下双向链表的增删。

1,添加到尾结点

我们结合着代码来看下

    void linkLast(Object e) {
        final Node<Object> last = tail;
        final Node<Object> newNode = new Node<>(last, e, null);
        tail = newNode;
        if (last == null)
            head = newNode;
        else
            last.next = newNode;
        size++;
    }

总共分为4步


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2,添加到头结点

    private void linkFirst(Object e) {
        //用临时变量firt保存head结点
        final Node<Object> first = head;
        //创建一个新的结点,让他的pre指针指向null,next指针指向head结点
        final Node<Object> newNode = new Node<>(null, e, first);
        //让head指向新的结点
        head = newNode;
        //如果之前的first为空,说明之前链表是空的,让head和tial同时指向新结点即可
        if (first == null)
            tail = newNode;
        else//如果之前链表不为空,让之前first结点的pre指向新的结点
            first.prev = newNode;
        size++;
    }

添加到头结点和添加到尾结点很类似,图就不在画了,大家可以看下上面的代码。

3,添加到指定结点之前

比如我们在a结点之前添加一个指定的结点,先来看下代码

    void linkBefore(Object e, Node<Object> a) {
        final Node<Object> pred = a.prev;
        final Node<Object> newNode = new Node<>(pred, e, a);
        a.prev = newNode;
        if (pred == null)
            head = newNode;
        else
            pred.next = newNode;
        size++;
    }

假如我们在a结点之前添加一个结点,图就不在细画,简单看一下即可


在这里插入图片描述

4,删除链表的尾结点

 private Object unlinkLast(Node<Object> last) {
     final Object element = last.data;
     final Node<Object> prev = last.prev;
     last.data = null;
     last.prev = null;
     tail = prev;
     if (prev == null)//如果只有一个结点,把尾结点删除,相当于把链表清空了
         head = null;
     else
        prev.next = null;
    size--;
    return element;
}

如果链表只有一个结点的话,我们把它删除,相当于直接把链表清空了,这种很好理解,就不再画。下面画一个长度大于1的链表,然后删除最后一个结点


在这里插入图片描述

5,删除链表的头结点

    private Object unlinkFirst(Node<Object> first) {
        final Object element = first.data;
        final Node<Object> next = first.next;
        first.data = null;
        first.next = null;
        head = next;
        if (next == null)
            tail = null;
        else
            next.prev = null;
        size--;
        return element;
    }
在这里插入图片描述

6,删除链表的中间结点

    Object unlink(Node<Object> x) {
        final Object element = x.data;
        final Node<Object> next = x.next;//x的前一个结点
        final Node<Object> prev = x.prev;//x的后一个结点

        if (prev == null) {//前一个结点是空
            head = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {//后一个结点是空
            tail = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.data = null;
        size--;
        return element;
    }
在这里插入图片描述

4,双向环形链表

双向环形链表在代码中最常见的就是LinkedHashMap了,这个一般用于图片缓存的比较多一些,LRUCache这个类里面主要使用的就是LinkedHashMap(java语言中),通过上面的分析,如果对linkedList能理解的话,那么双向环形链表也就不难理解了,其实原理都差不多,这里就不在过多介绍,下面是双向环形链表的图。

在这里插入图片描述
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容