技巧一:指针与引用
下列都表示存储所指对象的存储地址:
- 有指针概念:C语言
- 类似于指针的“引用”概念:Java, Python
将变量赋值给指针,即将变量的地址赋值给指针。
例如:
p->next=q,意思是p结点中的next指针存储了q结点的内存地址。
p->next=p->next->next,意思是p结点的next指针存储了p结点的下下个结点的内存地址。
技巧二:警惕指针丢失和内存泄漏
插入结点时顺序:
- 将结点x的next指针指向结点b
- 将结点a的next指针指向结点x
即插入新结点:
new_node->next = p->next;
p->next = new_node;
删除结点:
p->next = p->next->next;
C语言中,内存管理由程序员负责,如果没有手动释放结点对应的内存空间,则会产生内存泄漏。Java则可以虚拟机自动管理内存。
技巧三:利用哨兵简化实现难度
当向空链表插入第一个结点时,需要做特殊处理:
if (head == null) { //head表示链表的头结点
head = new_node;
}
删除链表中最后一个结点:
if(head->next==null){
head=null
}
为了避免以上的特殊处理,引入哨兵结点:任何时候,无论链表是否为空,head指针都指向哨兵结点。有哨兵结点的链表叫带头链表。
技巧四:边界条件处理
用来检查链表代码的边界条件:
- 链表为空时;
- 链表中只有一个结点时;
- 链表中只有两个结点时;
- 代码逻辑处理头结点和尾结点时。
同时,对于复杂的例子,建议用画图的方式来举例分析。
常见链表操作:
- 单链表反转
- 链表中环的检测
- 两个有序链表的合并
- 删除链表倒数第n个结点
- 求链表的中间结点