手写代码:LinkedList


一、链表特点以及原理

查询慢,增删快。

这个分为两种情况,在头尾增加,查询、和在中间增删,查询;

在头尾进行:

在头尾进行增删,直接在存储头尾对象的first、last添加上新加的元素即可(效率极高);

在头尾进行查询,因为内部优化,大于一半从后往前查询,效率方面也是可以的。

在中间进行:

在中间查询,需要把位数走一遍,也就是需要查询索引5的内容,就要创建(5-1)次的对象,时间及其慢。

在中间删除,要把上面查询的步骤走一遍以后,在把指定的元素删除,再把其前后两个对象(链表),连接起来。

二、单个实现原理解析

1、添加元素add方法(添加内容)

分为添加第一个元素和其它元素两种:

第一个元素: 存储的首个元素(first)和末尾元素(last)都是当前设置的元素;

其它元素: 先将两个元素进行绑定、设置最后一个元素的next为null,存储的最后一个元素为当前元素;

2、toString方法

利用StringBuilder创建对象,从first元素递归获取每一个存储的对象(element)值,存储进StringBuilder中。

3、get方法(根据索引)

调用7、检测索引,调用4、获取指定对象,并从对象中获取元素(element)的值;

4、获取指定索引的对象(索引)

分两种情况,索引大于有效长度一半(size),从后侧开始检索,不满足从左侧;

根据当前对象存储的下一个对象的内容,进行递归判断,直到找到自己想要的次数对象为止;

5、remove方法(删除指定索引位置元素)

调用4、获取指定对象,分情况进行删除(1、开头;2、中间位置;3、末尾位置),长度减一(size–)

开头: 根据当前元素获取后一个元素对象,设置存储的首个元素(first)为后一个元素;

中间: 获取当前元素前后两个元素对象,并把这两个元素进行连接;

末尾: 根据当前元素获取前一个元素对象,设置存储的末尾元素(last)为前一个元素;

6、add方法(根据索引添加到指定位置)

调用4、获取指定对象,分情况进行添加(1、开头;2、中间位置;3、末尾位置),长度加一(size++)

开头: 创建两个元素连接,设置存储的首个元素(first)为当前对象;

中间: 获取当前元素前一个元素对象,将前一个元素与当前元素连接,将当前元素与后一个元素连接;

末尾: 创建两个元素连接,设置存储的末尾元素(last)为当前对象;

7、索引检测

判断索引的数组内容进行判断,不满足则抛出运行时异常(RuntimeException)

三、完整增加步骤

1、创建基础结构,添加add、toString方法

Node存储元素对象

public class Node {
    Node privious;  //上一个节点
    Node next;      //下一个节点
    Object element;   //元素数据

    public Node(Node privious, Node next, Object element) {
        this.privious = privious;
        this.next = next;
        this.element = element;
    }

    public Node(Object element) {
        this.element = element;
    }
}

MyLinkedList工具类

public class MyLinkedList {

    private Node first;
    private Node last;

//    1、添加元素add方法
    public void add(Object obj) {
        Node node = new Node(obj);
        
        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的开头为上一个元素对象(最后一个对象)
            node.privious = last;
//            元素的最后为null
            node.next = null;
//            上一个元素设置为后一个对象
            last.next = node;
//            设置最后元素为现在的对象
            last = node;
        }
    }

//    2、toString方法
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        Node temp = first;
        while (temp != null) {
            sb.append(temp.element + ",");
            temp = temp.next;
        }
        sb.setCharAt(sb.length() - 1, ']');
        return sb.toString();
    }

调用测试:

    public static void main(String[] args) {
        MyLinkedList list = new MyLinkedList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        String s = list.toString();
        System.out.println(s);
    }
}

难点解析:

是设置新创建的元素的对象里面的,前一个元素地址(privious)和后一个元素地址(next)

//元素的开头为上一个元素对象(最后一个对象)
node.privious = last;
//元素的最后为null
node.next = null;

是设置创建的上一个元素里面的,后一个元素地址(next)的值,其中last存储的上一个元素对象的地址值;

//上一个元素设置为后一个对象
last.next = node;
//设置最后元素为现在的对象
last = node;

2、增加get方法

增加size变量,并添加size的增加方法

    private int size;

    //    1、添加元素add方法
    public void add(Object obj) {
        Node node = new Node(obj);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的开头为上一个元素对象(最后一个对象)
            node.privious = last;
//            元素的最后为null
            node.next = null;
//            上一个元素设置为后一个对象
            last.next = node;
//            设置最后元素为现在的对象
            last = node;
        }
        size++;
    }

增加get方法

    //    3、get方法
    public Object get(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引数字不合法" + index);
        }
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近与开头位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近于末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp.element;
    }

3、get方法优化、增加remove方法

get方法优化

    //    3、get方法
    public Object get(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引数字不合法" + index);
        }
        //调用封装的getNode方法获取指定索引位置对象
        Node temp = getNode(index);
        return temp != null ? temp.element : null;
    }

    //    4、封装的获取指定索引的对象
    public Node getNode(int index) {
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近与开头位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近于末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp;
    }

增加remove方法

    //        5、remove方法
    public void remove(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引数字不合法" + index);
        }

        Node temp = getNode(index);
        if (temp != null) {
            Node before = temp.privious;
            Node after = temp.next;

            if (before != null) {
                before.next = after;
            }
            if (after != null) {
                after.privious = before;
            }
//            被删除的元素是第一个元素时
            if (index == 0) {
                first = after;
            }
//            被删除的元素时最后一个元素时
            if (size == index - 1) {
                last = before;
            }
            size--;
        }
    }

4、根据索引添加元素

//    6、add方法
    public void add(int index,Object obj){
        if (index<0||index>size-1){
            throw new RuntimeException("请输入指定范围索引 "+index);
        }
        Node newNode = new Node(obj);
        Node temp = getNode(index);

        if (temp!=null){
            Node before = temp.privious;
//            1、添加的元素在第一个时
            if (index==0){
                newNode.next=temp;
                temp.privious=newNode;
                first=newNode;
            }
//            2、添加的元素在最后一个时
            if (index==size-1){
                newNode.privious=temp;
                temp.next=newNode;
                last=newNode;
                size++;
                return;
            }
//            3、添加的元素在普通位置时
            if (before!=null){
//                新建元素与前一个创建连接
                before.next=newNode;
                newNode.privious=before;
//                新建元素与后一个创建连接
                newNode.next=temp;
                temp.privious=newNode;
            }
            size++;
        }
    }

5、增加索引检测、泛型

索引检测:

    //    7、索引检测
    private void checkRange(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引数字不合法: " + index);
        }
    }

泛型:

public class MyLinkedList<E> {

    private Node first;
    private Node last;
    private int size;

    //    1、添加元素add方法
    public void add(E element) {
        Node node = new Node(element);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的开头为上一个元素对象(最后一个对象)
            node.privious = last;
//            元素的最后为null
            node.next = null;
//            上一个元素设置为后一个对象
            last.next = node;
//            设置最后元素为现在的对象
            last = node;
        }
        size++;
    }

四、完整版代码

public class MyLinkedList<E> {
    
    private Node first;     //存储第一个元素对象
    private Node last;      //存储最后一个元素对象
    private int size;       //有效的索引长度(存有值的位数)

    //    1、添加元素add方法(添加内容)
    public void add(E element) {
        Node node = new Node(element);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的开头为上一个元素对象(最后一个对象)
            node.privious = last;
//            元素的最后为null
            node.next = null;
//            上一个元素设置为后一个对象
            last.next = node;
//            设置最后元素为现在的对象
            last = node;
        }
        size++;
    }

    //    2、toString方法
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        Node temp = first;
        while (temp != null) {
            sb.append(temp.element + ",");
            temp = temp.next;
        }
        sb.setCharAt(sb.length() - 1, ']');
        return sb.toString();
    }

    //    3、get方法(根据索引)
    public E get(int index) {
//        检测索引
        checkRange(index);
        //调用封装的getNode方法获取指定索引位置对象
        Node temp = getNode(index);
        return temp != null ? (E)temp.element : null;
    }

    //    4、获取指定索引的对象
    private Node getNode(int index) {
//        检测索引
        checkRange(index);
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近与开头位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近于末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp;
    }

    //        5、remove方法(删除指定索引位置元素)
    public void remove(int index) {
//        检测索引
        checkRange(index);

        Node temp = getNode(index);
        if (temp != null) {
            Node before = temp.privious;
            Node after = temp.next;

            if (before != null) {
                before.next = after;
            }
            if (after != null) {
                after.privious = before;
            }
//            被删除的元素是第一个元素时
            if (index == 0) {
                first = after;
            }
//            被删除的元素时最后一个元素时
            if (size == index - 1) {
                last = before;
            }
            size--;
        }
    }

    //    6、add方法(根据索引添加到指定位置)
    public void add(int index, E element) {
//        检测索引
        checkRange(index);

        Node newNode = new Node(element);
        Node temp = getNode(index);

        if (temp != null) {
            Node before = temp.privious;
//            1、添加的元素在第一个时
            if (index == 0) {
                newNode.next = temp;
                temp.privious = newNode;
                first = newNode;
            }
//            2、添加的元素在最后一个时
            if (index == size - 1) {
                newNode.privious = temp;
                temp.next = newNode;
                last = newNode;
                size++;
                return;
            }
//            3、添加的元素在普通位置时
            if (before != null) {
//                新建元素与前一个创建连接
                before.next = newNode;
                newNode.privious = before;
//                新建元素与后一个创建连接
                newNode.next = temp;
                temp.privious = newNode;
            }
            size++;
        }
    }

    //    7、索引检测
    private void checkRange(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引数字不合法: " + index);
        }
    }
}
posted @ 2020-02-09 14:17  ah_lydms  阅读(117)  评论(0编辑  收藏  举报