zoukankan      html  css  js  c++  java
  • 链表

    链表不需要一块连续的内存空间来存储,它通过“指针”将一组零散的内存块串联起来使用
    三种最常见的链表结构:单链表、双向链表和循环链表

    单链表

    为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址

    从图中我们可以看出,针对链表的插入和删除操作,我们只需要考虑相邻结点的指针改变,所以对应的时间复杂度是 O(1)

    链表要想随机访问第 k 个元素,就没有数组那么高效了。因为链表中的数据并非连续存储的,所以无法像数组那样,根据首地址和下标,通过寻址公式就能直接计算出对应的内存地址,而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的结点。链表随机访问的性能没有数组好,需要 O(n) 的时间复杂度。

    设计一个基于对象的单链表

    //Node 类用来表示节点
    function Node(element) {
        this.element = element;
        this.next = null;
    }
    // 链表类
    function LinkedList() {
        this.head = new Node("head");
        this.find = find;
        this.insert = insert;
        this.remove = remove;
        this.display = display;
        this.findPrevious = findPrevious;
        this.remove = remove;
    }
    //该方法遍历链表,查找给定数据
    function find(item) {
        var currNode = this.head;
        while (currNode.element != item) {
            currNode = currNode.next;
        }
        return currNode;
    }
    //将新节点插入链表
    function insert(newElement, item) {
        var newNode = new Node(newElement);
        var current = this.find(item);
        newNode.next = current.next;
        current.next = newNode;
    }
    // 输出当前链表
    function display() {
        var currNode = this.head;
        while (!(currNode.next == null)) {
            console.log(currNode.next.element);
            currNode = currNode.next;
        }
    }
    // 查找待删除节点前面的节点
    function findPrevious(item) {
        var currNode = this.head;
        while (!(currNode.next == null) && (currNode.next.element != item)) {
            currNode = currNode.next;
        }
        return currNode;
    }
    // 删除节点
    function remove(item) {
        var prevNode = this.findPrevious(item);
        if (!(prevNode.next == null)) {
            prevNode.next = prevNode.next.next;
        }
    }
    var cities = new LinkedList();
    cities.insert("Conway", "head");
    cities.insert("Russellville", "Conway");
    cities.insert("Carlisle", "Russellville");
    cities.insert("Alma", "Carlisle");
    cities.display();
    cities.remove("Carlisle");
    cities.display();
    

    循环链表

    循环链表的尾结点指针是指向链表的头结点

    双向链表


    双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历

    
    

    链表 VS 数组性能大比拼


    和数组相比,链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高。

    将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
    针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。

    if (head == null) { head = new_node;} //插入头结点
    if (head->next == null) { head = null;} // 插入尾节点
    

    重点留意边界条件处理

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

    1. 如果链表为空时,代码是否能正常工作?
      2.如果链表只包含一个结点时,代码是否能正常工作?
      3.如果链表只包含两个结点时,代码是否能正常工作?
      4.代码逻辑在处理头结点和尾结点的时候,是否能正常工作?

    5个常见的链表操作

    1. 单链表反转
      输入: 1->2->3->4->5->NULL
      输出: 5->4->3->2->1->NULL
    /**
     * @param {ListNode} head
     * @return {ListNode}
     */
    var reverseList = function(head) {
        if(head == null || head.next == null){
            return head
        }
        const current = reverseList(head.next);
        //例如,1,2,3,4,5,null
        //current是5
        //head是4
        //head.next 是 5
        //head.next.next 就是5指向的指针,指向当前的head(4)
        //5-4-3-2-1-null
        head.next.next = head;
        //注意把head.next设置为null,切断4链接5的指针
        head.next = null
        //每层递归返回当前的节点,也就是最后一个节点。(因为head.next.next改变了,所以下一层current变4,head变3)
        return current;
    };
    
    1. 链表中环的检测
    2. 两个有序的链表合并删除
    3. 链表倒数第 n 个结点
      给定一个链表: 1->2->3->4->5, 和 k = 2.
      返回链表 4->5.
      解题思路:用两个指针,让第一个先走k步,然后两个指针一起移动,当第一个指针到最后一个节点处,第二个指针就在倒数第K个节点
    var getKthFromEnd = function(head, k) {
        let first = head,
            second = head;
        while (k !== 0) {
            first = first.next;
            k--;
        }
        while (first !== null) {
            first = first.next;
            second = second.next;
        }
        return second;
    };
    
    1. 求链表的中间结点
  • 相关阅读:
    tornado中form表单验证详解
    关于tornado中session的总结
    Linux常用命令
    css3动画属性详解 与超酷例子
    keepalive高可用的健康检查
    keepalive的nginx防火墙问题
    安装配置hadoop
    tmux的简单快捷键
    部署使用elk
    k8s搭建部署
  • 原文地址:https://www.cnblogs.com/jesse131/p/12732103.html
Copyright © 2011-2022 走看看