算法通关村第一关——青铜挑战(链表基本问题)

青铜挑战——链表的基本问题

⚡️关关难过关关过。第一关:链表的基本问题(java)
☕️内容:1、构造链表;2、链表的增删改查

一、链表介绍

  • 在Java中的链表是一种常见的数据结构,用于保存一系列的节点(Node)组成的序列。每个节点包含一个数据元素(数据域)和一个指向下一个节点的引用(指针域),链表中的节点可以通过引用连接在一起,形成一个链式结构。
  • 常用的有单链表、双链表,循环链表在Java集合阅读源码的时候经常看到数组、链表、红黑树,因此学习链表对理解底层源码有一定帮助。

二、初始化链表

1、画一个朴实无华的单链表,每一个ListNode都具有数据域和指针域两部分。
朴实无华的单链表
2、内部类定义结点

static class ListNode {
    
    
        public int val;
        public ListNode next;

        ListNode(int val) {
    
    
            this.val = val;
            next = null;
        }
    }

3、初始化链表

private static ListNode initLinkedList(int[] array) {
    
    
        // 头结点和当前结点
        ListNode head = null, cur = null;
        for (int i = 0; i < array.length; i++) {
    
    
            ListNode newNode = new ListNode(array[i]);
            if (i == 0) {
    
    
                head = newNode;
                cur = newNode;
            } else {
    
    
                // 这里就是用cur当作一个指针辅助构建链表
                cur.next = newNode;
                // 每次需要重新指向新的结点
                cur = newNode;
            }
        }
        return head;
    }

初始化的图如下:
一开始定义head和cur结点指向null,因为此时还没有任何元素。遍历array,每次遍历就new一个新的结点,第一次的时候直接让head指向newNode,在此之后就不需要再动了,cur结点也指向newNode。
当继续往下加结点的时候,cur相当于游标,不断地使上一个结点地next指针指向下一个结点,然后自己指向下一个结点,也就是cur一直重复做下1和2步骤。
在这里插入图片描述

三、遍历

就是很简单的利用游标,一开始让他指向头结点,不为空就一直往下遍历,直接上代码。

public static int getListLength(ListNode head) {
    
    
        int len = 0;
        // 定义一个游标指向这个链表
        ListNode node = head;
        while (node != null) {
    
    
            len++;
            node = node.next;
        }

        return len;
    }

下图就是游标不断向下遍历的过程
在这里插入图片描述

四、链表的插入

一共有三类情况

  • 头插 时间复杂度(O(1))
  • 表中 时间复杂度(O(n))
  • 尾插 时间复杂度(O(1))
public static ListNode headAdd(ListNode head, ListNode newNode, int position) {
    
    
        if (head == null) {
    
    
            // 这里就是直接将新的结点作为头结点return
            return newNode;
        }
        // 不是空链表的话,看下链表当前长度
        int length = getListLength(head);
        if (position > length + 1 || position < 1) {
    
    
            // 为什么是length + 1,如果正好是length + 1的时候,就是尾插
            System.out.println("插入位置越界,插入失败");
            return head;
        }
        if (position == 1) {
    
    
            // 这里千万不要是node = head.next,head.next是第二个结点,head直接就是头结点
            newNode.next = head;
            // 也可以直接return newNode;因为可以直接认为newNode是头结点了
            head = newNode;
            return head;
        }
        // 定义游标在表中或者表尾使用
        ListNode pNode = head;
        int count = 1;
        // 到这里position 至少是2
        while (count < position - 1) {
    
    
            pNode = pNode.next;
            count++;
        }
        newNode.next = pNode.next;
        pNode.next = newNode;

        return head;
    }

头插示图
在这里插入图片描述
尾插示图
在这里插入图片描述
中间插入
在这里插入图片描述
从上图也可以看出为啥先是newNode.next = pNode.next然后是pNode.next = newNode。这里的pNode就像是游标,根据传的position来指向某个结点,通过断开next引用,接收新的结点插进来。

五、链表删除

删除的时间复杂度同插入,也有头删除,中间删除和尾删除,只要画出图来总体来说还是不难的。

 /**
     * 删除结点
     *
     * @param head     头结点
     * @param position 下标从 1 开始
     * @return 删除后的头结点
     */
    public static ListNode deleteNode(ListNode head, int position) {
    
    
        if (head == null) {
    
    
            return null;
        }
        int len = getListLength(head);

        // 由于链表长度就是len,不能删除(len + 1)处的结点数据
        if (position > len || position < 1) {
    
    
            System.out.println("输入的position有误");
            return head;
        }
        if (position == 1) {
    
    
            // 直接返回head的next指向的结点就可
            return head.next;
        } else {
    
    
            ListNode pNode = head;
            int count = 1;
            // 此时的position肯定>1,要处理position的结点就要定位到游标指到position-1处
            while (count < position - 1) {
    
    
                pNode = pNode.next;
                count++;
            }
            pNode.next = pNode.next.next;
        }

        return head;
    }

在这里插入图片描述
链表的删除非常好理解,从图中得知,我们要删除第二个结点,那么我们position就要定位到头结点,这也是为什我们从中间删除要用count<position-1。接着直接让pNode的next指向它指向的结点的next指向的结点(可能有点绕,看图非常好理解),这样就直接删除中间的结点。
由于插入可以尾插,也就是说比如链表有5个结点,我可以在第6个位序插入,但是删除不可以,我不能删除位序为6的结点(根本不存在)。

六、总结

单链表的定义以及增删改,还是比较容易的,只需要画画图,然后想一想while里面的条件就可以啦。

扫描二维码关注公众号,回复: 17275130 查看本文章

猜你喜欢

转载自blog.csdn.net/losthief/article/details/131784929