面试官Q1:可以手写一个LinkedList的简单实现吗?

当听见手写一个具体类的实现的时候,是不是有点懵逼,其实在大多数面试中,要手写几率还是很小的,对一些工作了好几年的老油条,一般面试只是让你简单介绍一下LinkedList数据结构,但是对于应届毕业生,不管是单向链表还是双向链表,考到要手写的几率还是蛮大的,所以扎实的基本功还是必须的。废话不多说了,我们先来总结一下LinkedList有哪些特点:

  • LinkedList是基于链表实现;

  • 其存储数据放在节点里面;

  • 插入和删除操作效率高,无需遍历数据,打断节点连接,重新连接即可;

  • 遍历数据效率低,无法通过索引定位;


数据结构

LinkedList底层的数据结构是基于双向循环链表的,每个节点分为头部、尾部以及业务数据,前一个节点尾部指向后一个节点的头部,后一节点的头部指向前一个节点的尾部,对应的就是下面图示:

手写LinkedList的实现,彻底搞清楚什么是链表?_链表

但是对于头结点和尾节点比较特殊,头结点的头部没有上一个结点,从上图可知并没有指向,尾节点的尾部也没有指向下一个结点。
所以,如上我们可以知道链表是由一个一个节点构成,我们是不是可以定义一个结点类Node,如下:

 1//用来表示一个节点
2public class Node {
3     Node previous;//头部,用来指向上一个节点
4     Object obj;
5     Node next;//尾部,用来指向下一个节点
6
7    public Node() {
8    }
9
10    public Node(Node previous, Object obj, Node next) {
11        super();
12        this.previous = previous;
13        this.obj = obj;
14        this.next = next;
15    }
16
17    public Node getPrevious() {
18        return previous;
19    }
20
21    public void setPrevious(Node previous) {
22        this.previous = previous;
23    }
24
25    public Object getObj() {
26        return obj;
27    }
28
29    public void setObj(Object obj) {
30        this.obj = obj;
31    }
32
33    public Node getNext() {
34        return next;
35    }
36
37    public void setNext(Node next) {
38        this.next = next;
39    }   
40}


添加数据add

既然结点已经有了,我们是不是考虑将这些结点一个个串起来,形成我们想要的链表结构,定义add方法如下:

 1public class MyLinkedList {
2    private Node first;//定义一个头结点
3    private Node last;//定义一个尾节点
4    private int size;
5
6        //添加业务数据
7    public void add(Object obj){
8                //new一个结点出来
9        Node n = new Node();
10            //如果是一个新的链表,没有任何数据
11        if(first==null){
12        //从图示可知,这个时候新增的结点既是头结点也是尾节点,头结点的头部没有任何指向所以设置为null,
13       //尾节点的尾部没有任何指向,所以也为null,真正的业务数据放在obj属性里面
14            n.setPrevious(null);
15            n.setObj(obj);
16            n.setNext(null);
17
18            first = n;
19            last = n;
20        }else{
21            //这个时候我们添加下一个结点,直接往last节点后增加新的节点
22            n.setPrevious(last);
23            n.setObj(obj);
24            n.setNext(null);
25            //当前结点尾部要指向新添加进来的结点
26            last.setNext(n);
27            //此时,新加进来的结点就变成了尾节点
28            last = n;
29        }
30        size++;
31    }
32
33     public int size(){
34        return size;
35    }
36}

用一张图,描述上述add的过程,结点就这样被串起来了

手写LinkedList的实现,彻底搞清楚什么是链表?_链表_02

我们简单测试一下,add方法是否成功。

查询数据get

当我们add完数据后,如果想获取某个下标对应的结点,这个时候又该如何操作呢?LinkedList不像ArrayList可以直接通过索引下标定位到具体的数据,链表是一个个结点组成,当我们想要获取第三个结点时,需要一个个的去遍历,从头开始找直到找到为止

手写LinkedList的实现,彻底搞清楚什么是链表?_链表_03

对应代码如下:

 1public Object get(int index){   //2
2        // 0 1 2 3 4
3        Node temp = node(index);
4        if(temp!=null){
5            return temp.obj;
6        }
7        return null;
8    }
9
10    public Node node(int index){
11        Node temp = null;
12        if(first!=null){
13            temp = first;
14            for(int i=0;i<index;i++){
15                temp = temp.next;
16            }
17        }
18        return temp;
19    }

可想而知,如果这个链表数据量很大,这种方式去遍历效率该有多低,当然这个不是我们这节的重点,笔者只是想通过简单的方式,来阐述一下链表的实现原理。

删除数据remove

查询完数据后,现在我们来删数据,链表删数据效率是很高的,为什么效率高,看完下面这张图你就理解了:

手写LinkedList的实现,彻底搞清楚什么是链表?_链表_04

只需要打断原有的指向关系,重新连接指向,就可以删除指定位置处的数据,非常的高效。如果是ArrayList删除数据,上面2节点,将会被后面的3节点取代,它要移位,后面的所有节点都要跟着移位,所以ArrayList效率比较低。用一段代码,描述上述过程:

 1public void remove(int index){
2        Node temp = node(index);
3
4        if(temp!=null){
5            Node up = temp.previous;
6            Node down = temp.next;
7            up.next = down;
8            down.previous = up;
9            size--;
10        }
11
12    }


指定位置添加数据

如果在指定位置添加一条数据,又该如何实现呢?

 1public void add(int index,Object obj){
2        Node temp = node(index);
3
4        Node newNode = new Node();
5        newNode.obj = obj;
6
7        if(temp!=null){
8            Node up = temp.previous;
9            up.next = newNode;
10            newNode.previous = up;
11
12            newNode.next = temp;
13            temp.previous = newNode;
14
15            size++;
16        }
17    }

跟上面删除节点一个原理,打断原有指向,重新指向,数据就插入进去了。如下图:

手写LinkedList的实现,彻底搞清楚什么是链表?_链表_05

本文通过简单的代码以及图示,讲解了LinkedList的实现原理,当然JDK源码里面的LinkedList实现方式可没有这个这么简单,大家如果有兴趣可以去看一下源码,源码提供了各种使用方式。如果面试遇到手写LinkedList实现,你会了吗?

转自;https://mp.weixin.qq.com/s/kEooIdoqaHpRYniKu1BshA