链表简介
1. 是什么?
-
多个元素组成的列表
-
元素存储不连续 , 用
next
指针连在一起
2. 链表与数组的关系
相同: 都是多个元素组成的列表
不同:
- 数组中增删非首尾的元素 ,是需要移动大部分的元素 ==》 数组中常用 split() 进行增删
- 链表增删 不需要移动元素 , 仅仅改变next 指针的指向即可
3. 链表的常见操作
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;
// 增加
const m = {val : 'm'};
c.next = m;
m.next = d;
//遍历链表
let p = a;
while (p){
console.log(p.val);
p = p.next;
}
// 删除
c.next = d;
刷题
1. 237. 删除链表中的节点 - 力扣(LeetCode) (leetcode-cn.com)
思路:
- 链表的特点是只能获取下一节点
- 因此 删除此节点的最好方法就是将下一节点的值迁移 ,将本节点指向下下节点 即删除下一节点
/**
* 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;
};
2.206. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)
思路:
- 核心: 链表直接转化方向
- 遍历链表 双指针
- 改变方向后 ==》 如何再继续遍历?
- 在改变方向之前直接将指针赋值为一个变量
- 真正往前推进的是head 所在的指针
- 因此赋值一定是head 所在的指针
- 时间复杂度 On
- 空间 O1
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let p1 = head;
let p2 = null;
while(p1){
const temp = p1.next;
p1.next = p2;
p2 = p1;
p1 = temp;
}
return p2
};
3. 2. 两数相加 - 力扣(LeetCode) (leetcode-cn.com)
思路:
- 第一次提交错误 ==》 末位有进位的情况
- 步骤
- 加减运算 ==》 进位 Math.floor (v3 /10) 余位 v3%10
- 遍历 p1 = p1.next ==> 还要考虑为空的情况
- 初始化 创造了空节点 末位要返回 p.next
- 考虑 p不存在的情况 赋值为零
解决:
/**
* 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();
let p1 = l1;
let p2 = l2;
let p3 = l3;
// 进位
let carry = 0;
while(p1 || p2 || carry){
const v1 = p1 ? p1.val : 0;
const v2 = p2 ? p2.val : 0;
let v3 = v1 + v2 + carry;
// 初始化
p3.next = new ListNode(v3 % 10);
carry = Math.floor(v3 / 10);
// 看是否存在 不存在赋 0
if(p1) p1 = p1.next;
if(p2) p2 = p2.next;
p3 = p3.next;
}
return l3.next;
};
4. 83. 删除排序链表中的重复元素 - 力扣(LeetCode) (leetcode-cn.com)
思路:
- 相同就跳过
- 连着相同 就一直跳 直到不同是
p.next
解决
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
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
};
5. 141. 环形链表 - 力扣(LeetCode) (leetcode-cn.com)
思路:
- 环形赛道 跑的慢的一定会被超越
- 相遇时 即 p1 === p2
- 确保只有一个元素情况 并且 快的p 存在 p1.next
解决:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let p1 = head;
let p2 = head;
while(p1 && p2 && p1.next){
p1 = p1.next.next;
p2 = p2.next;
if(p1 === p2){
return true
}
}
return false
};
前端与链表
1. js 中的原型链
1.1 是什么
- 原型链的本质是链表
- 原型链上的节点是各种的原型对象,比如:
Function.prototype 、 Object.prototype
- 原型链通过
__proto__
属性连接各种原型对象
1.2 常见的原型链
obj ==> Object.prototype ==> null
arr ==> Array.prototype ==> Object.prototype ==> null
function ==> Function.prototype ==> Object.prototype ==> null
注:除object 外 其他类型的原型链 先指向自己类型,再指向其他类型
1.3 知识点
-
如果A沿着原型链能找到B.prototype(B的原型对象) , 那么 A instanceof B 为 true
-
const obj = {}; const fun = () => {}; const arr = []; obj instanceof Object; true fun instanceof Object; true fun instanceof Function; true
-
-
如果在A对象上没有找到x属性,那么会沿着原型链找到x属性
-
const obj = {}; Object.prototype.x = 'dd'; ==> obj.x : "dd"
-
1.4 面试题
-
题目一:instanceof的原理 并用代码实现
-
instance(A , B){ let p1 = A; while(p1){ if(p1 === p2.prototype) return true p1 = p1.next; } return false; }
-
-
题目二:
-
value a undefinded value a value b
- 如果在A 对象上没有找到x属性,那么会沿着原型链找到x属性
-
2. 使用链表指针获取json
的节点值
const json = {
a: { b: { c : 1}},
d: { e: 2},
};
let p = json;
path.forEach((key) =>{
p = p[key];
})
const path = ['a' , 'b' , 'c']; // p = 1
const path = ['d' , 'e']; // p = 2
总结
-
链表中的元素是通过next 指针 连接 ,非连续、
-
JavaScript 中无链表 ,obj模拟链表
-
常用操作
- 修改next
- 遍历
-
链表与前端的联系
- js中的原型链
- 使用链表指针获取json的节点值
参考文献
lewis 《JavaScript版数据结构与算法》 系列课程-