JavaScript版数据结构与算法-链表
链表
- 多个元素组成的列表
- 元素存储不连续,用
next
指针连在一起
链表 VS 数组
- **数组:**增删非首尾元素时往往需要移动元素
- **链表:**增删非首尾元素,不需要移动元素,只需要更改
next
的指向即可
js
的链表
js
中是没有链表- 可以用
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 p = a
while(p){
p = p.next
}
// 插入
const e = { val : 'e'}
c.next = e
e.next = d
// 删除
c.next = d
237. 删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
现有一个链表 – head = [4,5,1,9],它可以表示为:
4 ==> 5 >1>9
解题思路
- 无法直接获取被删除节点的上个节点
- 将被删除节点转移到下个节点
解题步骤
- 将被删节点的值改为下个节点的值
- 删除下个节点
/**
* 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
node.next = node.next.next
};
206. 反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
解题思路
- 反转俩个节点,将
n+1
的next
指向n
- 反转多个节点:双指针遍历链表,重复上述操作
解题步骤
- 双指针一前一后遍历链表
- 反转双指针
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let p1 = head;
let p2 = null;
while(p1){
let tmp = p1.next;
p1.next = p2
p2 = p1
p1 = tmp
}
return p2
};
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
解题思路
- 模拟相加操作
- 遍历链表
解题步骤
- 新建一个空链表
- 遍历被相加的俩个链表,模拟相加操作,将 个位数 追加到新链表上,将十位数留到下一位去相加
/**
* 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){
let v1 = p1 ? p1.val : 0;
let v2 = p2 ? p2.val : 0;
let v3 = v1 + v2 + carry;
carry = Math.floor(v3/10);
p3.next = new ListNode(v3 % 10) ;
if(p1) p1 =p1.next;
if(p2) p2 =p2.next;
p3 = p3.next;
}
if(carry) p3.next = new ListNode(carry);
return l3.next
};
83. 删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2 输出: 1->2
解题思路
- 因为链表是有序的,所以重复元素一定相邻
- 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值
解题步骤
- 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值
- 遍历结束后,返回原链表的头部
/**
* 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 p1 = head;
while (p1 && p1.next){
if (p1.val === p1.next.val){
p1.next = p1.next.next
}else{
p1 = p1.next
}
}
return head
};
141. 环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
解题思路
- 跑步,跑的快一定比跑得慢的快一圈
- 用一块一慢俩个指针遍历链表,如果指针能够相逢,那么链表就有圈
解题步骤
- 用一块一慢俩个指针遍历链表,如果指针能够相逢,就返回
true
,slow
从head
开始,fast
从head.next
开始,如果移动过程中fast
可以追到slow
,则则证明有环,如果feat
移动到链表末尾说明没有环 - 遍历结束后,还没有相逢就返回
false
/**
* 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 && p2.next){
p1 = p1.next
p2 = p2.next.next
if(p1 === p2) return true
}
return false
};
原型链简介
-
原型链的本质是链表
-
原型链上的节点是各种原型对象,比如:
Function.prototype
、Object.prototype...
-
原型链通过
__proto__
属性连接各种原型对象 -
obj => Object.prototype => null
-
func => Function.prototype = > Object.Prototype => null
-
arr => Array.prototype => Object.prototype => null
实现原型链
const instanceOf = (A,B) =>{
let p = A
while(p){
if (B = p.prototype) return true
p = p.__proto__
}
return false
}
总结
- 链表里的元素存储是不连续的,之间通过
next
链接 js
中没有链表,但是可以用Object
模拟链表- 链表常用操作:修改
next
、 遍历链表 js
中的原型链也是一个链表- 使用链表指针可以获取
JSON
的节点值