[数据结构]P1.1 链表结构

* 注: 本文/本系列谢绝转载,如有转载,本人有权利追究相应责任。 2019年4月8日

Stan Zhang 2019年4月8日  格物致知,经世致用。

[面试题]1.为什么要用链表?

 数组具有的缺陷: 数组是长度固定类型固定的,并且它取值快,插入和删除慢

 链表正弥补了这样的不足,它是长度都可以灵活扩展的,另外它是插入和删除快,但是取值非常慢

查找也比较慢,这是这种线性结构的通病,需要通过散列的思路进行解决,当然一个例外是

有下标的有序线性结构可以使用二分查找提高查找效率

因此没有最好的,只有最合适的数据结构。

2.链表的分类.

A.单链表:

B:双端链表

C:有序链表 : 没有什么突出的优点,只是为了迎合扩展性(长度扩展)以及业务

故名思意,有序链表的序列是有序的。

先比较一下有序数组,有序数组牺牲插入效率来提升查找效率,配合二分查找(利用数组索引)可以LogN级别定位元素。

但是有序链表中有序并不能提升其查找效率(它没有数组的索引)。

 

List本生就是按照添加的顺序有序的,再引入一个排序标准没有必要,因此Java当中没有SortedList.

而Set本身没有顺序规则,可以设置一个排序,因此当需要使用有序的集合时,我们可以使用SortedSet.

 

D:双向链表

 

 

3.实现

A.单链表

 单链表的特点是头部插入快,其他无论是查找、尾部插入还是删除,效率都是N级别的。

Java代码的实现:

package ds1.linked.table.singleDirection;

/**
 * 单向链表
 * 单向链表的表中会存留一个链表的起始端 Node first,这导致从尾部插入数据非常麻烦需要花费O(N)的时间
 */
public class SingleDirectionList {
    static class Node {
        Node next;
        String data;
        Node(String data){
            this.data = data;
        }
    }

    static class LinkedList{
        Node first; // 保存第一个节点

        public LinkedList(){
        }

        public LinkedList(Node first) {
            this.first = first;
        }

        public void foreachPrint(){
            if(first == null){
                System.out.println("[]");
            }else{
                System.out.print("[" + this.first.data);
                Node current = first.next;
                while(current != null){
                    System.out.print("," + current.data);
                    current = current.next;
                }
                System.out.println("]");
            }
        }

        /**
         * 插入节点的时间复杂度: O(1)
         * @param node
         */
        public void addNode(Node node){
            if(first == null){
                this.first = node;
            }else{
                node.next = this.first;
                this.first = node;
            }
        }

        /**
         * 删除节点的时间复杂度: O(N)
         * @param node
         */
        public void removeNode(Node node){
            if(this.first == null){
                return;
            }

            if(this.first.equals(node)){
                this.first = this.first.next;
                node.next = null;
                return;
            }
            Node current = this.first;
            Node before = current;
            while(current.next != null){
                if(current.equals(node)){
                    before.next = current.next;
                    node.next = null;
                    break;
                }
                before = current;
                current = current.next;
            }
        }

        /**
         * 查找节点的时间复杂度: O(N)
         * @param data
         * @return
         */
        public Node findNodeByData(String data){
            Node result = null;
            Node current = this.first;
            while(current != null){
                if(current.data != null && current.data.equals(data)){
                    result = current;
                    break;
                }
                current = current.next;
            }
            return current;
        }



    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        Node node1 = new Node("node1");
        Node node2 = new Node("node2");
        Node node3 = new Node("node3");
        Node node4 = new Node("node4");
        Node node5 = new Node("node5");
        Node node6 = new Node("node6");
        Node node7 = new Node("node7");
        linkedList.addNode(node1);
        linkedList.addNode(node2);
        linkedList.addNode(node3);
        linkedList.addNode(node4);
        linkedList.addNode(node5);
        linkedList.addNode(node6);
        linkedList.addNode(node7);
        linkedList.foreachPrint();
        Node tmpNode4 = linkedList.findNodeByData("node4");
        linkedList.foreachPrint();
        linkedList.removeNode(tmpNode4);
        linkedList.foreachPrint();
    }
}

结果:

[node7,node6,node5,node4,node3,node2,node1]
[node7,node6,node5,node4,node3,node2,node1]
[node7,node6,node5,node3,node2,node1]

 

B.双端链表

双端链表具有两个端点指针,分别指向收尾指针。它具有首位快速插入,首部快速删除的能力。但是因为还是单向的,因此尾部删除性能不佳。

Java代码:

package ds1.linked.table.twoHead;

import ds1.linked.table.singleDirection.SingleDirectionList;

/**
 * 双端列表
 * 有两个端点
 */
public class TwoHeadLinkedList {
    static class Node{
        Node next;
        String data;

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

    static class LinkedList{
        Node first;
        Node tail;

        public LinkedList(){
        }

        public LinkedList(Node node){
            this();
            addNodeToHead(node);
        }

        /**
         * 从首部添加一个节点
         * 常数时间
         * @param node
         */
        public void addNodeToHead(Node node){
            if(this.first == null){
                this.first = node;
                this.tail = node;
            }else{
                node.next = this.first;
                this.first = node;
            }
        }

        /**
         * 删除头节点
         * 常数时间
         */
        public void removeHead(){
            if(this.first != null){
                if(this.first == this.tail){
                    this.tail = null;
                }
                this.first = this.first.next;
            }
        }

        /**
         * 添加尾部节点
         * 常数时间
         * @param node
         */
        public void addNodeTail(Node node){
            if(this.tail != null){
                this.tail.next = node;
                this.tail = node;
            }else{
                this.first = this.tail = node;
            }
        }

        /**
         * 删除尾部节点
         * 线性时间
         */
        public void removeTailNode(){
            if(this.first == null){
                return;
            }
            if(this.first == this.tail){
                this.first = this.tail = null;
                return;
            }

            Node current = this.first;
            Node before = current;
            while(current.next != null){
                before = current;
                current = current.next;
            }
            before.next = null;
            this.tail = before;
        }



        /**
         * 查找一个节点
         * 时间复杂度: 线性时间
         * @param data
         * @return
         */
        public Node findByData(String data){
            Node result = null;
            if(this.first == null){
                return result;
            }
            Node current = this.first;
            while(current != null){
                if(current.data != null && current.data.equals(data)){
                    result = current;
                    break;
                }
                current = current.next;
            }

            return result;
        }

        /**
         * 删除一个节点
         * 时间复杂度:线性时间
         * @param node
         */
        public void removeNode(Node node){
            if(this.first == null){
                return;
            }

            if(this.first.equals(node)){
                this.first = node.next;
            }

            Node current = this.first;
            Node before = current;
            while(current != null){
                if(current == node){
                    before.next = current.next;
                    break;
                }
                before = current;
                current = current.next;
            }

            if(this.tail == node){
                this.tail = before;
            }
        }

        public void foreachPrint(){
            if(first == null){
                System.out.println("[]");
            }else{
                System.out.print("[" + this.first.data);
                Node current = first.next;
                while(current != null){
                    System.out.print("," + current.data);
                    current = current.next;
                }
                System.out.println("]");
            }
        }

    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        Node node1 = new Node("node1");
        Node node2 = new Node("node2");
        Node node3 = new Node("node3");
        Node node4 = new Node("node4");
        Node node5 = new Node("node5");
        Node node6 = new Node("node6");
        Node node7 = new Node("node7");
        linkedList.addNodeToHead(node1);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node2);
        linkedList.foreachPrint();
        linkedList.addNodeTail(node3);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node4);
        linkedList.foreachPrint();
        linkedList.addNodeTail(node5);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node6);
        linkedList.foreachPrint();
        linkedList.addNodeTail(node7);
        linkedList.foreachPrint();
        Node tmpNode4 = linkedList.findByData("node4");
        linkedList.foreachPrint();
        linkedList.removeNode(tmpNode4);
        linkedList.foreachPrint();
    }

}

结果:

[node1]
[node2,node1]
[node2,node1,node3]
[node4,node2,node1,node3]
[node4,node2,node1,node3,node5]
[node6,node4,node2,node1,node3,node5]
[node6,node4,node2,node1,node3,node5,node7]
[node6,node4,node2,node1,node3,node5,node7]
[node6,node2,node1,node3,node5,node7]

 

C:有序链表 : 没有什么突出的优点,只是为了迎合扩展性(长度扩展/类型扩展)以及业务

package ds1.linked.table.orderedList;

public class OrderedList {
    static class Node{
        Node next;
        long data;

        public Node(long data) {
            this.data = data;
        }
    }

    static class LinkedList{
        Node first;

        LinkedList(){
        }

        public void foreachPrint(){
            if(first == null){
                System.out.println("[]");
            }else{
                System.out.print("[" + this.first.data);
                Node current = first.next;
                while(current != null){
                    System.out.print("," + current.data);
                    current = current.next;
                }
                System.out.println("]");
            }
        }

        /**
         * 插入过程,线性时间
         * @param node
         */
        public void insert(Node node){
            if(this.first == null){
                this.first = node;
                return;
            }

            if(this.first.data > node.data){
                node.next = this.first;
                this.first = node;
                return;
            }

            Node current = this.first;
            Node before = current;
            while(current != null){
                if(current.data > node.data){
                    node.next = current;
                    before.next = node;
                    break;
                }
                before = current;
                current = current.next;
            }

            if(current == null){
                before.next = node;
            }

        }

        /**
         * 查找过程: 不能利用二分查找,还是线性时间
         * @param data
         */
        public Node findByValue(long data){
            Node result = null;
            Node current = this.first;
            while (current != null){
                if(current.data == data){
                    result = current;
                    break;
                }
                current = current.next;
            }
            return result;
        }
    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.insert(new Node(100));
        linkedList.foreachPrint();
        linkedList.insert(new Node(200));
        linkedList.foreachPrint();
        linkedList.insert(new Node(122));
        linkedList.foreachPrint();
        linkedList.insert(new Node(39));
        linkedList.foreachPrint();
        linkedList.insert(new Node(103));
        linkedList.foreachPrint();
        linkedList.insert(new Node(145));
        linkedList.foreachPrint();
    }

}

结果:

[100]
[100,200]
[100,122,200]
[39,100,122,200]
[39,100,103,122,200]
[39,100,103,122,145,200]

 

D:双向链表

双向链表很好的利用了双端和双向的优势,使得双端的插入删除操作变为常数级别。

 

package ds1.linked.table.twoHeadTwoDirection;


/**
 * 双向链表
 * 有双端点双向,也就意味着两头的插入删除效率都是线性的。
 */
public class TwoHeadTwoDirectionList {
    static class Node {
        Node next;
        Node before;
        String data;
        Node(String data){
            this.data = data;
        }
    }

    static class LinkedList{
        Node first; // 保存第一个节点
        Node tail; // 保留最后一个节点

        public LinkedList(){
        }

        public LinkedList(Node first) {
            this.first = this.tail = first;
        }

        public void foreachPrint(){
            if(first == null){
                System.out.println("[]");
            }else{
                System.out.print("[" + this.first.data);
                Node current = first.next;
                while(current != null){
                    System.out.print("," + current.data);
                    current = current.next;
                }
                System.out.println("]");
            }
        }

        /**
         * 插入头节点的时间复杂度:线性
         * @param node
         */
        public void addNodeToHead(Node node){
            if(first == null){
                this.first = this.tail = node;
            }else{
                node.next = this.first;
                this.first = node;
            }
        }

        /**
         * 删除头节点的时间复杂度:线性时间
         */
        public void removeNodeFromHead(){
            if(this.first != null){
                Node tmp = this.first.next;
                this.first.next = null;
                this.first = tmp;
            }
        }

        /**
         * 尾部新增一个节点
         * 线性时间
         * @param node
         */
        public void addNodeToTail(Node node){
            if(this.tail == null){
                this.tail = node;
            }else{
                node.before = this.tail;
                this.tail.next = node;
                this.tail = node;
            }
        }

        /**
         * 从尾部删除节点
         * 线性时间
         */
        public void removeNodeFromTail(){
            if(this.tail != null){
                Node tmp = this.tail;
                this.tail = this.tail.before;
                this.tail.next = null;
                tmp.before = null;
            }
        }


        /**
         * 删除节点的时间复杂度: O(N)
         * @param node
         */
        public void removeNode(Node node){
            if(this.first == null){
                return;
            }

            if(this.first.equals(node)){
                this.first = this.first.next;
                node.next = null;
                return;
            }
            Node current = this.first;
            Node before = current;
            while(current.next != null){
                if(current.equals(node)){
                    before.next = current.next;
                    before.next.before = before;
                    node.next = null;
                    node.before = null;
                    break;
                }
                before = current;
                current = current.next;
            }
        }

        /**
         * 查找节点的时间复杂度: O(N)
         * @param data
         * @return
         */
        public Node findNodeByData(String data){
            Node result = null;
            Node current = this.first;
            while(current != null){
                if(current.data != null && current.data.equals(data)){
                    result = current;
                    break;
                }
                current = current.next;
            }
            return current;
        }



    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        Node node1 = new Node("node1");
        Node node2 = new Node("node2");
        Node node3 = new Node("node3");
        Node node4 = new Node("node4");
        Node node5 = new Node("node5");
        Node node6 = new Node("node6");
        Node node7 = new Node("node7");
        linkedList.addNodeToHead(node1);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node2);
        linkedList.foreachPrint();
        linkedList.addNodeToTail(node3);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node4);
        linkedList.foreachPrint();
        linkedList.addNodeToTail(node5);
        linkedList.foreachPrint();
        linkedList.addNodeToTail(node6);
        linkedList.foreachPrint();
        linkedList.addNodeToHead(node7);
        linkedList.foreachPrint();
        Node tmpNode4 = linkedList.findNodeByData("node4");
        linkedList.foreachPrint();
        linkedList.removeNode(tmpNode4);
        linkedList.foreachPrint();
        linkedList.removeNodeFromHead();
        linkedList.foreachPrint();
        linkedList.removeNodeFromTail();
        linkedList.foreachPrint();
        linkedList.removeNodeFromTail();
        linkedList.foreachPrint();
        linkedList.removeNodeFromHead();
        linkedList.foreachPrint();
    }
}

 结果:

[node1]
[node2,node1]
[node2,node1,node3]
[node4,node2,node1,node3]
[node4,node2,node1,node3,node5]
[node4,node2,node1,node3,node5,node6]
[node7,node4,node2,node1,node3,node5,node6]
[node7,node4,node2,node1,node3,node5,node6]
[node7,node2,node1,node3,node5,node6]
[node2,node1,node3,node5,node6]
[node2,node1,node3,node5]
[node2,node1,node3]
[node1,node3]

 

posted @ 2019-04-18 08:52  yosql473  阅读(235)  评论(0编辑  收藏  举报