链表

链表和数组不同,数组是必须要连续内存空间,链表是通过指针将零碎的内存空间串联起来

 

常见的链表有单链表,双向链表,循环链表

 

单向链表

单向链表每个数据后面都会有一个叫next的结点,头结点是记录链表的基地址,也就是链表第一个元素,尾结点指向null,代表链表结束,其他元素的结点均是指向下个元素。

所以,链表在查询的时候是要一个个遍历元素,直到找到目标元素,而数组访问是通过偏移公式,直接算出地址,于是数组的查询效率就会比链表快,而数组增删会比链表复杂,因为要挪移内存空间

 

循环链表

循环链表其实就是尾结点指向头结点,形成循环

 

循环链表从链尾到链头会比单向链表更方便,所以一般处理具有循环结构的数据时就是用循环链表,比如约瑟夫环

 

双向链表

 

整个链表的两端由first和last两个节点类型指向,每个对象都有pre,next两个节点,分别用来指向前一个对象和后一个对象,item是该对象的有效信息,举个简单的例子来模拟双向链表

public class Test {

    public static void main(String[] args) {
        Node liuBei = new Node("刘备");
        Node guanYu = new Node("关羽");
        Node zhangFei = new Node("张飞");
        Node zhuGeliang = new Node("诸葛亮");

        liuBei.next = zhuGeliang;
        zhuGeliang.next = guanYu;
        guanYu.next = zhangFei;
        guanYu.pre = zhuGeliang;
        zhangFei.pre = guanYu;

        Node temp = liuBei;
        while (temp != null){
            System.out.println(temp.name);
            temp = temp.next;
        }
    }
}

class Node {
    public String name;
    public Node pre;
    public Node next;

    public Node(String name) {
        this.name = name;
    }
}

 

双向链表由于多了个pre结点,所以内存空间比单链表要多,但是也正因为这样,双向链表能双向遍历,这样链表的插入,删除操作能更快,更简单

 

这其中涉及到一个重要的设计思想:空间换时间,时间换空间

当我们空间充足时,可以选择空间复杂度较高,时间复杂度较低的数据结构和算法,比如缓存技术,而如果内存紧缺,比如代码跑在手机上或单片机这些时,可以选择和前面相反的策略

 

链表和数组的性能比较

 

注意,数据简单易用,在内存上是连续的空间,这对cpu的缓存机制很友好,可以有效预读数据,提高访问效率,但是链表是分开的,所以不能进行有效预读。

而且,链表容易造成内存碎片,对于java来说,会造成频繁的GC

数组的缺点是大小固定,一经声明就要占用整块连续内存空间。如果声明的数组过大,系统可能没有足够的连续内存空间分配给它,导致“内存不足(out of memory)”。如果声明的数组过小,则可能出现不够用的情况。这时只能再申请一个更大的内存空间,把原数组拷贝进去,非常费时。链表本身没有大小的限制,天然地支持动态扩容,我觉得这也是它与数组最大的区别

 

链表注意事项

①经常用来检查链表代码是否正确的边界条件有这样几个:

 检查链表代码是否正确的边界条件有这样几个:如果链表为空时,代码是否能正常工作?

 如果链表只包含一个结点时,代码是否能正常工作?

 如果链表只包含两个结点时,代码是否能正常工作?

 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?

 

posted @ 2022-04-15 16:21  codemelo  阅读(76)  评论(0)    收藏  举报