[数据结构]C语言链表实现

[数据结构]C语言链表实现http://www.cnblogs.com/racaljk/p/7822311.html

我学数据结构的时候也是感觉很困难,当我学完后我发现了之所以困难时因为我没有系统的进行学习,而且很多教授都只是注重数据结构思想,而忽略了代码方面,为此我写了这些博文给那些试图自学数据结构的朋友,希望你们少走弯路


我尝试用最简单的语言与代码来描述链表,事实上它本身也很简单

静态单链表实现


下面一部分的讨论都将围绕上面这幅图片展开,既然是逐步实现,我不考虑在开头就让这个单链表完美实现,它将只有两个部分:链表的创建&遍历链表输出

首先我们要知道一些简单的概念,一个链表是由节点构成,而每个节点又是又一个数据域和一个指向下一个节点的指针构成,因此我们可以很容易写出下面的结构

  1. struct node//节点  
  2. {  
  3.     int data;//数据域,这里我们选择存储int型  
  4.     struct node *next;//指针域,指向下一个节点,因此被定义为struct *NODE  
  5. };  

然后再认真观察一下上面的图会发现似乎还有一个头指针没有用(head pointer),头指针的作用就是在浩瀚的内存中指向这个链表的第一个节点,然后我们由第一个节点(通常称之为头结点)的next指针指向下面一个,因此很容易就能想到,只要有了头指针我们就能很容易的对链表进行各项操作。

于是我们可以继续写代码了:

  1. int main(){  
  2.     //创建上图的链表  
  3.     node *head;//一个头指针。用来指向这个链表的第一个节点  
  4.     node *f=new node;//对应上图第一个节点first node,这种奇葩命名法不是我要让你们学会的,另我使用了new而不是malloc主要是因为惰性  
  5.     node *s=new node;//对应上图第二个节点second node  
  6.     node *t=new node;//对应上图第三个节点third node  
  7.     f->data=8;//第一个node的值  
  8.     f->next=s;//第一个节点next指针指向第二个节点  
  9.     s->data=7;//第二个node的值  
  10.     s->next=t;//第二个节点next指针指向第三个节点  
  11.     t->data=9;//第三个节点值  
  12.     t->next=NULL;//从上图得知第三个节点后没有节点了,所以指向NULL,通常称这个节点为尾节点  
  13.     head=f;//头指针指向第一个节点,至于为什么前面已经说了//打印这个链表里面储存的元素  
  14.     std::cout<<"链表数据:"<<"\n";  
  15.    node *print_ptr=head;//为什么这里要new一个print_ptr?因为我们可能还要利用head进行其他操作,如果直接用head进行下面的操作,就意味着head指向的位置已经改变了  
  16.    while(print_ptr!=NULL){  
  17.            std::cout<<print_ptr->data<<"\n";//通过头结点迭代打印每个节点的值  
  18.            print_ptr=print_ptr->next;//更新,让当前指针指向下一个节点  
  19.    }  
  20.    //输出节点的个数,虽然我们已经知道了是3个  
  21.    node *length_ptr=head;//同上面的  
  22.    print_ptrint k=0;  
  23.    while(length_ptr!=NULL){  
  24.            k++;length_ptr=length_ptr->next;  
  25.    }  
  26.    std::cout<<"链表节点个数:"<<"\n"<<k<<"\n";//插入一个节点,这里我选择在第一个节点与第二个节点之间插入一个新节点ins_node  
  27.    node *ins_node=new node;//为即将插入的节点从堆上分配点内存  
  28.    ins_node->data=14;//赋值  
  29.    ins_node->next=s;//让新节点的next指向第二个节点  
  30.    f->next=ins_node;//然后让第一个节点的next指向新节点,这就完成了插入//删除之前插入的节点  
  31.    ins_node=f->next;//让第一个节点next指向新插入的节点,这里你可能会感到疑惑,我建议你画图或者再看看上面的图就能理解,当然你也可以看下面  
  32.   f->next=ins_node->next;//第一个节点的next指向新节点的next,因为新节点的next相当于指向了第二个节点,所以这里也等价于第一个节点指向第二个节点f->next=f-next->next  
  33.    system("pause");  
  34. }  
上面就完成了使用尾插法创建的一个链表及其简单操作包括创建/输出/插入/删除,不过如你所见它也存在许多不足,比如命名的拙计,new后没有delete,以及全部在main中执行没有考虑使用函数等等缺陷,不过这没关系,因为我们会一步步修改最终让他成为一个不错的链表


可能你关于上面插入与删除你没有搞清楚,这里我再特意讲一下


上图我们需要把储存14这个数据的node插入到一和二之间,于是我们就需要更改next指针,所以之前f->next=s;就不能用了,


你可能会想到让第一个指向新节点,然后再让新节点指向第三个,如:

  1. f->next=ins_node;  
  2. ins_node->next=f->next;  

事实通常证明第一感觉是错误的,仔细看看上面代码你就会发现加入f->next=ins_node,那么第二条就相当于ins_node->next=ins_node;它指向了自己!所以我们需要反过来思考,先让新节点指向第二个,然后再让第一个指向新节点,就如上面的代码了,至于删除我没有弄出图片,我只是简单讲一下,删除是直接让第一个节点的next指向需要删除的节点,然后再让第一个节点的next指向需要删除的节点的next,你可能会思考为什么不直接让第一个节点next指向第二个呢?这个疑问你可以自己解答比较好

动态单链表实现

到这里一个简单的链表就已经实现了,但是我们还需要继续改进,因为我们有时候不知道每个节点储存的数据,所以我们就需要一个动态链表了,下面这个将实现把用户输入的数据以链式结构储存,也就是动态链表


  1. #include <iostream>  
  2.   
  3. struct node  
  4. {  
  5.     int data;  
  6.     struct node *next;  
  7. };  
  8.   
  9.   
  10. node *create_linklist();  
  11. void print_linklist(node *head);  
  12.   
  13. node *create_linklist(){  
  14.     node *head=new node;  
  15.     node *new_node,*current;//一个新节点作为当前节点  
  16.     current=head;//头结点复制给当前节点  
  17.     int k;  
  18.     printf("输入节点个数:");  
  19.     scanf("%d",&k);  
  20.     for (int i = 0; i < k; ++i)  
  21.     {  
  22.         new_node=new node;  
  23.         if(i==0)//这里也就是创建第一个节点的情况  
  24.         {  
  25.             head=new_node;//把头指针指向第一个节点  
  26.         }  
  27.         printf("输入节点数据:");  
  28.         scanf("%d",&new_node->data);  
  29.         current->next=new_node;//注意这里由于创建了一个新节点,而当前节点还是开始的head的那个位置,所以就需要更新一下,让当前节点next等于new_node的位置  
  30.         new_node->next=NULL;//当前节点的next=NULL表示当前节点就是最后一个节点了  
  31.         current=new_node;//然后把new_node节点复制给当前节点,以便继续后面的节点添加  
  32.     }  
  33.     return head;  
  34. }  
  35.   
  36. void print_linklist(node *head)//参数传入一个头指针用来指向第一个节点  
  37. {  
  38.     node *phead=head;  
  39.     while(phead!=NULL)  
  40.     {  
  41.         printf("%d", phead->data);  
  42.         phead=phead->next;  
  43.     }  
  44. }  
  45.   
  46. int main(){  
  47.     node *h=create_linklist();  
  48.     print_linklist(h);  
  49.     system("pause");  
  50. }  

双向链表实现

再想想上面图片,下面要介绍的就是双向链表,双向链表与单项链表的区别就在于它有一个指向前一个节点的指针,于是我们就应该定义这样的结构体
  1. typedef struct NODE  
  2. {  
  3.     int data;  
  4.     struct NODE *next;  
  5.     struct NODE *pre;  
  6. }node;  
由于双向链表不可避免有些操作需要从后向前遍历,于是我们就应该添加一个概念,尾指针,也就是指向尾节点的指针,如下就实现了一个双向链表,它有三个节点abc,并有两种输出方式
  1. #include <iostream>  
  2.   
  3. typedef struct NODE  
  4. {  
  5.     int data;  
  6.     struct NODE *next;  
  7.     struct NODE *pre;  
  8. }node;  
  9.   
  10. int main(){  
  11.     node *a=new node,*b=new node,*c=new node;  
  12.     node *head=a;  
  13.     node *tail=c;  
  14.     a->data=9;  
  15.     a->next=b;  
  16.     a->pre=NULL;  
  17.   
  18.     b->data=17;  
  19.     b->next=c;  
  20.     b->pre=a;  
  21.   
  22.     c->data=6;  
  23.     c->next=NULL;  
  24.     c->pre=b;  
  25.     //输出  
  26.     /*node *print_head=head; 
  27.     while(print_head!=NULL){ 
  28.         std::cout<<print_head->data<<"\n"; 
  29.         print_head=print_head->next; 
  30.     }*/  
  31.     /*node *print_head=tail; 
  32.     while(print_head!=NULL){ 
  33.         std::cout<<print_head->data<<"\n"; 
  34.         print_head=print_head->pre; 
  35.     }*/  
  36.     system("pause");  
  37. }  

双向链表的难点不是创建输出而是插入与删除,我没有制作图片,所以这需要读者认真去思考一下,建议画图,也很容易理解,下面代码是在上面创建了abc的基础上实现的在ab间插入一个k,然后再删除它

  1. //插入  
  2. node *k=new node;  
  3. k->data=698;  
  4. k->pre=a;  
  5. k->next=a->next;  
  6. k->next->pre=a;  
  7. a->next=k;  
  8. //删除  
  9. k->pre->next=k->next;  
  10. k->next->pre=k->pre;  

循环单链表

循环链表我不考虑讲,因为它就是把尾巴节点指向了头节点从而形成一个环,所以说循环链表不叫loop linked list而叫circular linked list


作者:racaljk


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值