数据结构编程笔记四:第二章 线性表 单链表的实现

上次我们已经看过顺序表的实现方式,这次来看看单链表的实现。

还是老规矩:

程序在码云上可以下载。
地址:https://git.oschina.net/601345138/DataStructureCLanguage.git

单链表的ADT与顺序表一样:

ADT List{
    数据对象:D={ ai | ai ∈ElemSet, i=1,2,...,n,  n≥0 } 
    数据关系:R1={ <ai-1 ,ai >|ai-1 ,ai∈D,  i=2,...,n }
    基本操作:
             (1)线性表的初始化操作 
                  InitList(&L,n)
                  操作结果:将L初始化为空表,申请空间的大小为n。
             (2)线性表元素输入操作 
                  ListInput(&L)
                  初始条件:线性表L已存在 。
                  操作结果:线性表中的部分元素或全部元素已被赋值。     
             (3)线性表的置空操作 
                  ClearList(&L) 
                  初始条件:线性表L已存在且不为空 。
                  操作结果:将表L置为空表。
             (4)线性表的判空操作 
                  ListIsEmpty(&L)
                  初始条件:线性表L已存在。
                  操作结果:如果L为空表则返回1,否则返回0。
             (5)获取线性表元素个数的操作 
                  ListLength(L)
                  初始条件:线性表L已存在。
                  操作结果:如果L为空表则返回0,否则返回表中的元素个数。
             (6)获取线性表第i个元素的操作(用元素的位序查找元素的值) 
                  GetItem(L,i,&e)
                  初始条件:表L已存在且不为空,1<=i<=ListLength(L)。
                  操作结果:用e返回L中第i个元素的值。
             (7)确定线性表元素位置的操作 (用元素的值确定元素位置) 
                  LocateElem(L,e) 
                  初始条件:表L已存在且不为空,e为合法元素值。
                  操作结果:如果L中存在元素e,则将“当前指针”指向第一个这样的元素e所在位置并返回真,否则返回假。
             (8)线性表插入元素操作 
                  ListInsert(&L,i,e)
                  初始条件:表L已存在且不为空,e为合法元素值且1≤i≤ListLength(L)+1。
                  操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。
             (9)输出线性表元素操作 
                  PrintList(&L) 
                  初始条件:线性表L已存在且不为空。
                  操作结果:线性表L中的所有元素已输出。 
             (10)销毁线性表操作 
                  DestroyList(&L) 
                  初始条件:线性表L已存在。
                  操作结果:将L销毁。                      
}ADT List

单链表采用了链式存储结构,不再要求存储空间必须是连续的(我没说链表的存储空间不可以连续,只是不强制要求连续而已(连续不连续都可以),比如后面要介绍的静态链表的存储空间就是连续的)。这就决定了单链表的插入删除效率高(不需要来回移动元素,只要改改指针就行了),但是查询效果却不理想(只能一个个地扫描式的向后查找,不能像顺序表一样使用位置计算公式直接定位)。

一起来看看单链表的代码实现吧:

//*******************************************引入头文件*********************************************
#include <stdio.h>   //使用了标准库函数 
#include <stdlib.h>  //使用了动态内存分配函数 

//******************************************自定义符号常量******************************************* 

#define OVERFLOW -2         //内存溢出错误常量
#define ILLEGAL -1          //非法操作错误常量 
#define OK 1                //表示操作正确的常量 
#define ERROR 0             //表示操作错误的常量

//******************************************自定义数据类型********************************************

typedef int Status;       //用typedef给int起个别名,也便于程序的维护 
typedef float ElemType;   //用typedef给float起个别名,也便于程序的维护

typedef struct LNode {    //用C语言描述线性单链表的结构,声明结构体的同时声明一个结构体指针 
    ElemType data;            //数据域 
    struct LNode *next;       //指针域 
}LNode, * LinkList; 

/*说明:该操作等价于: 
     struct LNode   //先声明一个结构体 
     {
         ElemType data;            //数据域 
         struct LNode *next;       //指针域 
     };
     typedef LNode * LinkList;     //声明一个结构体指针 
*/

//******************************************线性表的主要操作****************************************** 

//1.-------------------------------------线性单链表的初始化操作--------------------------------------- 

/*
    函数:MallocList_L
    参数:LinkList &L 带回创建的节点  
    返回值:状态码,OK表示操作成功 
    作用:申请一个节点的内存空间
*/
Status MallocList_L(LinkList &L) {

    //为线性表L开辟内存空间,只申请一个结点的内存空间 
    L = (LinkList)malloc(sizeof(LNode));
    if(!L) {  //if(!L) <=> if(L == NULL) 
        printf("申请内存失败!\n");
        exit(OVERFLOW);  //退出程序,并提示用户内存分配失败的原因是内存泄露 
    }//if

    //操作成功 
    return OK; 
} //MallocList_L

/*
    函数:InitList_L
    参数:LinkList &L 单链表的头指针 
          int n 线性表元素的个数 
    返回值:状态码,OK表示操作成功 
    作用:构造一个线性表并将其初始化 
*/
Status InitList_L(LinkList &L, int n){

    //i声明在这里不是好的写法,应该将i写在for循环中,有利于缩短i的作用域,及早回收其内存空间。
    //但是由于C语言不兼容这种写法,考虑到兼容性所以将i写在外面。(C++支持这种写法) 
    int i;

    //工作指针 
    LinkList p;

    //先构造一个空的单链表,完成头结点的创建 
    MallocList_L(L);

    //将头结点的指针域设置为NULL 
    L->next = NULL;  

    //->初始化单链表的方法有多种,这里写了两种方法,根据需要保留一个,另一个注释掉即可 

    /* 
    //1.使用头插法创建带头结点的单链线性表L,要求输入元素时“逆位序 ”输入  
    for(i = n; i > 0; --i){

        //生成新结点
        p = (LinkList)malloc(sizeof(LNode));

        //从键盘接收元素值,并存入p指向结点的数据域
        printf("请输入第%d个元素的值:(逆位序输入)\n", i);
        scanf("%f", &p->data);

        //将线性表头结点后面的所有节点链接到p后面 
        p->next = L->next;

        //将p链接到头结点后面,也就是把p插入到了线性表的表头 
        L->next = p;
    } 
    */

    //2.使用尾插法创建带头结点的单链线性表L,按照正常位序输入元素
    printf("请依次输入元素的值(用空格隔开):\n");
    LinkList tail = L;
    for(i = 0; i < n; i++){

        //生成一个新结点,使p指向此结点
        MallocList_L(p);

        //从键盘接收元素值,并存入p指向结点的数据域 
        scanf("%f", &p->data);

        //将新申请的结点的指针域设为NULL,因为p结点要作为新的表尾结点使用,所以后面没有后继 
        p->next = NULL;

        //将p节点链接到当前表尾元素的后面,成为新的表尾结点 
        tail->next = p;

        //p成为了新的表尾结点,修改tail使其指向p
        tail = p;   
    }//for

    //头结点数据域记录了单链表的长度,由于初始化了n个元素,所以赋值n 
    L->data = n;

    //操作成功 
    return OK;  
} //InitList_L

//2.-------------------------------------判断线性单链表是否为空------------------------------------

/*
    函数:ListIsEmpty_L
    参数:LinkList &L 单链表的头指针 
    返回值:如果线性表是空表返回1,否则返回0 
    作用:判断线性表L是否为空 
*/
int ListIsEmpty_L(LinkList &L) {

    //如果头结点后面有节点,那么单链表就不为空
    //L->next == NULL表示头结点后面没有节点 
    return L->next == NULL; 
} //ListIsEmpty_L 

//3.------------------------------------线性单表中插入元素的操作-----------------------------------

/*
    函数:ListInsert_L
    参数:LinkList &L 单链表的头指针 
          int i 插入位置i 
          ElemType e 插入元素e 
    返回值:状态码,OK表示操作成功,ERROR表示操作失败 
    作用:在带头结点的线性单链表L中第i个位置之前插入元素e
*/
Status ListInsert_L(LinkList &L, int i, ElemType e){

    //工作指针 
    LinkList p = L,s;

    //计数器,临时变量,用于记录查找插入位置的下标情况 
    int
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值