数据结构和算法:链表(下)

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

技巧一:掌握指针或引用的含义

事实上,看懂链表的结构并非很难,可是一旦把它和指针混在一块儿,就和容易让人摸不着头脑。因此,要写好代码,首先就要理解好指针node

有些语言有“指针”的概念  ,好比C语言;有些语言没有指针,取而代之的是“引用”。实际上,它们的意思都是同样的,都是存储所指对象的内存地址算法

对于指针的理解:将某个变量赋值给指针,实际上就是将这变量的地址赋值给指针,或者反过来讲,指针中存储了这个变量的内存地址指向了这个变量,经过指针就能够找到这个变量。浏览器

在编写链表代码的时候,咱们常常会这样写:p->next=q,这行代码是说,p结点的next指针存储的是q结点的内存地址数据结构

还有一个比较复杂的:p->next=p->next->next,这行代码表示,p结点的next指针存储的是p结点的下下个结点的内存地址post


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

写链表代码的时候,指针指来指去,一会就不知道指哪里了。因此,咱们在写的时候,必定注意不要丢了指针指针

指针是怎么弄丢的呢?举例说明一下:假如一个链表有头结点a和尾结点b两个结点,咱们但愿在a和b之间插入结点x,假设当前指针p指向结点a,即p=a若是向下述同样实现代码,就会发生指针丢失和内存泄漏。调试

p->next=x;//将p的next指针指向x结点;code

x-next=p->next;//将x的next结点指向b结点对象

可是这里有个问题:p->next指针在完成第一步操做以后,已经再也不指向结点p了,而是指向结点x。第二行代码至关于将x赋给x->next本身指向本身。所以,整个链表就断成了两半从b之后的结点都没法访问了。内存


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

首先,回顾一下单链表的插入和删除操做。若是我么在p结点的后边插入一个新的结点,下面两行代码就能实现:

new_node->next=p-next;

p->next=new_node;

可是,对于一个空链表插入一个新节点,上述代码逻辑就不能用了。咱们须要进行以下的特殊处理:

if(head=null){head=new_node}

单链表删除结点,要删除p结点的后继结点,只须要一行代码就能实现:

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

可是,若是咱们要删除链表的尾结点,上述代码逻辑就不work了。跟插入相似,咱们也须要作以下的特殊处理

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

从上述的分析得出,针对链表的插入、删除操做,须要对插入第一个结点删除最后一个结点的状况作特殊处理。这样代码就会看起来很繁琐,不简洁,并且也容易由于考虑不全而出错。如何来解决这个问题呢?

哨兵就能够派上用场了。

若咱们引入哨兵结点,在任什么时候候,无论链表是否是空,head指针都会一直指向这哨兵结点。咱们把这种有哨兵结点的链表称做带头链表相反,没有哨兵的链表叫作不带头链表

哨兵结点是不存储数据的。由于哨兵结点一直存在,因此插入第一个结点和插入其余结点,删除最后一个结点和删除其余结点,均可以统一为相同的代码了。 

技巧四:重点留意边界条件处理

软件开发中,代码在如下边界或者异常状况下,最容易产生Bug。

咱们常常用来检查链表代码是否正确的边界条件有这几个:

1)若是链表为空时,代码是否正常工做?

2)若是链表只包含一个结点时,代码是否正常工做?

3)若是链表只包含两个结点时,代码是否正常工做?

4)代码处理头结点和尾结点时,代码是否能正常工做?

技巧五:举例画图,辅助思考

技巧六:多写多练,没有捷径

若是已理解不可以掌握以前的技巧,可是手写代码仍是会出现各类各样的错误,也不要着急,就是把常见的链表操做都本身多写几遍,出问题就一点一点调试,孰能生巧

下述选了链表的五个经常使用操做。将这几个操做都能写熟练,不熟就多写几遍,链表代码就不用惧怕了。

1)单链表反转

2)链表中环的检测

3)两个有序的链表合并

4)删除链表倒数第n个结点

5)求链表的中间结点


下一篇:数据结构与算法:栈:如何实现浏览器的前进和后退功能?

相关文章
相关标签/搜索