链表是通过一组任意的存储单元来存储线性表中的数据元素的,那么怎样表示出数据元素之间的线性关系呢?为建立数据元素之间的线性关系,对每个数据元素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. windows下安装rabbitmq以及php扩展amqp

    先安装RabbitMQ 安装Erlang  下载地址http://www.erlang.org/downloads 我选的是64位 这个根据自己情况下载,双击安装之 安装RabbitMQ 下载地址ht ...

  2. LeetCode 309. Best Time to Buy and Sell Stock with Cooldown (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  3. STM32F407 外部中断 个人笔记

    IO口 STM32F4有上百个IO口中,每个都可以作为外部中断输入 中断线 STM32F4的中断控制器支持22个外部中断/事件请求(中断线) 对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下 ...

  4. Leetcode 306.累加数

    累加数 累加数是一个字符串,组成它的数字可以形成累加序列. 一个有效的累加序列必须至少包含 3 个数.除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和. 给定一个只包含数字 '0'- ...

  5. 70.打印所有Spring boot载入的bean【从零开始学Spring Boot】

    [从零开始学习Spirng Boot-常见异常汇总] 问题的提出: 我们在开发过程当中,我们可能会碰到这样的问题:No qualifying bean  就是我们定义的bean无法进行注入,那到底是什 ...

  6. (3)梯度下降法Gradient Descent

    梯度下降法 不是一个机器学习算法 是一种基于搜索的最优化方法 作用:最小化一个损失函数 梯度上升法:最大化一个效用函数 举个栗子 直线方程:导数代表斜率 曲线方程:导数代表切线斜率 导数可以代表方向, ...

  7. devstack脚本安装Openstack总结(转载)

    1:vmware 基本设置 我采用的vmware workstation 8.0的版本,其他版本应该都是没问题.我是把虚拟机放在NAT的网络. 虚拟机就单块网卡就可以. 如果你希望可以在dashboa ...

  8. 【dp】codeforces C. Vladik and Memorable Trip

    http://codeforces.com/contest/811/problem/C [题意] 给定一个自然数序列,在这个序列中找出几个不相交段,使得每个段的异或值之和相加最大. 段的异或值这样定义 ...

  9. hdu 2167 状态压缩

    /*与1565的解法差不多*/ #include<stdio.h> #include<string.h> int map[16][16]; int dp[2][1<< ...

  10. CPU问题定位与解决

    CPU问题定位基本流程:   性能计数器诊断 主要用到的性能计数器 %Process Time 全实例 (主要用于查看当前服务器的CPU 情况) %Process Time sqlservr (主要用 ...