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

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

静态单链表实现

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

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

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

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

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

int main(){
    //创建上图的链表
    node *head;//一个头指针。用来指向这个链表的第一个节点
    node *f=new node;//对应上图第一个节点first node,这种奇葩命名法不是我要让你们学会的,另我使用了new而不是malloc主要是因为惰性
    node *s=new node;//对应上图第二个节点second node
    node *t=new node;//对应上图第三个节点third node
    f->data=8;//第一个node的值
    f->next=s;//第一个节点next指针指向第二个节点
    s->data=7;//第二个node的值
    s->next=t;//第二个节点next指针指向第三个节点
    t->data=9;//第三个节点值
    t->next=NULL;//从上图得知第三个节点后没有节点了,所以指向NULL,通常称这个节点为尾节点
    head=f;//头指针指向第一个节点,至于为什么前面已经说了//打印这个链表里面储存的元素
    std::cout<<"链表数据:"<<"\n";
   node *print_ptr=head;//为什么这里要new一个print_ptr?因为我们可能还要利用head进行其他操作,如果直接用head进行下面的操作,就意味着head指向的位置已经改变了
   while(print_ptr!=NULL){
           std::cout<<print_ptr->data<<"\n";//通过头结点迭代打印每个节点的值
           print_ptr=print_ptr->next;//更新,让当前指针指向下一个节点
   }
   //输出节点的个数,虽然我们已经知道了是3个
   node *length_ptr=head;//同上面的
   print_ptrint k=0;
   while(length_ptr!=NULL){
           k++;length_ptr=length_ptr->next;
   }
   std::cout<<"链表节点个数:"<<"\n"<<k<<"\n";//插入一个节点,这里我选择在第一个节点与第二个节点之间插入一个新节点ins_node
   node *ins_node=new node;//为即将插入的节点从堆上分配点内存
   ins_node->data=14;//赋值
   ins_node->next=s;//让新节点的next指向第二个节点
   f->next=ins_node;//然后让第一个节点的next指向新节点,这就完成了插入//删除之前插入的节点
   ins_node=f->next;//让第一个节点next指向新插入的节点,这里你可能会感到疑惑,我建议你画图或者再看看上面的图就能理解,当然你也可以看下面
  f->next=ins_node->next;//第一个节点的next指向新节点的next,因为新节点的next相当于指向了第二个节点,所以这里也等价于第一个节点指向第二个节点f->next=f-next->next
   system("pause");
}

上面就完成了使用尾插法创建的一个链表及其简单操作包括创建/输出/插入/删除,不过如你所见它也存在许多不足,比如命名的拙计,new后没有delete,以及全部在main中执行没有考虑使用函数等等缺陷,不过这没关系,因为我们会一步步修改最终让他成为一个不错的链表

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

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





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

f->next=ins_node;
ins_node->next=f->next;

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

动态单链表实现

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

#include <iostream>

struct node
{
    int data;
    struct node *next;
};

node *create_linklist();
void print_linklist(node *head);

node *create_linklist(){
    node *head=new node;
    node *new_node,*current;//一个新节点作为当前节点
    current=head;//头结点复制给当前节点
    int k;
    printf("输入节点个数:");
    scanf("%d",&k);
    for (int i = 0; i < k; ++i)
    {
        new_node=new node;
        if(i==0)//这里也就是创建第一个节点的情况
        {
            head=new_node;//把头指针指向第一个节点
        }
        printf("输入节点数据:");
        scanf("%d",&new_node->data);
        current->next=new_node;//注意这里由于创建了一个新节点,而当前节点还是开始的head的那个位置,所以就需要更新一下,让当前节点next等于new_node的位置
        new_node->next=NULL;//当前节点的next=NULL表示当前节点就是最后一个节点了
        current=new_node;//然后把new_node节点复制给当前节点,以便继续后面的节点添加
    }
    return head;
}

void print_linklist(node *head)//参数传入一个头指针用来指向第一个节点
{
    node *phead=head;
    while(phead!=NULL)
    {
        printf("%d", phead->data);
        phead=phead->next;
    }
}

int main(){
    node *h=create_linklist();
    print_linklist(h);
    system("pause");
}

双向链表实现

再想想上面图片,下面要介绍的就是双向链表,双向链表与单项链表的区别就在于它有一个指向前一个节点的指针,于是我们就应该定义这样的结构体
typedef struct NODE
{
	int data;
	struct NODE *next;
	struct NODE *pre;
}node;

由于双向链表不可避免有些操作需要从后向前遍历,于是我们就应该添加一个概念,尾指针,也就是指向尾节点的指针,如下就实现了一个双向链表,它有三个节点abc,并有两种输出方式

#include <iostream>

typedef struct NODE
{
	int data;
	struct NODE *next;
	struct NODE *pre;
}node;

int main(){
	node *a=new node,*b=new node,*c=new node;
	node *head=a;
	node *tail=c;
	a->data=9;
	a->next=b;
	a->pre=NULL;

	b->data=17;
	b->next=c;
	b->pre=a;

	c->data=6;
	c->next=NULL;
	c->pre=b;
	//输出
	/*node *print_head=head;
	while(print_head!=NULL){
		std::cout<<print_head->data<<"\n";
		print_head=print_head->next;
	}*/
	/*node *print_head=tail;
	while(print_head!=NULL){
		std::cout<<print_head->data<<"\n";
		print_head=print_head->pre;
	}*/
	system("pause");
}

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

	//插入
	node *k=new node;
	k->data=698;
	k->pre=a;
	k->next=a->next;
	k->next->pre=a;
	a->next=k;
	//删除
	k->pre->next=k->next;
	k->next->pre=k->pre;

循环单链表

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

[数据结构]C语言链表实现的更多相关文章

  1. 数据结构C语言版 有向图的十字链表存储表示和实现

    /*1wangxiaobo@163.com 数据结构C语言版 有向图的十字链表存储表示和实现 P165 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h> ...

  2. 数据结构C语言版 表插入排序 静态表

    数据结构C语言版 表插入排序.txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了.他只是比对方更珍惜这份感情./*  数据结构C语言版 表插入排序  算法10.3 P267-P270  编译 ...

  3. C语言 链表

    原文:C语言 链表 最近在复习数据结构,想把数据结构里面涉及的都自己实现一下,完全是用C语言实现的. 自己编写的不是很好,大家可以参考,有错误希望帮忙指正,现在正处于编写阶段,一共将要实现19个功能. ...

  4. ZT C语言链表操作(新增单向链表的逆序建立)

    这个不好懂,不如看 转贴:C语言链表基本操作http://www.cnblogs.com/jeanschen/p/3542668.html ZT 链表逆序http://www.cnblogs.com/ ...

  5. 图的存储结构大赏------数据结构C语言(图)

    图的存储结构大赏------数据结构C语言(图) 本次所讲的是常有的四种结构: 邻接矩阵 邻接表 十字链表 邻接多重表 邻接矩阵 概念 两个数组,一个表示顶点的信息,一个用来表示关联的关系. 如果是无 ...

  6. python数据结构与算法——链表

    具体的数据结构可以参考下面的这两篇博客: python 数据结构之单链表的实现: http://www.cnblogs.com/yupeng/p/3413763.html python 数据结构之双向 ...

  7. 数据结构C语言版 弗洛伊德算法实现

    /* 数据结构C语言版 弗洛伊德算法  P191 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h>#include <limits.h> # ...

  8. C语言链表操作模板(添加,删除,遍历,排序)

    C语言链表操作模板,摘自郝斌的C语言视频教程,简单的修改成了纯C格式.当年照着视频学习的时候记录下来的,在使用的时候直接拿来修改修改修改能节约不少时间的. /********************* ...

  9. Python数据结构之单链表

    Python数据结构之单链表 单链表有后继结点,无前继结点. 以下实现: 创建单链表 打印单链表 获取单链表的长度 判断单链表是否为空 在单链表后插入数据 获取单链表指定位置的数据 获取单链表指定元素 ...

随机推荐

  1. SUCTF--WEB

    题目:flag 在哪? 介绍:网址(http://23.106.143.230/a_real_babyweb.php) 1,打开网址,抓包可以发现在 HTTP 头里面有 Cookie:suctf{Th ...

  2. 项目详解4—haproxy 详解

    一.企业服务架构图及负载均衡的要求 1.场景说明 在企业生产环境中,每天会有很多的需求变更,比如增加服务器.新业务上线.url路由修改.域名配置等等,对于前端负载均衡设备来说,容易维护,复杂度低,是首 ...

  3. 利用vertical-align实现行内元素对齐

    实际项目中,常常会遇到一排行内元素对齐排列的需求,但是往往它们是这样的 我们想要的其实是这样的 曾经我一度不得不使用定位来实现我想要的位置效果,将父元素设置 position:relative ,行内 ...

  4. NETStandard,NETFx,Mono,NETCore,ASPNetCore 之间关系的整理

    因为现在很多人对这几者之间的关系还不甚了解,这里根据我所知来做一个大概的介绍...... .NET Standard:         .NET标准,只要符合这个标准实现类库,即可在支持此标准的Run ...

  5. php的定界符<<<eof的问题

    在php的编程过程中难免会遇到输出大段的html和javascript脚本的情况,可都放在具体的地方的时候,路由不好处理,而且比较浪费时间 如果按照传统的输出方法,按照字符串输出的话,需要大量的转义字 ...

  6. Numpy入门 - 数组基本运算

    本节主要讲解numpy数组的基本运算,包括两数组相加.相减.相乘和相除. 一.两数组相加add import numpy as np arr1 = np.array([[1, 2, 3], [4, 5 ...

  7. log4net使用注意事项

    1配置Log4net Log4net的配置文件有几种使用方式,这里将配置log4net的部分独立出来,即关于log4net的配置独立成文件log4net.config. 1)写入Mysql log4n ...

  8. oracle触发器 调用 web接口

    最近要求开发当数据表发生变化的时候调用web接口的需求,上网找了好几篇文章看着都觉得不是很好,也根据别人的思路去实现了下,感觉都不太理想,最后使用URLConnection实现了调用.具体查看一下代码 ...

  9. esp8266 SDK开发之编译流程

    最近刚完成自己8266的小项目,已经发布在github上,有兴趣的朋友可以看一下 github地址:esp-ujn 1. 通过MQTT协议与服务器交互 2. 内置HTTP服务器,支持通过浏览器进行参数 ...

  10. PHP基础知识点

    //语法错误(syntax error)在语法分析阶段,源代码并未被执行,故不会有任何输出. /* [命名规则] */常量名 类常量建议全大写,单词间用下划线分隔 // MIN_WIDTH变量名建议用 ...