算法要求:在单链表按升序插入一个值。成功插入返回1,已存在返回0,插入失败返回-1。
结点结构:
typedef struct Node
{
int value;
struct Node* next;
}Node;
C语言新手写单链表的有序插入算法可能会写出下面的算法: int list_insert(Node *list, int value)
{
Node *p, *q, *new;
p = list;
// 查找正确插入位置
while (p!=NULL && p->value < value) {
q = p;
p = p->next;
}
// 已存在,返回0
if (p!=NULL && p->value==value)
return 0;
new = (Node*)malloc(sizeof(Node));
if (!new)
return -1; // 失败,返回-1
new->value = value;
new->next = p;
q->next = new;
return 1;
}
仔细看,这是一个有bug的代码。如果要插入的值比链表第一个结点的值小,那么应该插入在第一个结点之前。这样就应该修改头结点的指针。但是,这个代码却实现不了这样的功能。
经过以下修改可以修正这个bug。
int list_insert(Node **list, int value)
{
Node *p, *q, *new;
p = *list;
q = NULL;
// 查找正确插入位置
while (p!=NULL && p->value < value){
q = p;
p = p->next;
}
// 已存在,返回0
if (p!=NULL && p->value==value)
return 0;
new = ( Node*)malloc(sizeof(Node));
if (!new)
return -1; // 失败,返回-1
new->value = value;
new->next = p;
if (q == NULL) // 插入在第一个位置,改变头结点指针
*list = new;
else // 插入位置不是第一个位置
q->next = new;
return 1;
}
这个代码没有bug了。思路清晰。但是却有冗余。有最后要判断是不是应该插入到链表的第一个位置。如果忘了判断,还是会写出第一个代码那样的bug出来。下面有一个改进的代码。
int list_insert(Node **pnext, int value)
{
Node *p, *new;
// 查找正确插入位置
while ((p=*pnext)!=NULL && p->value < value){
pnext = &p->next;
}
// 已存在,返回0
if (p!=NULL && p->value==value)
reutrn 0;
new = (Node*)malloc(sizeof(Node));
if (!new)
return -1; // 失败,返回-1
new->value = value;
new->next = current;
*pnext->new;
return 1;
}
这个算法不用考虑插入的是不是第一位置,因为它已经跟其他的操作一样的。如果插入的是在第一位置,那么*pnext已经修改了头结点的指针。如果不是第一个位置,也可以正确插入。
说明下,这儿传入的参数是头节点的地址,而不是头节点,这点主要是针对当插入的数时最小的情况,当插入的值比已有链表中所有的值都小时,就得修改头节点,这时就只有传入指针的指针,也就是头结点的地址。
另外一个就是这里面没移动一次节点,都取了当前节点地址来赋值给linkp,这也是针对头结点这个特殊节点来的,以为我们插入一个节点,就是要找到插入点的上一个节点的link,而这个link可能是普通节点的link,也有可能是头结点,
linkp = &t->link;
就是来抽象这两种不同的情况,把这情况的节点的属性都看成一种节点。