C语言中线性表的实现

       最为简单的数据结构是线性结构,包括线性表、堆栈、队列等,本篇文章将为大家介绍对线性表的操作。

        首先明确线性表的特点以及它所需要实现的功能,明确了这两点我们就可以编程形成一个“.h”文件,供以后开发app的时候使用,完成了线性表这个工具后,我们就可以在任何需要线性存储结构的地方使用我们提供的线性表工具。

        下面我们来介绍线性表的特点:首先要明确线性表是一个用来存储数据的工具。其实我们耳熟能详的数组、链表等都属于线性结构,但是并不能把他们成为线性表,原因就是他们只是可以存储数据,但是不存在对数据的操作与控制。生活中我们接触各种各样的表格,它们都有一个共同的特点:1、都是用来存储信息的,或是人员信息,或是书籍登录信息... ... 2、都有表头来进行索引,以方便我们快速找到人员的信息;比如我们可以通过学号找到其对应的学生的姓名。可以通过书籍的编号找到其对应的书名。

        线性表也具有同样的特点,因此,它相对于的简单的线性结构所不同的就是在于多了一个控制头以方便我们对其内部的数据进行操作。那么,究竟要用什么做它的控制头呢?我们要从线性表的实际功能出发。首先,线性表的最大空间是多少,即,最多能存储多少的数据;其次,线性表当前存储了多少的数据;最后,就是真正存储数据的空间。真正存储数据的空间可以是链表也可以是数组,如果是链表存储,那么就不需要规定最大存储空间,本文介绍用数组方式的实现。

        有了上文说的几点,我们就可以生成线性表的表头了,跟链表的控制头一样,我们知道了一个线性表的表头就等于知道了一整个线性表,只不过线性表的表头提供的信息更加丰富一些。具体代码如下:

typedef struct LINEAR{
	int count;
	int maxRoom;
	USER_TYPE *head;
}LINEAR;
        我们用一个结构体来存储一个线性表的所有信息,这里的“head”是一个动态申请空间的数组。由于我们现在做的是线性表工具,是提供给想用线性表存储数据的程序员来开发app的,我们不知道以后开发人员想要用线性表存储什么样的数据,可能是简单的整形数据,也可能是非常复杂的结构体数据,那么我们就要求使用我们线性表工具的开发人员在使用我们的工具之前先实现形如这样的语句:”typedef xxx USER_TYPE;“来定义他希望用线性表来存储的数据类型。

        有了表头,接下来我们要考虑的事情就是如何初始化线性表了。初始化线性表说白了就是生成一个表头,因为我们之前说过有了表头就相当于有了一个线性表,那么我们在初始化线性表的时候需要知道的信息就是这个线性表能够存储多少数据,即maxRoom是多少,具体代码如下:

boolean initLinear(LINEAR **linear, int maxRoom) {
	if (*linear != NULL) {
		return FALSE;
	}

	*linear = (LINEAR *)calloc(1, sizeof(LINEAR));
	(*linear)->maxRoom = maxRoom;
	(*linear)->head = (USER_TYPE *)calloc(maxRoom, sizeof(USER_TYPE));

	return TRUE;
}
        注:C语言里不存在boolean类型,这里的boolean是我们自己用"typedef"定义的类型,只有两种取值,TRUE和FALSE。

        我们在这段代码中可以观察到传递过来的表头是“LINEAR **”类型的,至于为什么传递二阶指针我们用一幅图来解释。


        由于我们想要初始化一个线性表,首先要动态申请一个表头的LINEAR类型的空间,因此使用我们工具的人那里必须有一个LINEAR *类型的空间用来存储当前线性表的首地址,那么我们现在初始化一个线性表第一步要做的就是动态申请一个线性表表头空间并把申请到的空间的首地址的值赋给用户程序中传递过来的LINEAR *类型的变量空间,这样用户在调用了我们的初始化线性表的程序后,他传递过来的LINEAR *类型的变量就指向了一个我们编写的初始化线性表函数新申请的表头空间。
        通过以上的分析我们可以看出,我们需要更改用户的LINEAR *类型的变量的值,因此我们必须要求用户传递给我们LINEAR *类型空间的首地址,即,LINEAR **。
        通过图中我们可以看到,初始化线性表的第二步,是要给线性表申请真正存储数据的空间,我们就要根据传递过来的maxRoom的值为线性表的数组元素动态申请空间,即,给USER_TYPE *data赋值。使得线性表拥有可以存储数据的空间。
        这样,线性表的初始化工作就完成了,接下来就是完成线性表的各种操作了,其中包括:增加数据,删除数据, 更改指定数据的值,查找数据,重置线性表,销毁线性表... ...
        这里我们只介绍增加数据,查找数据,和销毁线性表。懂得了这几种操作会发现,其实对线性表的操作都大同小异。我们首先先把线性表的整体程序给出再做详细介绍。
#ifndef _LINEAR_H_
#define    _LINEAR_H_

#include<stdio.h>
#include<malloc.h>

typedef    unsigned char boolean;
#define    TRUE        1
#define    FALSE        0

#define NOT_FOUND    -1

typedef struct LINEAR{
    int count;
    int maxRoom;
    USER_TYPE *head;
}LINEAR;

boolean initLinear(LINEAR **linear, int maxRoom);
void destoryLinear(LINEAR **linear);
boolean isLinearFull(LINEAR *linear);
boolean isLinearEmpty(LINEAR *linear);
boolean appendEleToLinear(LINEAR *linear, USER_TYPE ele);
boolean insertEleBeforeTarget(LINEAR *linear, USER_TYPE ele, USER_TYPE local, boolean equals(USER_TYPE one, USER_TYPE another));
int findElefromLinear(USER_TYPE ele, LINEAR linear, boolean equals(USER_TYPE one, USER_TYPE another));
boolean removeEleFromLinear(LINEAR *linear, USER_TYPE ele, boolean equals(USER_TYPE one, USER_TYPE another));
void showLinear(LINEAR *linear, void showOneEle(USER_TYPE ele));
void resetLinear(LINEAR *linear);

void resetLinear(LINEAR *linear) {
    linear->count = 0;
}

void showLinear(LINEAR *linear, void showOneEle(USER_TYPE ele)) {
    int i;

    for (i = 0; i < linear->count; i++) {
        showOneEle(linear->head[i]);
    }
}

boolean removeEleFromLinear(LINEAR *linear, USER_TYPE ele, boolean equals(USER_TYPE one, USER_TYPE another)) {
    int i;

    if (isLinearEmpty(linear)) {
        return FALSE;
    }
    i = findElefromLinear(ele, *linear, equals);
    if (i == NOT_FOUND) {
        return FALSE;
    }

    for (; i < linear->count-1; i++) {
        linear->head[i] = linear->head[i + 1];
    }
    linear->count--;
    return TRUE;
}

int findElefromLinear(USER_TYPE ele, LINEAR linear, boolean equals(USER_TYPE one, USER_TYPE another)) {
    int i;

    for (i = 0; i < linear.count && equals(linear.head[i], ele) == FALSE; i++)
        ;
    if (i == linear.count) {
        return NOT_FOUND;
    }
    return i;
}

boolean insertEleBeforeTarget(LINEAR *linear, USER_TYPE ele, USER_TYPE localEle, boolean equals(USER_TYPE one, USER_TYPE another)) {
    int local;
    int j;

    if (isLinearFull(linear)) {
        return FALSE;
    }

    local = findElefromLinear(localEle, *linear, equals);

    if (local == NOT_FOUND) {
        return FALSE;
    }

    for (j = linear->count - 1; j >= local; j--) {
        linear->head[j + 1] = linear->head[j];
    }
    linear->head[local] = ele;
    linear->count++;

    return TRUE;
}

boolean appendEleToLinear(LINEAR *linear, USER_TYPE ele) {
    if (linear == NULL) {
        return FALSE;
    }
    if (TRUE == isLinearFull(linear)) {
        return FALSE;
    }

    linear->head[linear->count] = ele;
    linear->count++;

    return TRUE;
}

boolean isLinearEmpty(LINEAR *linear) {
    return linear->count == 0;
}

boolean isLinearFull(LINEAR *linear) {
    return linear->count == linear->maxRoom;
}

void destoryLinear(LINEAR **linear) {
    if (NULL == linear) {
        return;
    }

    if ((*linear)->head) {
        free((*linear)->head);
    }
    free(linear);
}

boolean initLinear(LINEAR **linear, int maxRoom) {
    if (*linear != NULL) {
        return FALSE;
    }

    *linear = (LINEAR *)calloc(1, sizeof(LINEAR));
    (*linear)->maxRoom = maxRoom;
    (*linear)->head = (USER_TYPE *)calloc(maxRoom, sizeof(USER_TYPE));

    return TRUE;
}

#endif // !_LINEAR_H_

        首先先说查找数据:
        其实线性表查找数据的本身和给数组查找数据并无两样,只不过要通过表头来访问表头里的数组元素,再进行查找功能。但是这里需要注意的是,我们所编写的是一个工具,我们并不知道以后用户想要用我们的工具存储什么样的数据,所以我们并不知道应该如何判定用户所给的数据和线性表中的数据的哪一个是相等的。因此我们就需要让用户来编写判断两个数据是否相等的函数,然后将指向这个函数的指针传递过来,我们在编写程序是就只要用形参里的函数代替我们无法确定的比较过程就好了。详情见程序中
int findElefromLinear(USER_TYPE ele, LINEAR linear, boolean equals(USER_TYPE one, USER_TYPE another));
        接下来介绍删除元素(插入元素和删除较为类似):
        在编写完查找数据后,删除数据就显得比较容易了,因为删除的最重要的一个步骤就是定位到要删除得数据的位置,而查找函数已经实现了这一功能,我们只需要调用就好了。在定位完之后,我们就可以通过移动数组内元素的位置来实现删除。详情见程序中
boolean removeEleFromLinear(LINEAR *linear, USER_TYPE ele, boolean equals(USER_TYPE one, USER_TYPE another));
        由于其他操作或是类似于前两个操作,或是比较简单且容易理解,这里就不多介绍了,下面我们着重讲销毁线性表:
        前面我们介绍过,线性表的空间都是动态申请的,而C语言中动态申请的空间都是要手动释放的,因此我们就不得不编写销毁线性表的程序,不然会造成非常危险的内存泄漏问题。通过前面的介绍,我们知道我们的线性表有两类空间是我们动态申请的,一个是真正存储数据的空间,一个是控制头的空间,由于我们的真正存储空间的首地址是存放在控制头内的,我们就必须先释放存储数据的空间,再释放控制头空间,这样才不会发生非法内存访问。详情见程序中
void destoryLinear(LINEAR **linear);
        到这里,线性表的基本内容就已经介绍完毕了,如果还有不合理的地方,欢迎给我留言。
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值