算法通关村第一关 | 链表青铜挑战笔记

一、 什么是链表?

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

二、链表的特点

链表是一种比较简单、很常见的数据结构,是线性表(List)的一种,是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
1、插入删除效率高:任意位置插入元素和删除元素效率较高,时间复杂度为O(1)
2、灵活度高:链表不需要预先分配固定大小的空间,可以随着元素的增加自动扩容,因此不会出现内存不足的情况。
3、空间分散:在内存中,元素的空间可以在任意地方,空间是分散的,不需要连续。
4、查找效率低:查找数据时效率低,时间复杂度为O(N),因为链表的空间是分散的,所以不具有随机访问性,如要需要访问某个位置的数据,需要从第一个数据开始找起,依次往后遍历,直到找到待查询的位置,故可能在查找某个元素时,时间复杂度达到O(N)。
5、空间利用率高:空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,故空间的利用率较高。

三、 如何构造链表

首先要先理解是怎么构建出链表,我们知道JVM里友栈区和堆区,栈区主要存引用,也就是一个指向实际对象的地址,而堆区存的才是创建的对象。
JVM构建链表图示:
Alt
实现单链表的完整代码:

public class BasicLink {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        Node node = getLinked(a);
        System.out.println(node);
    }

    private static Node getLinked(int[] a) {
        Node head = null;
        Node newNode2 = new Node(a[2]);
        Node newNode1 = new Node(a[1]);
        Node newNode0 = new Node(a[0]);
        head = newNode0;
        newNode0.next = newNode1;
        newNode1.next = newNode2;
        return head;

    }
}
class Node {
    public int val;//节点值
    public Node next;//指向下一个节点的引用

    public Node(int var) {
        this.val = var;
    }
}

我们debug一下看一下从head开始next会发现是这样的:
在这里插入图片描述
这就是一个简单的线性访问了,所以链表就是从head开始,逐个开始向后访问,而每次所访问对象的类型都是一样的。

四、遍历链表

对于单链表,不管进行什么操作,一定是从头开始逐个向后访问,所以操作之后是否还能找到表头非常重要。
Alt
代码如下:

 public void printLinkedList(Node head){
        while(head != null){
            System.out.print(head.val + "-");
            head = head.next;
        }
        System.out.println("null");
    }
五、链表的插入

单链表的插入,和数组的插入一样,过程不复杂,但是在编码时会发现处处时坑。单链表的插入操作需要考虑三种情况:首部、中部、尾部。

(1)表头插入

链表表头插入新结点非常简单,其实也就是在一辆火车的头部接入一个新的火车头。容易出错的是经常会忘了head需要重新指向表头。
Alt
代码如下:

Node newNode = new Node();
newNode.val = "新火车头";
newNode.next = head;
head = newNode
(2)中间插入
在中间位置插入,我们必须先遍历找到要插入的位置,然后将当前位置接入到前驱结点和后继结点之间,但是到了该位置之后我们却不能获得前驱结点了,也就无法将结点接入进来了。这就好比一边过河一边拆桥,结果自己也回不去了。
为此,我们要在目标结点的前一个位置停下来,也就是使用cur.next的值而不是cur的值来判断,这是链表最常用的策略。
例如下图中,如果要在7的前面插入,当cur.next = node(7)了就应该停下来,此时cur.val = 15。然后需要给newNode前后接两根线,此时只能先让new.next = node(15).nxet, 然后node(15).next = new,而且顺序还不能错。
想一想为什么不能颠倒顺序?

由于每个节点都只有一个next,因此执行了node(15).next=new之后,节点15和7之间的连线就自动断开了,如下图所示:
Alt

六、结尾插入

表尾插入就比较容易了,我们只要将尾结点指向新结点就行了。
Alt

七、链表删除

删除同样分为删除头部元素,删除中间元素和删除尾部元素。

(1)删除表头结点

只需要将头节点指向下一个节点即可。

if(head == null){ //判断链表是否为空列表
		return;
}
head = head.nxet;

Alt

(2)删除最后一个结点

我们只要找到尾节点的上一个节点,然后将其指向null就可以。例如下图中删除40,其前驱节点是7.遍历的时候需要判断cur.next是否为40,如果是,则只要执行cur.next = null即可,此时节点40变得不可达,最终会被JVM回收掉。
Alt

(3)删除中间结点

删除中间节点时,也会要用cur.next来比较,找到位置后,将cur.next指针的值更新为cur.next.next就可以解决,如下图所示:
Alt

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值