【数据结构】线性表之单向链表


链表通常指的是单向链表,他包含多个节点,每个节点都有一个纸箱下一个几点的next指针。表中最后一个节点指向NULL,表示链表的结束。
image.png
下面是一个基于Java 声明的链表节点

public class Node<T extends Serializable> {
  // 节点数据
  private  T data;

  // 指向的Next 节点
  private Node<T> next;
}
  • 遍历链表
  • 在链表中插入一个元素
  • 在链表中删除一个元素

1.1 链表的遍历

假设表头节点是指向链表的第一个节点,完成链表的遍历只需要执行以下步骤。

  1. 沿指针遍历
  2. 显示当前指针所指向的内容
  3. 将当前节点指向 Next 节点
  4. 当 Next 节点指向NULL的时候遍历结束
  public static <T extends Serializable> void linkedListForeach(Node<T> headNode) {
    Node<T> currentNode = headNode;
    // 4. 遍历终止条件,Next指向NULL
    while (currentNode != null) {
      // 2.输出当前节点的数据
      System.out.println(currentNode.getData() + "->");
      // 3. 将当前节点指向Next 节点
      currentNode = currentNode.getNext();
    }
  }


根据同样的逻辑,也可以根据此实现链表长度的计算。

  private static <T extends Serializable> int length(Node<T> headNode) {
    Node<T> currentNode = headNode;
    int count = 0;
    while (currentNode != null) {
      count++;
      currentNode = currentNode.getNext();
    }
    return count;
  }

1.2 链表插入元素

链表中插入元素分为三种,分别是在链表头部插入元素,在链表中间插入元素以及在链表尾部插入元素。需要注意的这里的链表的位置指的在p位置插入元素,在插入完成之后,链表的p位置指向的是新的节点。

1.2.1 在链表的头部插入新的节点

image.png

其逻辑步骤是:

  • 首先将新的节点的指针指向表头节点
  • 将表头节点指向新的节点



1.2.2 在链表尾部插入节点


image.png




在链表尾部插入新的节点也非常简单,需要将新的节点的NEXT 指针指向NULL,然后将原链表的尾指针执行新节点,即可。

1.2.3 在链表中间插入节点

订单的


  • 首先定位到需要插入的节点位置,断开其 Next 节点,注意需要保存 Next 节点的引用
  • 将当前节点的 Next 指针执行新的节点
  • 将新节点的 Next 指针指向 Next 节点

1.2.4 源码示例

  // 时间复杂度 O(n) 空间复杂度 O(1)
  private static <T extends Serializable> Node<T> insert(
      Node<T> headNode, Node<T> insertNode, int position) {
    
    // 判断长度,插入的位置是否合法
    int length = length(headNode);
    if (position <= 0 || position > length) {
      throw new IllegalArgumentException();
    }
    

    // 如果是链表头部,直接指向Next节点
    if (position == 1) {
      insertNode.setNext(headNode);
      return insertNode;
    }

    // 如果不是头部,那么获取到当前插入位置的节点
    Node<T> currentNode = findByPosition(headNode, position - 1);
    if (currentNode == null) {
      return headNode;
    }

    // 保存当前插入位置节点的Next引用
    Node<T> next = currentNode.getNext();
    // 当前节点的Next 指向新的节点
    currentNode.setNext(insertNode);
    // 新的节点的Next 指向Next节点
    insertNode.setNext(next);

    return headNode;
  }

1.3 链表删除元素

同插入类似,链表删除也分为三种情况,分别是在删除链表头部元素,删除链表中间元素以及在删除链表尾部元素。

1.3.1 删除链表头部节点


image.png
删除头部节点元素非常简单,只需要将原头部节点的NEXT指向NULL,然后将表头指针指向原头结点的Next节点即可。

1.3.2 删除链表尾部节点

image.png


删除尾部节点也非常简单,只要将尾部节点的前驱的Next节点设置为NULL,即可




1.3.3 删除链表中间的节点


image.png


删除链表的中间节点的步骤是:

  • 定位当前节点为需要删除节点的前驱位置,当前节点的后驱就是即将删除的节点
  • 将删除节点的Next 指向NULL
  • 将当前节点的Next指针执行将要删除的节点的Next节点。

1.3.4 代码示例

 // 时间复杂度 O(n) 空间复杂度 O(1) 
 private static <T extends Serializable> Node<T> remove(Node<T> headNode, int position) {
    int length = length(headNode);
    if (position <= 0 || position > length) {
      throw new IllegalArgumentException();
    }
    // 如果是删除头部节点,直接返回头部节点的Next
    if (position == 1) {
      headNode.setNext(null);
      return headNode.getNext();
    }

    // 定位到删除节点的前驱节点,以及即将删除的节点
    Node<T> preNode = findByPosition(headNode, position - 1);
    Node<T> currentNode = findByPosition(headNode, position);

    // 如果前驱节点存在,则将前驱节点的Next设置为删除节点的Next
    if (preNode != null) {
      Node<T> next = Objects.isNull(currentNode) ? null : currentNode.getNext();
      preNode.setNext(next);
    }
    return headNode;
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值