链表

  链表是一个结构体。

  所以实现一个链表需要链表结构体以及节点结构体

  节点结构体

 

typedef struct node{//节点的结构体 
    int a;
    struct node* next;
    //struct node* previous;
}node,*pnode;

  在此例中,a为节点所附带的属性,不一定只有一个int型的变量,还可以有char name[15]等其他数据。

  为方便起见,用typedef关键字为节点结构体重新命名

 

  链表结构体大体类似

1 typedef struct {//链表的结构体 
2     pnode head;
3     pnode last;
4     int lenth;
5 }list,*plist;

  在这个节点中,可以加入链表的额外的属性,如链表的长度等

  有些书中,不定义链表结构体,仅仅只用一个节点类型的指针全局变量head来代替,这也是可行的。但如果我们要创建多个链表,这样子做就不太好了。而且,一个链表可以有多个属性,如果写成结构体类型,对于属性的表达显然更有益处。

  

 对于链表,他的特点便是其长度不定。这是使用链表而不使用数组的主要原因之一。而实现其长度任意增长的函数便是malloc函数,而malloc函数的原型是:

      void *malloc(long NumBytes);

  首先,malloc函数的功能是向系统申请一块内存。其次我们可以看到,malloc函数的参数只有一个,long NumByte。这个long型参数代表的是向系统申请的内存的大小。因为不同的系统为各类型变量分配内存的大小不同,所以我们一般用sizeof来计算其大小。  malloc返回的是一个空指针,所以我们要对其进行强制转换。还有就是对于使用malloc函数分配的内存系统不会自动释放,所以在程序结尾我们要对它进行内存释放,用free函数。free函数的原型为:void free(void *FirstByte);  其参数为所欲释放内存的地址,也就是指向这个内存的指针。

  对于链表,实质上是对指针的应用。节点与节点间的联系通过   节点里的定义的    节点结构体的指针变量    来表示,在这一个例子里,就是next变量。

  对于单向链表,节点里只有一个指针变量,这个指针变量存储着下一个节点的地址,通过对这个指针变量的解引用,可以获得下一个节点的内容。  我们可以把指针变量想象成一条铁链,连接着两个节点,那么多个节点互相连接,就形成了一个链条,这就是链表了。而对于这条铁链,我们只能通过首尾来访问、使用这个链条,所以我们把他包装起来,那么,这个包装链条的包装袋要把这条铁链的头和尾露出来,这就是head和last指针。  这个包装袋上还要写明这个铁链的长度,就是length变量。

  对于双向链表,节点里有两个指针变量,在本例中就是被注释掉的previous变量,其原理就是在previous变量里存储上一个节点的指针,那么,我们不仅能从head遍历链表到last,还能通过last遍历到head,简而言之,这个链表是双向的。

 

  链表的基本操作为增删改查。

    首先为增

void add(plist p){
    int i=0,j=0,k=0;
    pnode news=(pnode)malloc(sizeof(node));//用malloc开辟的内存  最后要记得释放 
    scanf("%d",&news->a);
    news->next=NULL;//对news进行赋值 
    if(0== p->lenth){//此时,链表为空 
        p->head=news;//链表的头指针指向新节点news的地址 
        p->last;    //此时  链表为空  所以新节点news也是最后一个节点
        p->lenth=p->lenth+1; //  news已经加入链表,链表长度加一 
    }
    else{// 如果链表长度不为空 
        p->last->next=news;//链表的尾节点指向新节点news 
        p->head=news;    //链表的尾指针向后移,指向新加入的节点news,此时news为最后一个节点
        p->lenth++; //链表长度加一 
    }
   //p->lenth++; 
}

 在此例里,我们先通过malloc函数向系统申请一块内存来存放新的节点,在对节点进行初始化。在本例中,对节点的初始化的语句只有:scanf("%d",&news->a);   但在实际应用中,因为节点的数据域会有多个数据,所以其初始化语句会更多,而多个初始化语句中就要注意缓冲区的刷新了。   在这边我举个例子:  scanf("%d",&a);  gets(c);   在这里,输入完成后,c的值为回车符。这是因为在输入完scanf后,我们敲击回车,产生回车符,这个回车符留在了缓冲区,被gets函数读取了。

  要解决这个问题,一个方法就是这样写:scanf("%d",&a);  getchar(); gets(c);   吧回车符用getchar吃掉。 

  还有一个方法就是用flush()函数刷新缓冲区。这样写的逼格会比较高,但是高不到哪里去。  

  还有一种方法就是这样写:scanf("%d%*c",&a);  gets(c);   这里%*c在scanf里的作用就是读取一个字符并丢弃它。scanf函数中  比如 scanf("  %c",&c);   这在%号前加了空格,其作用为跳过所有空白字符,读取第一个非空白字符,赋值给变量c

  推荐熟记个个输入函数的效果,比如scanf函数读取%s时遇到空格就停止读入,gets函数在读取一串字符串后读取并丢弃回车,puts函数会自动添加一个回车。这样才能写出更灵活的代码。

  在初始化完成后,我们就要判断这个链表的长度是否为空,若链表为空表,则在新节点为第一个节点,也是最后一个节点。我们head和last都要指向它。如果链表并不是空表,我们就把这个节点加入的链表的后面。所谓加入链表,就是令原链表的最后一个节点的next指针指向这个新加入的节点,在令链表的last指针指向新节点,则新节点就成为新的最后一个节点。有些帖子上说的什么头插法,尾插法,大概的原理是一样的。

  在完成上述操作后,将链表的长度加一。

  然后就是删。

 1 void deletes(plist p,int n)//删除链表索引n处的节点
 2 {
 3     int i=0,j=0,k=0;
 4     pnode iterator;
 5     iterator=p->head;
 6     for(i=1;i<n;i++){//遍历链表至所要删除节点的前一个节点处 
 7         iterator=iterator->next;
 8     }
 9     iterator->next=iterator->next->next;
10     free(iterator->next);
11     p->lenth--; 
12 } 

  删的原理就是将要删除节点的上一个节点的next指针指向要删除的节点的下一个节点。这样就把要删除的节点从链表中删除了。  因为在增加函数中我们用malloc函数申请的内存,所以在删除中我们要用free释放要删除节点的内存,最后不要忘记将链表的长度减一。

  在这个代码中我们用了一个iterator变量来存储中间节点,以此来迭代遍历整个链表。

 

在然后就是查

void search(plist p,int n){//查询索引n处的节点 
    int i=0,j=0,k=0;
    pnode iterator;
    iterator=p->head;
    if(0==p->lenth){
        printf("错误,链表的长度为零");
        return ;
    }
    for(i=1;i<n;i++){
        iterator=iterator->next;
    }
    printf("索引n处的节点的值为:%d",iterator->a); 
}

void search2(plist p ,int n){//查询值为n的节点的索引值 
    int i=0,j=0,k=0;
    pnode iterator;
    iterator=p->head;
    for(i=0;i<p->lenth;i++){
        if(n==iterator->a)
            printf("第%d个节点为所求节点之一",i+1);
        iterator=iterator->next;
    }
}

查的操作并无什么难的,便不多讲了。

 

至于该,与上述函数大同小异,就不写了

 

最后也是重中之重的便是销毁整个链表

void destroy(plist p){
    int i=0;
    pnode iterator=p->head;//迭代器iterator指向链表的第一个节点 
    if(0==p->lenth) return ;//如果链表为空,这返回,不进行任何操作 
    for(i=0;i<p->lenth;i++){
        p->head=p->head->next;//将head指向第二个节点
        free(iterator);
        iterator= p->head; 
    }
}

 遍历整个链表,将其节点一个一个的释放掉内存。  然后再main函数的末尾调用这个函数。

 

这就是链表了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

  

posted @ 2017-07-18 23:51  岁月难言  阅读(290)  评论(0编辑  收藏  举报