• 链表的设计


    链表和数组之间最大的区别就是,链表不需要一块连续的内存地址,它无需任何的寻址公式来进行下一个元素的寻找,只需要存储对应的引用就可以做到。

    数组在进行删除的时候,除非是删除数组尾,否则就需要进行数组的重新整理。在平时开发的时候,可能使用的ArrayList<?>,?一般都可以是Obeject的子类,所以null也可以进行占位,可以不进行重新整理,但是严谨一点的话,理论上是需要进行重新整理的。而链表的特点就是增加和删除。直接对Node的指针进行操作就可以完成。

    至于查询方法,如果Array是有序的,那么Array的查询是会比Linked快,如果都不是有序的,需要从头遍历。那么时间复杂度理论上是一致的。

    假设我们做个容器进行数据的暂存。Array的特点是,时间天然有序,并且存储时候只需要一个数据的引用就行,但是虚拟内存上需要一块连续的空间。如果使用Linked的话,同样可以做到时间的有序性,但是有个不好的地方就是它存储的数据是 原数据+引用的合集。说白了就是会多一个对象出来,空间上损耗比Array大一点,但是基本无影响。

    链表中有几个分类,单链表,双链表,循环链表。

    单链表在使用的时候,有个问题就是例如你要删除一个元素,当你遍历到某个元素的时候,你删除了这个元素,但是你已经无法得知之前的节点了,需要重新遍历一遍。当然使用哨兵来存储之前prev节点会是一个很好的选择。在我看链表数据结构的时候,很直观的一个感觉就是链表的重点应该是如何安置哨兵。哨兵的存在可以节约很多的操作,对应的就是空间换时间。

    一般手写链表的时候几个地方要注意下:内存泄露,闭环以及指针丢失。

    import java.util.LinkedList;
    
    /**
     * description: LinkedListDemo
     * date: 2020/12/1 8:53
     * 单链表练习
     * @author: SmartCat
     * version: 1.0.0
     */
    public class LinkedListDemo<T>  {
    
    
        //头节点
        private ScNode<T> head = new ScNode<>(null,null);
    
        //当前结点,默认一开始为头节点
        private ScNode<T> cur  = head;
    
        //大小
        private int size;
    
        //内部类定义节点
        static class ScNode<T>{
            private T t;
            private ScNode next;
    
            public ScNode(T t,ScNode next){
                this.t = t;
                this.next  = next;
            }
    
            public ScNode(T t){
                this.t = t;
            }
    
            @Override
            public String toString(){
                if (t==null){
                    return "头节点为空";
                }
                return t.toString();
            }
        }
    
        public void add(T t){
            ScNode node = new ScNode(t);
            cur.next = node;
            cur = node;
            size++;
        }
    
        public void remove(T t){
            ScNode scNode = null;
            ScNode prev = null;
            //空链表则跳出
            if (head.next==null){
                return;
            }
            //第一个Node
            scNode = head.next;
            prev = head;
            while (scNode!=null){
                if (scNode.t.equals(t)){
                    prev.next = scNode.next;
                    size--;
                    return;
                }else {
                    prev = scNode;
                    scNode  = scNode.next;
                }
            }
        }
    
        public int getSize() {
            return size;
        }
    
    
        @Override
        public String toString(){
            StringBuilder builder = new StringBuilder();
            ScNode scNode = head.next;
            while (scNode!=null&&scNode.next!=null){
                builder.append(scNode.t.toString()).append(" ");
                scNode = scNode.next;
            }
            return builder.toString();
        }
    
        public void getCur(){
            System.out.println(cur.toString());
        }
    
        public void reverse(){
            //如果是空链表,无需反转
            if (head.next==null){
                return;
            }
    
            //反转需要三个节点
            ScNode prev = this.head;
            ScNode cur = prev.next;
            head.next = null;
            while (cur!=null){
                ScNode next = cur.next;
                cur.next = prev;
                prev = cur;
                cur = next;
                this.head = prev;
            }
        }
    
        public boolean check(){
            ScNode scNode1 = this.head;
            ScNode scNode2 = this.head;
    
            while (scNode1.next!=null&&scNode2.next.next!=null){
                scNode1 = scNode1.next;
                scNode2 = scNode2.next.next;
                if (scNode1.equals(scNode2)){
                    return false;
                }else {return true;}
            }
            return false;
        }
    
        public static LinkedListDemo<Integer>  merge(LinkedListDemo<Integer> list1,LinkedListDemo<Integer> list2){
            LinkedListDemo<Integer> list= new LinkedListDemo<>();
    
            if (list1.head.next==null||list2.head.next==null){
                return list;
            }
    
            ScNode<Integer> node1 = list1.head.next;
            ScNode<Integer> node2 = list2.head.next;
            ScNode<Integer> node = list.head;
    
            while (node1!=null||node2!=null){
                if (node1==null&&node2!=null){
                    node.next = node2;
                    node2 = node2.next;
                    node = node.next;
                }
                if (node2==null&&node1!=null){
                    node.next = node1;
                    node1 = node1.next;
                    node = node.next;
                }if (node1!=null&&node2!=null){
                    if (node1.t>node2.t){
                        node.next = node2;
                        node2 = node2.next;
                        node = node.next;
                    }else {
                        node.next = node1;
                        node1 = node1.next;
                        node = node.next;
                    }
                }
            }
            return list;
        }
    
        public static void main(String[] args) {
            LinkedListDemo<Integer> demo = new LinkedListDemo<>();
            for (int i = 10; i <= 15; i++) {
                demo.add(i);
            }
            System.out.println(demo.toString());
            System.out.println("**************");
            LinkedListDemo<Integer> demo1 = new LinkedListDemo<>();
            for (int i = 5; i <= 15; i++) {
                demo1.add(i);
            }
            System.out.println(demo1.toString());
            LinkedListDemo<Integer> linkedListDemo = LinkedListDemo.merge(demo, demo1);
            System.out.println(linkedListDemo.toString());
    
    
        }
    
    
    }

    同样的空间换时间的思想还有:快慢指针思想。至于双链表的提出,使用哨兵可以解决的情况下,为什么还需要存prev节点呢?

    我个人认为一个是在多线程的时候,哨兵的争抢过于频繁,另外一个就是因为双链表本身就具有了单链表的功能,只是在存储上会多一个前向指针。所以在不考虑空间浪费的情况下,双链表更通用,覆盖面更广。

  • 相关阅读:
    10查询结果排序
    11汇总和分组数据
    09查询基础
    07修改数据
    08删除数据
    06插入数据
    PHP 判断数组是否为空的5大方法
    Mysql模糊查询like效率,以及更高效的写法
    经典面试题golang实现方式(一)
    php调用c语言编写的so动态库
  • 原文地址:https://www.cnblogs.com/SmartCat994/p/14072549.html
Copyright © 2020-2023  润新知