什么是链表
元素存储不连续,用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}