理解指针的含义
指针中存储了数据的内存地址,在javascript中,我们可以把指针简单的理解为引用,类似对象的引用。
警惕指针丢失和内存泄漏
在写链表代码时,指来指去很容易就不知道指到哪了,所以在写的时候一定注意不要丢失指针。
具体的例子,在链表中插入结点x
如果这样实现代码,就会发生指针丢失和内存泄漏
p.next = x; // p指向x
x.next = p.next; x再指向p
乍一看好像没问题,第一行代码p指向了x,第二行p.x就相当于x,实际上是x指向了自己,这时候链表的被分为了两部分,后半部分就丢失了。 对于某些语言,内存管理是由程序员来负责的,这时候就会出现内存泄露。那么怎么来改一下呢
x.next = p.next;
p.next = x;
这样调换一下顺序问题就解决了,所以我们在操作结点的时候,一定要注意操作的顺序。
合理利用头结点
只需要上面两行代码就可以实现链表的插入操作,但是这只能满足大多数的操作场景,如果是往空链表中插入一个元素这两行代码就行不通了,需要进行一下判断单独处理
if(!head){
head = x;
}
删除结点也会遇到同样的问题
上面的情况很容易因为考虑不全而出错,那么怎么解决呢?我们只需要增加一个头结点,这个结点一般不存储数据,只是存储链表第一个结点的地址。这样不管链表是不是为空,这个结点都是存在的,我们把这种带头结点的链表叫做带头链表,相反,不带头结点的链表叫做不带头链表。
因为头结点始终都存在,所以插入和删除操作就可以统一为相同的逻辑了
留意边界条件的处理
常见的边界条件如下:
- 链表为空代码是否正常工作
- 当只有1个或者两个结点是否正常工作
- 处理头结点、第一个结点和尾结点是否正常工作
通过画图来辅助思考
对于稍微复杂的链表操作,比如前面我们提到的单链表反转,指针一会儿指这,一会儿指那,一会儿就被绕晕了。总感觉脑容量不够,想不清楚。所以这个时候就要使用大招了,举例法和画图法。
没有捷径,多练
推荐题目如下
- 单链表反转
- 链表中环的检测
- 合并两个有序列表
- 删除链表倒数第n个结点
- 求链表的中间结点
具体题目解法见下面笔记: