数据结构之单向和双向链表

简介: 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

一、链表简介

1、链表概念

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点组成,节点可以在运行时动态生成,节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

2、基础特点

内存存储

03-1.png

逻辑结构

03-2.png

特点描述

  • 物理存储上是无序且不连续的;
  • 链表是由多个节点以链式结构组成;
  • 逻辑层面上看形成一个有序的链路结构;

链表结构解决数组存储需要预先知道元素个数的缺陷,可以充分利用内存空间,实现灵活的内存动态管理。

二、单向链表

1、基础描述

03-3.png

单向链表是链表的一种,其特点是链表的链接方向是单向的,链表的遍历要从头部开始顺序读取;结点构成,head指针指向第一个成为表头结点,终止于最后一个指向NULL的指针。

2、基础操作

添加数据

03-4.png

  • 初始化head节点,作为链表的头;
  • 修改当前末尾节点的next指针;
  • 新添加的节点房子在链表末尾;

删除数据

03-5.png

遍历找到要删除的节点,把删除节点前个节点的指针指向该删除节点的下个节点;

三、双向链表

1、概念描述

03-6.png

双向链表也叫双链表,是链表的一种,链表的每个数据结点中都有两个指针,分别指向直接后继和直接前驱,从双向链表中的任意一个结点开始,都可以很快速地访问它的前驱结点和后继结点,链表结构的使用多数都是构造双向循环链表。

2、基础操作

添加数据

03-7.png

  • 遍历找到链表的最后一个节点;
  • 修改当前末尾节点的next指针;
  • 新添加的节点房子在链表末尾;
  • 添加最新尾节点的prev指针;

删除数据

03-8.png

  • 双向链表,基于要删除节点操作即可;
  • 操作上图中要删除的Node2节点;
  • Node2.prev.next = Node2.next;
  • Node2.next.prev = Node2.prev;

通过上述流程的操作,就把链表中一个节点删除,剩下节点再度连接成链式结构。

3、源码分析

在Java的API中,LinkedList是典型的双向链表结构,下面基于LinkedList源码看双向链表的操作。

基础案例

public class M01_Linked {
    public static void main(String[] args) {
        List<User> userList = new LinkedList<>() ;
        User removeUser = new User(200,"Second") ;
        // 添加元素
        userList.add(new User(100,"First")) ;
        userList.add(removeUser) ;
        userList.add(new User(300,"Third")) ;
        System.out.println("初始化:"+userList);
        // 修改元素
        userList.get(0).setUserName("Zero");
        System.out.println("修改后:"+userList);
        // 删除元素
        userList.remove(removeUser) ;
        System.out.println("删除后:"+userList);
    }
}
class User {
    private Integer userId ;
    private String userName ;
    public User(Integer userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }
    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
    // 省略Get和Set方法
}

节点描述

节点三个核心描述:数据,next指针,prev指针。

private static class Node<E> {
    E item;         // 数据
    Node<E> next;   // 下个指针
    Node<E> prev;   // 上个指针
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

首位节点处理

基于LinkedList源码,首尾节点方式,针对上图双链表的首位指针特点,这里源码很好理解。

public class LinkedList {
    transient Node<E> first;
    transient Node<E> last;
    // 处理首节点
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
    }
    // 处理尾节点
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
    }
}

添加节点

添加节点的方法直接调用linkLast方法,把新节点放到链表的尾部即可。

public boolean add(E e) {
    linkLast(e);
    return true;
}

删除节点

第一步:遍历对比,找到要删除的节点;

public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

第二步:移除节点,重新搭建链表结构,并且把当前链表的数据置为null,并返回被移除的节点;

E unlink(Node<E> x) {
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;
    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }
    x.item = null;
    return element;
}

如上就是对Java中LinkedList双链表源码的部分结构分析,这种代码看多了,总感觉自己写的代码不是Java。

四、环形链表

在单链表中,将终端结点的指针域NULL改为指向表头结点或开始结点,这样就形成了环形链表:

03-9.png

环形链表链表的一种结构,特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。

相关文章
|
29天前
|
存储 缓存 算法
数据结构-链表(一)
链表(Linked List)是一种常见的数据结构,用于存储和组织数据。与数组不同,链表的元素(节点)在内存中不必连续存储,而是通过指针链接在一起。 链表由多个节点组成,每个节点包含两部分:数据(存储实际的元素值)和指针(指向下一个节点的引用)。链表的第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针通常指向空值(null)。
31 1
|
1月前
|
存储 C++
数据结构第六弹---带头双向循环链表
数据结构第六弹---带头双向循环链表
|
1月前
|
存储
【单链表】数据结构单链表的实现
【单链表】数据结构单链表的实现
|
1月前
|
C++
从0开始回顾数据结构---链表与堆
#include <iostream> #include <algorithm> #include <string.h> using namespace std; const int N = 100010; int h[N], ph[N], hp[N], cnt; void heap_swap(int a, int b) { swap(ph[hp[a]],ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); } void down(int u) { int t = u; if (u * 2 <= cnt &&
|
1月前
|
存储
【数据结构】双向带头循环链表的实现
【数据结构】双向带头循环链表的实现
|
1月前
【数据结构】单链表之--无头单向非循环链表
【数据结构】单链表之--无头单向非循环链表
|
1天前
|
存储 C语言
数据结构基础:双链表结构、实现
数据结构基础:双链表结构、实现
|
12天前
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
|
14天前
数据结构--链表刷题(一)快慢指针(上)
数据结构--链表刷题(一)快慢指针
16 0