#if 1
printf("sizeof(SingleListNode) = %d n" , sizeof(SingleListNode) ) ;
printf("sizeof(*pHead) = %d n" , sizeof(*pHead) ) ;
printf("sizeof(pHead) = %d n" , sizeof(pHead) ) ;
#endif
输出结果为:8,8,4。
创建链表即要将data和next置空。
pHead->next = NULL ; //将头节点指针域置空
pHead->data = NULL ; //将头节点数据域置空
在将节点插入到链表之前,首先得为这个待插入的节点申请存储空间,并且建立该节点的指针ptr。
SingleListNode *ptr = (SingleListNode *)malloc( sizeof(SingleListNode) ) ;
这里ptr为待插入节点的指针,ptr的值即为待插入链表的节点的地址。*ptr即为节点中存储的数据:data和next,共占8个字节。
待插入节点插入链表第一步,将数据x保存到ptr->data。
ptr->data = x ;
待插入节点插入链表第二步,将pos->next赋给ptr->next。
ptr->next = pos->next ;
待插入节点插入链表第三步,将pos->next指向待插入节点。
pos->next = ptr ;
这样我们就完成了将数据x的节点插入到链表中。
#if 1
printf(" x = %c n" , x ) ;
printf("&x = %#x n" , &x ) ;
printf(" ptr = %#x n" , ptr ) ;
printf("&ptr = %#x n" , &ptr ) ;
printf("(pos->next) = %#x n" , (pos->next) ) ;
printf("(ptr->next) = %#x nn" , (ptr->next) ) ;
#endif
在插入节点函数中加入调试代码,运行程序,输入abc,调试结果。
请输入链表节点数据: abc
x = a
&x = 0x12fe3c
ptr = 0x392920
&ptr = 0x12fe24
(pos->next) = 0x392920
(ptr->next) = 0
x = b
&x = 0x12fe3c
ptr = 0x392958
&ptr = 0x12fe24
(pos->next) = 0x392958
(ptr->next) = 0
x = c
&x = 0x12fe3c
ptr = 0x392990
&ptr = 0x12fe24
(pos->next) = 0x392990
(ptr->next) = 0
ListData: abc
ListAddress: 0x394b48 0x392920 0x392958 0x392990
通过上面的调试,我们将数据和地址放进节点,便于我们明白链表的储存。
可以看到pos->next是和ptr相等的,因为两者都是指向待插入节点,而ptr->next始终是为0,因为ptr->next是指向NULL。
从中我们还可以看出,链表的存储空间是间断的,而不是像数组元素存储空间是连续的。
/*************************************************************************
** 函数名称 :sll_FindPrevious
** 函数功能 :单向链表按值搜索位置的前驱算法函数
** 入口参数 :pList ,
** 出口参数 : pCur
*************************************************************************/
Position sll_FindPrevious(SingleList pList , ElementType x)
上面这个函数得明白什么叫做函数指针,什么叫做指针函数。
void *Func () :指针类型的函数
void (*Func)() :一个指向void型函数的指针
定义一个结构体指针pCur,使其总是指向当前搜索节点的前一个节点。初始状态下pCur是指向头结点。
Position pCur ;
pCur = pList ; //pCur的初值指向头节点
从第一个节点开始比较pCur指向下一个节点的数据域和数据x的值,如果两者相等,则返回pCur;否则pCur重新指向下一个节点。
while ( (pCur->next) && (pCur->next->data) != x) {
pCur = pCur->next ; //pCur重新指向下一个节点
}
return pCur ; //返回已找到元素x所在节点的地址
删除节点和插入节点是相对应的,在删除节点之前,我们先要找到需要删除节点的位置,此时,我们就可以使用按值搜索节点位置的前去算法找到需要删除的节点位置。
/*************************************************************************
** 函数名称 :sll_delete
** 函数功能 :删除节点函数
** 入口参数 :pList ,x
** 出口参数 :void
*************************************************************************/
void sll_delete(SingleList pList , ElementType x)
Position pCur , temp ; //pCur指向要删除节点的上一个节点,temp指向删除的节点
pCur = sll_FindPrevious(pList , x) ; //返回需要删除的节点的上一个节点地址
如果sll_FindPrevious能返回删除节点的位置(即pCur不为NULL),那么执行删除该节点的操作。
if ( pCur && (pCur->next) )
删除操作第一步,让temp指向待删除的节点。
temp = pCur->next ;
删除操作第二步,让pCur->next指向待删除节点的下一个节点,这样就把待删除节点*temp删除。
pCur->next = temp->next ;
最后将已经删除了的节点temp的存储空间释放掉。
free(temp) ;
在删除节点函数中添加调试代码,在删除节点前和删除节点后都加入,进行前后对比。
#if 1
printf("pCur = %#x n" , pCur) ;
printf("pCur->next = %#x n" , pCur->next) ;
sll_printAddress(pList) ;
printf("nn") ;
#endif
if ( pCur && (pCur->next) ) {
temp = pCur->next ;
pCur->next = temp->next ;
free(temp) ;
}
#if 1
printf("pCur = %#x n" , pCur) ;
printf("pCur->next = %#x n" , pCur->next) ;
sll_printAddress(pList) ;
printf("nn") ;
#endif
调试代码的输出。
请输入链表节点数据: abc
ListData: abc
ListAddress: 0x394b48 0x392920 0x392958 0x392990
输入要删除的字符: b
pCur = 0x392920
pCur->next = 0x392958
0x394b48 0x392920 0x392958 0x392990
pCur = 0x392920
pCur->next = 0x392990
0x394b48 0x392920 0x392990
ListData: ac
请按任意键继续. . .
通过前后比较可以知道,我们删除了0x392958这个地址的节点,即为储存b的节点。
如果这个链表不再需要使用,则要把这个链表销毁掉。销毁办法是一个一个节点释放掉。
销毁链表第一步,让待销毁的节点指针(ptr)指向头节点(初始时,pList是指向头结点)。
SingleListNode *ptr = pList ;
销毁链表第二步,让现在pList所指的节点的下一个节点成为头节点,因为现节点将要被删除,直到所指节点为空为止。
pList = pList->next ;
销毁链表第三步,释放掉待销毁节点。
free(ptr) ;
销毁链表第四步,让下一个节点(即为pList现在所指节点)成为待删除节点。
ptr = pList ;
这样往复循环,直到ptr指向NULL为止。这样就成功把一个链表销毁了。
在销毁链表函数中加入调试代码,运行。
#if 1
printf("ptr = %#x n" , ptr) ;
sll_printAddress(pList) ;
printf("nn") ;
#endif
运行结果,
ptr = 0x394b48
0x392920 0x392990 0x3929c8
ptr = 0x392920
0x392990 0x3929c8
ptr = 0x392990
0x3929c8
ptr = 0x3929c8
请按任意键继续. . .
我们可以看到当前删除的节点地址ptr,和删除节点之后各节点的地址。