链表的寻址定位

 链表的结构并不复杂,只是一个自引用结构
  1. typedef Struct LNode{
  2. ElemType e ;                     //数据元素
  3. struct LNode * next ;             /后继指针
  4. }LNode,*LinkList

并且链表操作,如删除,插入等更改表结构也很容易理解。但有一个很容易出错问题必须引起我们的重视: 如何准确定到应该定位的链表元素,以便我们进行操作(或者取值,或者插入,或者删除),我们应该对此进行一个总结。

 

一:取值操作
请见下图: 

链表的寻址定位_第1张图片                     
 如果我们需要取出第3个元素的值

  1. P = L->next ; j = 1 ; //定位指针指向第一个元素,并初始化计数器
  2. While(j <3)  //j为计数器 
  3. {  p = p->next ;
  4.    J++; 
  5. Return *p ; 


 分析后发现:以上代码不具备健壮性,假设用户需要取得的元素是6呢? 

链表的寻址定位_第2张图片

 我们必须对指针的前进加以限制,防止指针超出范围
 修改后代码:

  1. While(p && j 
  2. {  p = p->next ; 
  3.    J++; 

   该循环跳出时,指针已经越界了,成为了空指针。
   加上判断条件 

  1. If( !p && j>i) return ERROR ; //出循环后计算器J的值只可能等于i,但如果i=-1的话呢? 
  2. //组装上,这将是具有健壮性的代码: 
  3. While(p && j 
  4. {  p = p->next ; 
  5.    J++; 
  6. If( !p &&  j>i) return ERROR ; 


二:插入操作
值得提出的是,由于单链表只知道直接后继,所以我们插入删除时,必须定位到插入或删除位置之前,也就是i-1元素
显然插入位置的取值范围应该是 1~length+1,那么,可以推出我们寻址范围应该为0~length ; 

链表的寻址定位_第3张图片

  1. P = L ; int j = 0 ; //寻址从0开始 
  2. While(p && j < i-1) //定位到i-1个元素 
  3. {p = p->next ; 
  4. J ++} 


三:删除操作
删除元素范围为1~length,因此寻址范围为 0~ length-1

链表的寻址定位_第4张图片

由上图得知,当指针定位到F的时候,实则已经越界了,错误!! 

  1. P = L ; int j = 0 ; //寻址从0开始 
  2. While(p->next  && j < i-1) //定位到i-1个元素 
  3. {p = p->next ; 
  4. J ++} 


总结:
1 链表复习时不仅要熟悉插入删除时修改表结构,更要熟悉链表操作时的寻址 。举一反三,如:需求为不能删除最后一个元素,则定位应该改为:
P = L ; int j = 0 ; //寻址从0开始
While(p->next->next  && j < i-1) //定位到i-1个元素
{p = p->next ;
J ++}
 
2 笔者在循环跳出后,一直利用循环后指针所指点来判断是否已经越界,如:if( p->next && j>i-1) {返回错误},也有另一种方法如:
在取值操作寻址时候就控制指针,让其无法越界。
While(p->next && j {  p = p->next ;
   J++;
}
  If(I == j ) {定位正确,执行操作}  //由于控制了指针范围,则只有定位正确时,i才会等于j
这样,思路更清晰,并少做一次循环
 
3 静态链表笔者也有些感悟,并深感其代码之美,这里就暂不写了,总结点如下:
1 链表的表头插入与表尾插入的区别 。
2 静态链表需要自己管理存储空间,自己写malloc与free
3 时刻须记住,公用空间上可能跑着多条链,至少是一条备用链表(空闲空间)
 
 

你可能感兴趣的:(数据结构)