如何轻松正确的写出链表代码?

文章来源于极客时间前google工程师−王争专栏。

技巧一:理解指针或引用的含义

指针:存储所指对象的内存地址

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

p->next = q
p结点的next指针存储了q结点的内存地址。

p->next = p->next->next
p结点的next指针存储了p结点的下下一个结点的内存地址。

技巧二:警惕指针丢失和内存泄漏

如何轻松正确的写出链表代码?_第1张图片

p->next = x 
x->next = p->next
  • p->next指针完成第一步,不再指向结点b了,而是指向了结点x。
  • 第二行相当于将x赋值给x->next,自己指向自己,链表断开。
  • 左边next代表指针,右边next代表结点。

正确操作是将上面两行代码执行顺序调换。

技巧三:利用哨兵简化实现难度

单链表p结点后面插入一个新结点

new_node->next = p->next;
p->next=new_node;

如果是插入第一个结点,刚刚的逻辑不能用

if(head == null){
    head = new_node;
}

单链表结点删除操作,如果删除结点p的后继结点

p->next = p->next->next

如果要删除链表中最后一个结点,上面的删除逻辑就不能用了

if(head->next == null){
    head = null;
}

针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理

哨兵专门解决“边界问题”,不直接参与业务逻辑。

如何表示一个空链表?head=null表示链表中没有结点。

引入哨兵结点,任何时候,不管链表是不是空,head指针都会一直指向这个哨兵结点

  • 带有哨兵结点的链表叫作带头链表
  • 没有哨兵结点的链表叫作不带头链表

实现:返回数组中特定值的下标

  • 普通方法
//n为数组a的长度
public int find(char[] a,int n,char key){
    if(a == null || n<=0){
        return -1;
    }
    int i = 0;
    while(i
  • 哨兵机制
public int find(char[] a,int n,char key){
    if(a == null || n<=0){
        return -1;
    }
    if(a[n-1] == key){
        return n-1;
    }
    //记录数组临界位置
    char temp = a[n-1];
    //哨兵处理
    a[n-1] = key;
    int i=0;
    //由于哨兵 优化掉一条i

重点留意边界条件处理

基础考虑

  • 如果链表为空,代码是否能正常工作?
  • 如果链表只包含一个结点时,代码是否能正常工作?
  • 如果链表只包含两个结点时,代码是否能正常工作?
  • 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?

举例画图,辅助思考

多写多练

  • 单链表反转
  • 链表中环的检测
  • 两个有序链表合并
  • 删除链表倒数第n个结点
  • 求链表的中间结点
  • 206,141,21,19,876(leecode对应编号)

你可能感兴趣的:(数据结构与算法,链表,数据结构)