数据结构-链表及LeetCode相关题目分析

80 阅读4分钟

什么是链表

元素存储不连续,用next指针连在一起

链表和数组的区别?

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

JS中没有链表这个数据结构,可以通过对象/构造函数的方式来模拟

// 1.使用对象方式-----------------------------
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 p = a; //声明一个指针p,指向a
while (p) {
  console.log(p.val);
  p = p.next;
}

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

// 删除
c.next = d;

// 2.使用构造函数的方式-----------------------------
const Node = function (data) {
   this.data = data;
   this,next = nul1
}

const node1 = new Node(1);
const node2 = new Node(2);
const node3 = new Node(3);
node1.next = node2;
node2 next = node3:

LeetCode题

237 删除链表中的节点

单链表找不到父节点,只能通过next找到子节点。

如何不拿到上一个节点,还能删除这个节点呢?

和下一个节点进行交换,就是让被删除节点等于下一个节点。此时就相当于删除了下一个节点。

var deleteNode = function(node) {
    node.val = node.next.val
    node.next = node.next.next
};

时间复杂度:没有任何循环所以 是 o1
空间复杂度:没有数组或矩阵 也是 o1

206反转链表

前提:反转2个节点:反转两个节点:将n+1的next 指向n。

思路:2个指针,一前一后2个指针

var reverseList = function(head) {
    let p1 = head;
    let p2 = null;
    while(p1){
       const tmn = p1.next
        p1.next = p2
        p2 = p1;
        p1 = tmn
    }
    return p2
};

时间复杂度:有一个while循环体显然复杂度o(n)
空间复杂度:我们的临时变量是单个值没有数组和矩阵,所以是o(1)

2 两数相加

思路: 新建空链表(输出的新链表)
遍历两个相加的链表。模拟相加操作
将个位数追加到新链表,十位数留到下一位相加。

var addTwoNumbers = function(l1, l2) {
    const l3 = new ListNode(0) //新建链表节点 空节点
    let p1 = l1 //新建指针指向链表的头部
    let p2 = l2
    let p3 = l3

    let carry = 0
    while(p1||p2){//p1 或 p2 一个有值就执行循环
    //两个链表可能长短不一 所以要判断是否存在 不存在就当他是0 
        const v1 = p1? p1.val : 0
        const v2 = p2? p2.val : 0
        const val = v1 + v2 + carry
        carry = Math.floor(val/10) //当val大于9时 获取val 十位上的数,然后下一轮循环加上,如果不大于9就为空
        p3.next = new ListNode(val%10)
        if(p1) p1 = p1.next
        if(p2) p2 = p2.next
        p3 = p3.next
    }
    if(carry){ //如果最后一位相加大于10 循环已经结束了 自己加上
        p3.next = new ListNode(carry)
    }
    return l3.next //因为我们新建的链表 是在空节点的后面 不包括他自己 所以要从next开始

时间复杂度:有while 故o(n) n为两个链表中的长度大的值。 空间复杂度:o(n) 没有数组 矩阵,但是有一个新造的链表

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

思路:

遍历链表 如果发现当前元素和下个元素值相同,就删除 下个元素

var deleteDuplicates = function(head) {
    let p = head
    while(p && p.next){ //确保p下一个元素也存在
        if(p.val === p.next.val){
            p.next = p.next.next
        }else{//前后两个不同我才变,如果相同我就不变为下一个,我就看看和下下个一样不
             p = p.next
        }     
    }
    return head
};
};

时间复杂度:O(n) ,因为有一个while n就是链表长度
空间复杂度:O(1)没有额外存储任何线性增长的变量。没有数组矩阵链表。

141. 环形链表

用一快(走两步)一慢(走一步)两个指针遍历链表,如果指针能够重逢,返回true

var hasCycle = function(head) {
    let p1 = head
    let p2 = head
    while(p1 && p2 && p2.next){ //都有值的情况下循环。 如果p2.next 没有值,那 p2.next.next会报错。
        p1 = p1.next
        p2 = p2.next.next
        if(p1 === p2){
            return true
        }
    }
    return false
};

时间复杂度:while循环体 O(n) 空间复杂度:O(1)

由链表引发的js原型链的思考

原型链的本质是链表

原型链通过-proto-属性链接各种原型对象(链表是通过next连接的)

obj -> Object.prototype -> null
func -> Function.prototype -> Object.prototype -> null
arr -> Array.prototype -> Object.prototype -> null

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

原理:如果A沿着原型链能找到B.prototype,那么A instanceof B 为 ture

const instanceOf = (A,B)=>{
  let p = A
  while(p){
    if(p === B.prototype){
      return true
    }
    p = p.__proto__
  }
  return false
}

2.原型对象及原型链衍生题

var foo = {}
var F = function(){}

Object.prototype.a = 'value a'
Function.prototype.b = 'value b'

console.log(foo.a)// 'value a'
console.log(foo.b)//undefined

console.log(F.a)// 'value a'
console.log(F.b)//'value b'

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

const json = {
  a:{b:{c:1}},
  d:{e:2}
}
//path是一个数组 记录了json属性的路径
const path = ['a','b','c']
//我们要求沿着这个路径最后得到的节点值是多少

let p = json  //定义一个指针
path.forEach(k=>{
  p = p[k]
})

const path = ['a','b']
得到 {c:1}