链表是通过一组任意的存储单元来存储线性表中的数据元素的,那么怎样表示出数据元素之间的线性关系呢?为建立数据元素之间的线性关系,对每个数据元素ai,除了存放数据元素的自身信息ai之外,还需要存放和ai一起存放其后继ai+1所在的存储单元的地址,这两部分信息组成一个"节点"(如图),存放数据元素信息的称为数据域,存放其后继地址的称为指针域。因此,n个元素的线性表通过每个节点的指针域拉成了一条"链子", 称之为链表。因为每个节点中只有一个指向后继的指针,所以称之为单链表。

链表是由一个个节点构成,节点定义如下:

typedef   struct   node

{

datatype data;

struct node *next;

}LNode, *LinkList;

定义指针变量: LinkList H;

下图分别是带头节点的单链表空表和非空表的示意图

注:LNode是节点的类型,LinkList是指向LNode类型节点的指针类型。

在操作中需要用到某节点的指针变量时, 做如下声明是等价的。

LNode *p;  等价于  LinkList H;

p  = malloc(sizeof(LNode)); 表示申请一块LNode类型的存储单元,并将这块存储空间的地址赋值给变量p. p所指的节点为*p, *p的类型为LNode型,所以该节点的数据域为(*p).data 或 p->data, 指针域为(*p)->next, 或 p->next。

存储空间的分配和释放

1.存储空间分配函数原型: void *malloc(unsigned int size);

作用是在内存中动态获取一个大小为size个字节的连续的存储空间。并返回一个void类型的指针,若分配成功,该指针指向已分配空间的起始地址,否则,该指针将为空(NULL)

2.连续空间分配函数原型: void *calloc(unsigned, n unsigned size);

作用是在内存中动态获取n个大小为size个字节的连续的存储空间。 该函数将返回一个void类型指针,若分配成功,该指针指向已分配空间的首地址,否则返回空(NULL)。用calloc()可以动态获取一个一维数组空间,其中n为数组元素个数,每个数组元素的大小为size个字节。

3.空间释放函数原型:void free(void *addr);

free()的作用是释放由addr指针所指向的空间,即系统回收,使这段空间又可以被其它变量所用。

建立和输出链表

所谓动态建立链表是指在程序执行过程中从无到有地建立链表,将一个个新生成的节点依次链接入已建立起来的链表上。上一个节点的指针域存放下一个节点的起始地址,并给各个节点数据域赋值。

例如,建立一个学生成绩的链表,其节点的结构体定义如下:

struct student
{
    long num;
    char name[20];
    float score;
    struct student *next;
};

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

4步建立链表:

1.定义三个指针变量,head头指针,p1指向新节点,p2指向尾节点

2.产生一个节点, head , p1和p2都指向它,并输入想要的数据。

3.循环操作,陆续产生新节点,输入数据,链接到表尾

4.最后,尾节点指针域置空,返回头指针。

create()函数创建链表, 返回链表头指针:

struct student *create()
{
    struct student *p1, *p2, *head;
    int i, n = 2;
    head = NULL;
 
    head = p1 = p2 = (struct student *) malloc(sizeof(struct student));
 
    if(!head) return false; //检测内存空间是否申请成功
 
    printf("input num  name  score\n");
    scanf("%ld%s%f",&p1->num,&p1->name,&p1->score);
 
    for(int i = 1; i<n; i++)
    {
        p1= (struct student *)malloc(sizeof(struct student));
 
        if(!p1) return false;
 
        scanf("%ld%s%f",&p1->num,&p1->name,&p1->score);
        p2->next = p1;
        p2 = p1;
    }
    p2->next = NULL;
    return head;
}

利用print()函数输出链表数据:

void print(struct student *p)
{
    while(p != NULL)
    {
        printf("ID: %ld Name: %10s  score:%6.2f\n",p->num,p->name,p->score);
        p=p->next;
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }最后在main函数中完成调用工作:

int main(void)
{
    struct student *head;
    head = create();
    print(head);
 
    fflush(stdin);
    getchar();
 
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }单链表的基本操作

1.插入。

如果要在单链表的两个数据元素之间插入一个数据元素x,已知p为其单链表存储结构中指向节点a的指针(如图(a)),为插入数据元素x,首选要生成一个数据域为x的节点,然后插在单链表中,插入前需要修改节点a中的指针域,令其指向节点x,而节点x中的指针域应指向节点b,从而实现三个元素a,b和x之间逻辑关系的变化。插入单链表如图(b),假设s为指向节点x的指针,则上述过程可表述如下:

s->next = p->next;

p->next = s;

图(a)

图(b)

2.删除

要删除单链表中的元素x,仅需要把x节点的指针域目前保存的它的下一个节点b的地址赋值给a节点的指针域即可。 假设p,q分别是指向a,x节点的指针,则:

p->next = q->next;

free(q);

3.查找

链表查找是指在链表中查找某成员值为给定值的节点。下面定义一个查找函数,它的返回值即为指向查找到的节点的指针。查找方法是先输入要查找的给定值,然后从链表的头指针所指的第一个节点开始,按链接顺序逐一比较:当查找到给定值的节点时,则返回该节点,否则返回空指针。

struct student *find(struct student *p)
{
    long num;
    printf("Input the std ID :");
    scanf("%ld",&num);
 
    while(p != NULL)
        {
            if(num == p->num) return p;
            p = p->next;
        }
    return NULL;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

双链表

单链表的节点中只有一个指向其后继节点的指针域next, 因此,若已知某节点的指针为p, 其后继节点的指针则为p->next, 而找其前驱则只能从该链表的头指针开始,顺着各节点的next域进行,也就是说找后继的时间性能是O(1),而找前驱的时间性能是O(n) , 如果希望找前驱的时间性能达到O(1),则只能付出空间代价:每个节点再加一个指向前驱的指针域,节点的结构如图(1),用这种节点组成的链表称为双向链表:

双链表节点的定义如下:

typedef struct dlnode
{
    datatype data;
    struct dlnode *prior , *next;
}DLNode , *DLinkList;

与单链表类似,双向链表通常也是用头指针标识,也可以带头节点和作成循环结构,图2是带头节点的双向链表图。显然通过某节点的指针p即可以直接得到它的后继节点指针p->next和前驱节点p->prior. 假设p是指向双向循环链表中的某一节点的指针,则p->prior->next表示的是*p节点的前驱节点的后继节点的指针,即与p相等。

双向链表中节点的插入:设p指向双向链表中某节点,s指向待插入的值为x的新节点,将*s插入到*p的前面,插入如图3, 操作如下:
1. s->prior = p->prior;
2. p->prior->next = s;
3. s->next = p;
4. p->prior = s;
双向链表中节点的删除,蛇p指向双向链表中某节点,删除*p;
1.p->prior->next = p->next;
2. p->next->prior = p->prior;
3. free(p ) ;
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

C 项目案例实践(1)数据结构之链表(0)的更多相关文章

  1. Selenium自动化测试项目案例实践公开课

    Selenium自动化测试项目案例实践公开课: http://gdtesting.cn/news.php?id=55

  2. C项目案例实践(0)-语言基础

    1.C语言数据类型 所谓数据类型是按被定义变量的性质.表示形式.占据存储空间的多少.构造特点来划分的.C中数据类型可分为基本数据类型.构造数据类型.指针类型.空类型4大类,结构如图: (1)基本数据类 ...

  3. Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍

    网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edisonchou/p/4458219.html 网站日志分析项目案例 ...

  4. TOP100summit 2017:小米唐沐等大咖精心挑选的100个年度研发案例实践

    2017年,机器学习.大数据.人工智能等词汇成为软件研发行业的主流,大前端.DevOps.区块链等技术方式成为热点方向:2017年,智能硬件开始成为新的焦点,这一年更被称为智能音箱井喷的一年:2017 ...

  5. ASP.NET Core基于K8S的微服务电商案例实践--学习笔记

    摘要 一个完整的电商项目微服务的实践过程,从选型.业务设计.架构设计到开发过程管理.以及上线运维的完整过程总结与剖析. 讲师介绍 产品需求介绍 纯线上商城 线上线下一体化 跨行业 跨商业模式 从0开始 ...

  6. 前端 go.js 流程图基于vue开发项目案例

    一.流程图效果 最近一段时间在研究go.js,它是一款前端开发画流程图的一个插件,也是一个难点,要说为什么是难点,首先,它是依赖画布canvas知识开发.其次,要依赖于内部API开发需求,开发项目需求 ...

  7. 测试开发实战[提测平台]20-图表G2Plot在项目的实践实录

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. G2Plot项目应用 上一篇<提测平台19-Echarts图表在项目的实践>讲解了Echarts的图表应用,此篇来看下开箱即用 ...

  8. Kafka与Spark案例实践

    1.概述 Kafka系统的灵活多变,让它拥有丰富的拓展性,可以与第三方套件很方便的对接.例如,实时计算引擎Spark.接下来通过一个完整案例,运用Kafka和Spark来合理完成. 2.内容 2.1 ...

  9. 学习javascript数据结构(二)——链表

    前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...

随机推荐

  1. IDM下载器使用方法详解:百度网盘下载,视频会员一网打尽!

    一. IDM的设置 [01]IDM插件与各大浏览器的集成 默认情况下,在成功安装IDM后,直接点击这里的选项,会弹出[常规设置],一般情况下直接保持默认的配置即可,如果你使用的是比较小众的浏览器,你可 ...

  2. Vutrl 自己搞SS的些问题

    虽然是第二次搞这玩意但还是搞了我三天,有些东西还是想要记录一下的,以下是我犯的错误 至于如何开始搭建Vutrl上面的服务器,下面有两个链接自己搞,我就讲讲我自己碰到的问题 https://segmen ...

  3. POJ 1486 Sorting Slides【二分图匹配】

    题目大意:有n张幻灯片和n个数字,幻灯片放置有重叠,每个数字隶属于一个幻灯片,现在问你能够确定多少数字一定属于某个幻灯片 思路:上次刷过二分图的必须点后这题思路就显然了 做一次二分匹配后将当前匹配的边 ...

  4. [NOIP2000] 提高组 洛谷P1023 税收与补贴问题

    题目背景 每样商品的价格越低,其销量就会相应增大.现已知某种商品的成本及其在若干价位上的销量(产品不会低于成本销售),并假设相邻价位间销量的变化是线性的且在价格高于给定的最高价位后,销量以某固定数值递 ...

  5. 在eclipse中画类图

    学习设计模式的时候,希望能够画出类图,理清关系.但是StarUML还有重新去写类名.属性.方法等,不是很方便.网上给出了安装插件的方法额,就可以直接在eclipse中拖拽类,很方便.但是网上给出的插件 ...

  6. 2018 百度之星 初赛 第六题 HDU6349

    三原色图  Accepts: 281  Submissions: 1261  Time Limit: 1500/1000 MS (Java/Others)  Memory Limit: 262144/ ...

  7. HDU3430 (置换群循环节+中国剩余定理)

    题意:给出n张牌,标号为1-n,然后给出两个序列,序列1表示序列1,2,3,4……,n洗一次牌后到达的,序列2表示目标序列,问初始序列按序列1的洗牌方式洗几次能到达序列2的情况,如果不能到达输出-1. ...

  8. ATcoder 1983 BBQ Hard

    E - BBQ Hard Time limit : 2sec / Memory limit : 256MB Score : 1400 points Problem Statement Snuke is ...

  9. 转: 关于Linux常用的二进制文件分析方法

    当你在unix下拿到一个二进制文件但不知道它是什么的时候,可以通过以下方法得到一此提示 1. 最首先应该尝试strings命令,比如拿到一个叫cr1的二进制文件,可以: $ strings cr1 | ...

  10. IOCP实现的任务队列

    unit IOCPQueue; interface uses windows, classes; type TOnQueueProc = procedure(sender: tobject; Para ...