上一篇文章,已经初步会构造“链表”这种数据数据结构,本文主要讲一下构造的链表如何进行优化。
链表代码如下:
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
Node *head=NULL;
int number;
do{
scanf("%d",number);
if(number!=-1){
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
head=p;
}
}
}while(number!=-1);
return 0;
}
首先,链表结点已经在“node.h”的头文件中,通过对主函数的分析,我们可以看到,可以将在链表中增加结点这一行为提取出来,作为一个函数,命名为add()。要在一个链表中增加结点,因此add函数传入的参数就应该包含两个部分:链表的头结点和插入链表的数字。当然,如果改成“链表的头结点和新插入的结点”也是可以的,只不过需要将创建新结点单独拿出去在作为一个函数。因此修改之后的函数如下:
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void add(Node *head,int number);
int main(int argc, char *argv[]) {
Node *head=NULL;
int number;
do{
scanf("%d",number);
if(number!=-1){
add(head,number);
}while(number!=-1);
return 0;
}
}
void add(Node *head,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
head=p;
}
}
void add(Node *head,int number);
int main(int argc, char *argv[]) {
Node *head=NULL;
int number;
do{
scanf("%d",number);
if(number!=-1){
add(head,number);
}while(number!=-1);
return 0;
}
}
void add(Node *head,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
head=p;
}
}
红色部分是修改的程序。这里要注意每次传进去的head仍然是一个null,add程序无法修改head。
这里,有些教科书会说到用全局变量head,将head定义在主函数外面。有两个原因导致我们不愿意这样使用:1、要尽可能避免使用全局变量,全局变量是有害的,很可能在程序的某个地方就会修改全局变量;2、全局变量head我们现在设想的是只在这个链表中使用,如果存在多个链表,那多个链表的head就无法使用这个全局变量。
另外一个方法是可以在add函数里面将head作为返回值,然后将add函数定义为一个结点型指针(Node* add(Node *head,int number)),并将函数返回值赋给head。
程序修改如下:
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void add(Node *head,int number);
int main(int argc, char *argv[]) {
Node *head=NULL;
int number;
do{
scanf("%d",number);
if(number!=-1){
head=add(head,number);
}while(number!=-1);
return 0;
}
}
Node* add(Node *head,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
head=p;
}
return head;
}
head=add(head,number);
}while(number!=-1);
return 0;
}
}
Node* add(Node *head,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
head=p;
}
return head;
}
由于add函数没有使用全局变量,因此add函数可以针对不同的链表,是一个应用范围变广的函数,这看起来像是一个进步。但是这里存在一个接口函数设计的问题。这个函数最终要给其他程序员使用,程序员很难注意到
head=add(head,number);
head=add(head,number);
程序员使用add函数只是在链表上增加一个结点而已,实在是很难想到竟然通过add函数的返回值去修改head。因此,站在接口设计的角度,依然是存在问题。如果程序员忘了这个add赋给head的操作,那么对于空链表的add操作就是错的。
第三种方案:在add函数的输入参数中不再传入head结点,而是传入头结点的指针。即add(&head,number)。程序的其他部分也需要进行修改。
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void add(Node *head,int number);
int main(int argc, char *argv[]) {
Node *head=NULL;
int number;
do{
scanf("%d",number);
if(number!=-1){
add(&head,number);
}while(number!=-1);
return 0;
}
}
Node* add(Node **pHead,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=*pHead;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
*pHead=p;
}
}
add(&head,number);
}while(number!=-1);
return 0;
}
}
Node* add(Node **pHead,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=*pHead;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
*pHead=p;
}
}
这里有一个比较高级的方法:先定义一个结构体,这个结构体包含Node的指针head
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
typedef struct _list{
Node* head;
}List;
void add(Node *head,int number);
int main(int argc, char *argv[]) {
int number;
List list;
list.head=NULL;
do{
scanf("%d",number);
if(number!=-1){
add(&list,number);
}while(number!=-1);
return 0;
}
}
void add(List *pList,int number)
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//Find the last
Node *last=pList->head;
if(last){
while(last->next){
last=last->next;
}
//attach
last->next=p;
} else{
pList->head=p;
}
}
这个改进的好处是用了一个自己定义的结构体代表整个链表,而不再是之前仅有节点的概念。从结点层次的操作提升到链表层次的操作,很多事情就会变得简单。比如链表结构体中不仅可以包含结构体头结点,还可以包含尾结点,节点数量等等。