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

来源:互联网 发布:div自动调用js函数 编辑:程序博客网 时间:2024/03/29 18:50

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

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


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

静态单链表实现


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

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

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

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

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

[cpp] view plaincopyprint?
  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;就不能用了,


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

[cpp] view plaincopyprint?
  1. f->next=ins_node;  
  2. ins_node->next=f->next;  

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

动态单链表实现

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


[cpp] view plaincopyprint?
  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. }  

双向链表实现

再想想上面图片,下面要介绍的就是双向链表,双向链表与单项链表的区别就在于它有一个指向前一个节点的指针,于是我们就应该定义这样的结构体
[cpp] view plaincopyprint?
  1. typedef struct NODE  
  2. {  
  3.     int data;  
  4.     struct NODE *next;  
  5.     struct NODE *pre;  
  6. }node;  
由于双向链表不可避免有些操作需要从后向前遍历,于是我们就应该添加一个概念,尾指针,也就是指向尾节点的指针,如下就实现了一个双向链表,它有三个节点abc,并有两种输出方式
[cpp] view plaincopyprint?
  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,然后再删除它

[cpp] view plaincopyprint?
  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


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 眼睛干干的 眼睛发干 干眼病的症状 眼睛干痒 眼睛特别干 脸干干怎么办 眼睛非常干 眼干眼涩 干眼症用什么眼药水 干眼症眼药水 干眼症自愈 眼干 眼干眼涩用什么眼药水 眼干怎么办 眼睛干痒用什么眼药水 得了干眼症多少年会失明 干登眼打牌规则 干眼症图片 严重干眼症图片 为什么得干眼症 眼干涩 什么是干眼症 干眼症的危害 干眼症有什么症状 如何缓解干眼症 干眼症怎么缓解 干眼综合症 干眼症眼睛疼 干眼症按摩 干眼症滴眼液 干眼怎么办 干眼症好了 干眼症和干眼病 眼干的症状 干眼综合征 眼干的原因 眼涩眼干怎么办 干涩眼 早上 眼干 眼干吃什么 眼干眼涩中医