码迷,mamicode.com
首页 > 其他好文 > 详细

第五章-数据结构之链表

时间:2020-11-13 13:09:08      阅读:7      评论:0      收藏:0      [点我收藏+]

标签:odi   sel   临时   前端   @param   point   判断   void   提示   

链表简介

链表的概念:

  • 多个元素组成的列表
  • 元素存储不连续,用 next 指针连在一起

技术图片

数组和链表的对比:

  • 数组:增删非首尾元素时往往需要移动元素。
  • 链表: 增删非首尾元素时,不需要移动元素,只需要更改 next 的指向即可。

JS 中的链表

  • JavaScript 中没有链表这种数据结构
  • 但是可以用 Object 模拟链表
const a = { val: ‘a‘ };
const b = { val: ‘b‘ };
const c = { val: ‘c‘ };
const d = { val: ‘d‘ };

// 模拟链表
a.next = b;
b.next = c;
c.next = d;

// 遍历链表
let point = a;
while (point) {
  console.log(point.val);
  point = point.next;
}

// 插入
const e = { val: ‘e‘ };
c.next = e;
e.next = d;

// 删除
c.next = d;

LeetCode 237.删除链表中的节点

【题目描述】

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点

现有一个链表 -- head = [4,5,1,9],它可以表示为: 4->5->1->9

示例:

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

提示:

  • 链表至少包含两个节点。
  • 链表中所有节点的值都是唯一的。
  • 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
  • 不要从你的函数中返回任何结果。

【解题思路】

  • 将下一个节点的值赋值到当前节点
  • 将下一个节点的指向赋值为当前节点的指向

【复杂度分析】

  • 时间复杂度:O(1)
  • 空间复杂度:O(1)

【答案】

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} node
 * @return {void} Do not return anything, modify node in-place instead.
 */
var deleteNode = function(node) {
    // 将下一个节点的值赋值到当前节点
    node.val = node.next.val;
    // 将下一个节点的指向赋值为当前节点的指向
    node.next = node.next.next;
};

LeetCode 206 反转链表

【题目描述】

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

【解题思路】

  • 反转两个节点:
    • 将 n + 1 的 next 指向 n
    • 将 n + 1 赋值给 n
  • 反转多个节点:双指针遍历链表,重复上述操作(while)

【解题步骤】

  • 双指针一前一后遍历链表。
  • 反转双指针。

【复杂度分析】

  • 时间复杂度:O(n) --- while 循环
  • 空间复杂度:O(1) --- 没有矩阵、数组等

【答案】

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    let pre = null;
    let current = head;
    while (current) {
        // 临时存储当前指针的后续内容
        let tmp = current.next; 
        // 反转链表,将 current 的 next 指向 pre
        current.next = pre; 
        // 接收反转结果, 将 current 赋值给 pre 实现反转
        pre = current; 
        // 接收临时存储的后续内容,往后走
        current = tmp; 
    }
    return pre;
};

两数相加

【题目描述】

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

【解题思路】

  • 模拟相加操作,先加个位数,有进位则进位
  • 需要遍历链表

【解题步骤】

  • 新建一个空链表
  • 表里被相加的两个链表,模拟相加操作,将 个位数 追加到新链表上,将十位数留到下一位去相加

【复杂度分析】

  • 时间复杂度:O(n) --- while 循环以最长的那个链表为准
  • 空间复杂度:O(n) --- 新建了一个链表

【答案】

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var addTwoNumbers = function(l1, l2) {
    let l3 = new ListNode(0);
    let p1 = l1;
    let p2 = l2;
    let p3 = l3;
    // 暂存 十进位
    let carry = 0;
    // 遍历链表
    while(p1 || p2) {
        // 取到 p1 p2 的值
        let v1 = p1 ? p1.val : 0;
        let v2 = p2 ? p2.val : 0;
        let v3 = v1 + v2 + carry;
        // 暂存 十进位 的值
        carry = Math.floor(v3 / 10);
        // 将 p3 指向 p1 p2 之和的余数
        p3.next = new ListNode(v3 % 10);
        // 循环链表的条件
        if (p1) p1 = p1.next;
        if (p2) p2 = p2.next;
        // 向后推进一位
        p3 = p3.next;
    }
    // 在循环链表结束之后,最后两位相加有进位,那就将p3指向这个进位
    if (carry) {
        p3.next = new ListNode(carry);
    }
    // l3 是一个链表,p3 是 l3 的一个表头
    return l3.next;
};

LeetCode 83 删除排序链表中的重复元素

【题目描述】

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例:

输入: 1->1->2
输出: 1->2

输入: 1->1->2->3->3
输出: 1->2->3

【解题思路】

  • 因为链表是有序的,所以重复的元素一定相邻。
  • 遍历链表,如果当前元素和下个元素的值相同,就删除下个元素

【解题步骤】

  • 遍历链表,如果当前元素和下个元素的值相同,就删除下个元素的值
  • 遍历结束后,直接返回原链表的头部。

【复杂度分析】

  • 时间复杂度:O(n) ---- while 循环,循环的次数就是链表的长度
  • 空间复杂度:O(1) ---- 没有新的链表

【答案】

var deleteDuplicates = function(head) {
    let p = head;
    // 保证当前节点和下个节点都存在
    while(p && p.next) {
        // 如果当前节点的值 与 后一个节点的值相同,
        // 就将当前节点指向 第三个节点
        if (p.val === p.next.val) {
            p.next = p.next.next;  
        } else {
            // 如果不相同,就直接往后推进即可。
            p = p.next;
        }
    }
    return head;
};

LeetCode 141 环形链表

【题目描述】

  • 给定一个链表,判断链表中是否有环。

  • 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

  • 如果链表中存在环,则返回 true 。 否则,返回 false 。

示例:

技术图片

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

【解题思路】

  • 操场跑圈,两个人速度一快一慢,如果不考虑时间,速度快的一方一定追到速度慢的一方。
  • 使用一快一慢两个指针遍历链表,如果能够相逢,那么链表就有圈。返回 ture
  • 遍历结束后,还没有相逢就返回 false

【复杂度计算】

  • 时间复杂度:O(n) --- while循环
  • 空间复杂度:O(1) --- 没有复杂的数据结构

【答案】

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    // 慢指针
    let p1 = head;
    // 快指针
    let p2 = head;
    // 如果p2存在,那么p1一定存在;
    // 如果p2.next不存在,那就不是闭环了
    while (p2 && p2.next) {    
        p1 = p1.next;
        p2 = p2.next.next;
        if (p1 === p2) {
            return true;
        }
    }
    return false
};

前端与链表:JS 中的原型链

原型链:

  • 原型链本质上是一个链表
  • 原型链上的节点是各种原型对象,如Function.prototype、Object.prototype
  • 原型链通过 __proto__ 属性连接各个原型对象
obj -> Object.prototype -> null
arr -> Array.prototype -> Object.prototype -> null
fun -> Function.prototype -> Object.prototype -> null

原型链相关知识点

  • 如果 A 沿着原型链能找到 B.prototype ,那么 A instanceof B 为 true

    arr -> Array.prototype -> Object.prototype -> null
    
  • 如果在 A 对象上没有找打 x 属性,那么会沿着原型链找 x 属性

面试题一:

简述 instanceof 原理,并用代码实现。

  • 知识点:如果 A 沿着原型链能找到 B.prototype ,那么 A instanceof B 为 true
  • 解法:遍历 A 的原型链,如果找到 B.prototype,那么就返回 true ,否则返回 false
const instanceOf = (A, B) => {
  let p = A;
  while (p) {
    if (p === B.prototype) {
      return true
    }
    p = p.__proto__;
  }
  return false;
}

const res = instanceOf(1, Array);
console.log(res); // false

使用链表指针获取 JSON 的节点值

const json = {
  a: { b: { c: 1 } },
  d: { e: { f: 2 } }
}

let path = [‘a‘, ‘b‘, ‘c‘];

let p = json;
path.forEach(key => {
  p = p[key];
})
console.log(p); // 1

第五章-数据结构之链表

标签:odi   sel   临时   前端   @param   point   判断   void   提示   

原文地址:https://www.cnblogs.com/aurora-ql/p/13945301.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!