链表

JavaScript是一个比较魔幻的语言,它本身是不自带linkList的数据结构的,JS中的链表是通过对象来模拟的。下面来一段示例代码

let a = {val:1}
let b = {val:3}
let c = {val:5}
let d = {val:7}
let e = {val:9}

a.next = b
b.next = c
c.next = d
d.next = e
console.log(a) // { val: 1, next: { val: 3, next: { val: 5, next: {val: 7, next: {val: 9} } } } }

这样,我们通过打印a这个对象,可以看到a中有一个next属性,并且指向的是b这个对象,然后,b对象中也有val和next属性,其中指向的c对象

这样,对象a就是这个链表的head节点,

遍历一个链表怎么实现呢?

let p = head
while(p){
console.log(p.val)
p = p.next
}

这样 这个链表中的元素就会被遍历一遍,这里的p 是一个指针,它所指向的是head节点的地址,所以我们在while循环中,改变p的指向地址,就不会改变head

链表的插入

let f = {val:10}
d.next = f
f.next = e

这样 链表的结构就是 a->b->c->d->f->e

链表中的删除

d.next = e

将前面d和e中间连接的f直接断开连接,直接将d指向e,那么f就自己断开了

leetcode中是如何定义链表的呢,我们随便打开一题链表题,都可以看到,在代码上面的注释中,有一个函数,里面有两个参数,val和next,那么这就是链表中的基本结构。一个是当前节点的值,另外一个是指向了下一个节点。

接下来实际操作一题我自认为最简单的一链表题。83. 删除排序链表中的重复元素

这一题还是偏简单,思路:用指针遍历链表,如果当前节点的val属性和当前节点的next节点的val属性相等,就将当前节点与next的next节点连接,即删除next节点。


var deleteDuplicates = function(head) {
    let p =head; // 指针p
    while(p&&p.next ) { 这里也要判断p不是最后一个节点,不然它的next是一个null,那么程序就会报错
        if(p.val == p.next.val) { // 判断当前节点的val属性和下一个节点的val是否相等
            p.next = p.next.next    // 删除next节点
        }

        else p = p.next  //遍历这个链表
    }
    return head
};

那么,链表的基础就说到这儿了,(更新一题,反转链表)

leetcode 206

地址

就是反转一个链表,

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

先遍历链表 这是肯定的,然后是一步:当前节点的next节点,等于当前节点,然后当前节点等于当前节点的next节点,就拿一个最简单的 1->2来说 相当是1和2互换位置,但是这个操作不简单。前面说了,JS的链表是一个指向对象的地址,如果你直接将当前节点等于next节点的话,那么next节点也指向了一个新的地址,而不是原来的next节点,所以我们按照C语言中写一个两数交换的函数一样,需要传递的是值的指针,才可以交换,我们定义一个临时变量temp,让它等于next节点,然后让当前节点等于这个temp对象,并且让next指针指向当前节点,代码如下:

const reverseList = head => {
    let p1 = head
    let p2 = null
    while(p1) {
        const temp = p1.next
        p1.next = p2
               p2 = p1
        p1 = temp
    }
    return p2
}
function ListNodeToArray(head) {
        let p = head
        let arr = []
        while(p&&p.next) {
            arr.push(p.val)
            p = p.next
        }
        arr.push(p.val)
        return arr
}

let arr = ListNodeToArray(head)

function arrToListNode(arr) {
    let h = new ListNode(0)
    let p = h
    for(let i=0;i<arr.length;i++) {
        p.next = new ListNode(arr[i])
        p = p.next
    }
    return h.next
}

接下来看原型链。

这个东西带有一个链字,那么它就很可能和链表有关系

看一下下面这一张图。

16d04ccc5d03fbc7.jpg
原型是指一个构造函数(constructor)的属性

首先要了解两个东西

prototype和__proto__

image.png

原型链中的“链”字,指的是,一个对象从自己开始找,找父级,在找父级,这个过程叫做链。和作用域链有那么一丝丝相像,都是一级一级往上找,直到null (原型的终点) window/global (作用域的终点)

举一个简单的例子

函数的父亲是对象,那么 Function.prototype === Object.__proto__ // true

function foo() {}
let f = new foo()
f.__proto__ === foo.prototype // true

(2020-9-6)明天继续

9-13 更新...

这里可以拓展到 typeof和instanceof相关知识,我们知道,数组,null和对象都会被typeof看作是对象,那么对于数组的判断不能够用typeof来,这时,就有了其他的很多方法,例如instanceof,

let arr = [1,2,3,4]
arr instanceof Array // true

为什么instanceof可以获取到arr的类型呢,其实就是利用了原型链

我们可以看到 arr.__proto__ == Array.prototype // true

其实instanceof的原理就是 左边实例的__proto__ 一直向上查找,直到找到了null,如果到了null还不相等,那么就不属于这个类,否则就是它的实例

直接贴上代码,其中也有类似链表中的遍历过程

let myinstanceof = (left, right) => {
    left = left.__proto__
    right = right.prototype
    while(true) {
        if (left === null) return false
        if (right === left) return true
        left = left.__proto__ // 这一步操作类似链表中的遍历
        // 右指针(right)不变 左指针遍历 找到null就是false 在过程中找到右指针,那么就可以
    }
}

其实 差不多了吧。。

最后修改:2020 年 10 月 22 日 03 : 13 PM
如果觉得我的文章对你有用,请随意赞赏