带你详细了解链表


一、什么是链表?

链表(Linked List)是一种常见的数据结构,它由节点(Node)组成,每个节点包含两部分:数据域(存储元素值)和指针域(指向下一个节点)。链表使用指针来连接各个节点,形成一个逻辑上的序列。

常见的链表类型有单向链表、双向链表和循环链表。

  1. 单向链表(Singly Linked List):每个节点只包含一个指针,指向下一个节点。最后一个节点指向 null,表示链表的结束。

  2. 双向链表(Doubly Linked List):每个节点包含两个指针,分别指向前一个节点和后一个节点。首节点的前置指针和尾节点的后置指针都指向 null。

  3. 循环链表(Circular Linked List):在单向链表或双向链表的基础上,将尾节点的指针指向首节点(或将首节点的前置指针指向尾节点),形成一个环形结构。

二、链表的实现

1.链表的方法

首先我们将所有的方法放在一个接口里。

将链表的常见操作放在接口中,可以方便类的实现对这些操作进行统一的定义和实现。这样做的好处就是可以在不同的类中使用相同的方法名,而无需考虑具体实现细节。接口定义了一组行为规范,同一个接口的多个实现类可以共用该接口提供的方法,从而实现代码的复用和扩展。对于链表这种数据结构,许多常见的操作都是固定的,将其定义在接口中可以让其他类更容易地调用和重用这些方法。此外,将链表的常见操作封装在接口中,可以使代码更加抽象化,并隐藏底层实现细节。这有助于提高代码的可读性、可维护性和可扩展性,同时也可以使代码更加安全,减少出错的可能性。

同时我们创建一个链表。

1.1头插法

void addFirst (int data);

@Override
    public void addFirst(int data) {
       ListNode node = new ListNode(data);

       if(head == null){
           this.head = node;
       }else {
           node.next = head;
           this.head = node;
       }
    }

  1. 创建一个新节点 ListNode,并将其数据域设置为 data。
  2. 检查链表是否为空(即 head 是否为 null)。如果链表为空,说明此时新节点就是链表中唯一的节点,将 head 指向新节点即可。
  3. 如果链表不为空,将新节点的 next 指针指向原来的头节点,然后将 head 指向新节点。这样就实现了在链表头部插入新节点的操作。

1.2尾插法

void addLast(int data);

@Override
    public void addLast(int data) {
     ListNode node = new ListNode(data);

     ListNode cur = this.head;
     if (head == null){
         this.head = node;
     }else{
         while (cur.next != null){
             cur = cur.next;
             }
         cur.next = node;
     }
    }
  1. 创建一个新节点 ListNode,并将其数据域设置为 data。
  2. 声明一个指针 cur,初始化为头节点 head。
  3. 检查链表是否为空(即 head 是否为 null)。如果链表为空,说明此时新节点就是链表中唯一的节点,将 head 指向新节点即可。
  4. 如果链表不为空,通过循环找到链表的最后一个节点,即 cur.next 为 null 的节点。
  5. 将最后一个节点的 next 指针指向新节点,即将新节点插入到链表的尾部。

1.3任意位置插入,第一个数据节点为0号下标

void addIndex(int index,int data);

@Override
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {

            return;
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }

        ListNode cur = searchPrev(index);
        ListNode node = new ListNode(data);

        node.next = cur.next;
        cur.next = node;
    }

1.4查找是否包含关键字为key的节点

boolean contains(int key);

public ListNode FindKthToTail(int k){
        if(k <= 0 || head ==null){
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;

        int count = 0;
        while (k-1 !=count) {
            fast = fast.next;
            //处理k太大的问题
            if(fast == null){
                return null;
            }
            count++;
        }
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

  1. 首先,检查输入的 k 是否小于等于 0 或者链表 head 是否为 null。如果满足其中任一条件,说明无法找到倒数第 k 个节点,返回 null。

  2. 初始化两个指针 fast 和 slow,均指向头节点 head。使用一个计数器 count 来记录当前遍历到的节点位置,初始值为 0。

  3. 在 while 循环中,当 count 不等于 k-1 时,将 fast 指针向后移动一位,并递增 count 的值。这样做的目的是为了使 fast 指针领先 slow 指针 k-1 个节点。

  4. 在移动 fast 指针时,需要处理 k 大于链表长度的情况。即当 fast 指针移动到末尾时(fast == null),说明 k 太大,无法找到倒数第 k 个节点,返回 null。

  5. 当 fast 指针移动到末尾时,slow 指针所指的节点即为倒数第 k 个节点。

  6. 最后,返回 slow 指针所指的节点作为结果。

1.5删除第一次出现关键字为key的节点

void remove(int key);

 @Override
    public void remove(int key) {
        if(this.head == null){
            return;
        }
    if(this.head.val == key){
        this.head = this.head.next;
        return;
    }

    ListNode cur = findPrev(key);

    if(cur == null){
        System.out.println("没有你要删除的数字");
        return;
    }

    ListNode del = cur.next;
    cur.next = del.next;
    }

    private ListNode findPrev(int key){
        ListNode cur = this.head;
        while (cur.next != null){
            if(cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }
  1. 首先,检查头节点 head 是否为 null。如果链表为空,则直接返回。

  2. 检查头节点的值是否等于要删除的值 key。如果相等,说明要删除的节点就是头节点,将头节点的下一个节点作为新的头节点即可,然后返回。

  3. 如果头节点不是要删除的节点,需要找到要删除节点的前一个节点 cur。调用 findPrev 方法来寻找对应值为 key 的节点的前一个节点。

  4. 当 cur 为 null 时,说明找不到要删除的值 key,输出提示信息并返回。

  5. 如果能找到要删除的节点的前一个节点 cur,将要删除的节点 del 设置为 cur 的下一个节点 cur.next。

  6. 将 cur 的 next 指针指向 del 的下一个节点 del.next,从而跳过 del 节点,实现删除操作。

1.6打印

void display();

@Override
    public void display() {
        ListNode cur = this.head;
        while (cur != null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
    }


总结

以上就是今天要讲的内容,有什么不对的地方欢迎大家指正。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

所遇皆随风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值