链表是一种数据结构,它不仅非常有用,而且许多操作链表的技巧也适用于其他数据结构。
链表就是一些包含数据的独立数据结构(通常称为节点)的集合。链表中的每一个节点都是通过链或者指针连接在一起,程序通过指针访问链表中的节点,通常节点是动态分配的,但有时你也能看到节点数组构建的链表,即使在这种情况下,程序也是通过指针来遍历链表的。
先创建一个结构:
typedef struct NODE {
strcut NODE *link;
int value;
}Node;
下面来构建一个插入节点函数:
sll_insert(register Node **linkp,int new_value){
register Node *current;
register Node *new;
while((current=*linkp)!=NULL && current->value<new_value)
linkp=¤t->link;
new=(Node *)malloc(sizeof(Node));
if(new==NULL)
return 0;
new->value=new_value;
new->link=current;
*linkp=new;
return 1;
}
下面我们来几个关于单链表的编程练习:
1.编写一个函数,用于计数一个单链表的节点的个数。它的唯一参数就是一个指向链表第一个节点的指针。编写这个函数时,你必须知道哪些信息?这个函数还能用于执行其他任务吗?
解析:这个函数很简单,虽然它只能用于它声明的那种类型的节点---你必须知道节点内部的结构。
int sll_count_nodes( Node *first){
int count=0;
for(;first!=NULL;first=first->link)
count++;
return count;
}
如果这个函数被调用时传递给它的是一个指向链表中间是位置某个节点的的指针,那么它将对链表中这个节点以后的节点进行计数。
2.编写一个函数,在一个无序的单链表中寻找一个特定的值,并返回一个指向该节点的指针。如果想让这个函数适用于有序的单链表,需不需要进行修改?
Node * sll_find_nodes(Node *first ,int value){
while(first!=NULL){
if(first->value==value)
return first;
first=first->link;
}
return NULL;
}
如果想让它适用于有序单链表,不需要修改!
3.编写一个函数,反序排列一个单链表的所有节点。函数的原型是Node *sll_reverse(Node *first),函数的参数指向链表的第一个节点,当链表被重新排列后,函数返回一个指向链表新头节点的指针。链表的最后一个节点link字段的值应该是NULL,在空链表上执行这个函数将返回NULL。
方案1:
Node *sll_reverse(Node *first ){
if(first==NULL)
return NULL;
Node *preview;
Node *next;
preview=first;
first=first->link;
if(first==NULL)
return preview;
next=first->link;
preview->link=NULL;
first->link=preview;
while(next!=NULL){
preview=first;
first=next;
next=next->link;
first->link=preview;
}
return first;
}
方案2:
Node *sll_reverse(Node *first){
if(first==NULL)
return NULL;
Node *node_array[100];
int n;
for(n=0;first!=NULL;first=first->link){
node_array[n]=first;
if(n==0)
node_array[n]->link=NULL;
else
node_array[n]->link=node_array[n-1];
n++;
}
return node_array[n-1];
}
假如传递的是头指针*head。那么函数该怎么写呢?
Node *sll_resverse(Node *head){
if(head->link==NULL)
return NULL;
Node *current,*temp;
current=head->link;
current->link=NULL;
while(current!=NULL){
temp=current;
current=current->link;
temp->link=head->link;
head->link=temp;
}
return current;
}
4.编写一个函数,从单链表中移除一个节点,函数原型为int sll_remove(Node **rootp,Node *node)。函数中的第一个参数是一个指向链表头指针的指针,第二个参数是一个想要移除的节点的指针。
解析:可以分为三种情况:
1.要删除的节点为第一个;
2.要删除的节点在中间;
3.要删除的节点位于最后一个;
这是我想的一个思路。
但你要仔细想想,事实上都是一样的。
我的方案:
Node *sll_remove(Node **rootp,Node *node){
Node *current;
while((current=(*rootp)->link)!=NULL){
if(current==node){
(*rootp)->link=current->link;
return 1;
}
(*rootp)->link=current;
}
return 0;
}
参考书方案:
int sll_remove(Node *linkp,Node *delete){
register Node *current;
assert(delete!=NULL);
while((currnet=*linkp)!=NULL && current != delete)
linkp=¤t->link;
if(current==delete){
*linkp=current->link;
free(current);
return 1;
}
else
return 0;
}