线性表(顺序表和链表)的学习总结与C语言实现(数据结构学习2)

定义

通过学习我们知道,具有“一对一”逻辑关系的数据,最佳的存储方式是使用线性表。那么,什么是线性表呢?

线性表,全名为线性存储结构。使用线性表存储数据的方式可以这样理解,即“把所有数据用一根线儿串起来,再存储到物理空间中”。


线性表.png

将具有“一对一”关系的数据“线性”地存储到物理空间中,这种存储结构就称为线性存储结构(简称线性表)。

使用线性表存储的数据,如同向数组中存储数据那样,要求数据类型必须一致,也就是说,线性表存储的数据,要么全不都是整形,要么全部都是字符串。一半是整形,另一半是字符串的一组数据无法使用线性表存储。

顺序存储结构和链式存储结构

把这“一串儿”数据放置到物理空间,我们可以选择以下两种方式:

  • 1、顺序存储;


    顺序存储结构.png

如上图所示,将数据依次存储在连续的整块物理空间中,这种存储结构称为顺序存储结构(简称顺序表);

  • 2、链式存储。


    链式存储结构.png

    如上图所示,数据分散的存储在物理空间中,通过一根线保存着它们之间的逻辑关系,这种存储结构称为链式存储结构(简称链表);

也就是说,线性表存储结构可细分为顺序存储结构和链式存储结构。

对于非空的线性表和线性结构,其特点如下:

  1. 存在唯⼀的一个被称作”第⼀个”的数据元素;
  2. 存在唯⼀的一个被称作”最后一个"的数据元素;
  3. 除了了第一个之外,结构中的每个数据元素均有一个前驱;
  4. 除了了最后一个之外,结构中的每个数据元素都有一个后继。

顺序表代码实现

顺序表存储数据时,会提前申请一整块足够大小的物理空间,然后将数据依次存储起来,存储时做到数据元素之间不留一丝缝隙。

我们可以发现,顺序表存储数据同数组非常接近。其实,顺序表存储数据使用的就是数组。
使用顺序表存储数据之前,除了要申请足够大小的物理空间之外,为了方便后期使用表中的数据,顺序表还需要实时记录以下 2 项数据:

  • 顺序表申请的存储容量;
  • 顺序表的长度,也就是表中存储数据元素的个数;
typedef struct{
    ElemType *data;
    int length;
} SqlTable;

1、顺序表的初始化

//1、初始化顺序结构线性表
Status initTable(SqlTable *table){
    
    table->data = malloc(sizeof(ElemType) * MAX_SIZE);
    if (!table->data) {
        return ERROR;
    }
    table->length = 0;
    return OK;
}

2、顺序表插入数据

//2、往线性表中插入数据
Status insertTable(SqlTable *table, int index, ElemType data){
    
    //存储位置不对
    if (index < 1 || index > table->length + 1) {
        return ERROR;
    }
    //存储空间已满
    if (table -> length >= MAX_SIZE) {
        return ERROR;
    }
    //如果不在表尾插入数据,则需要在指定位置的后继元素都向后移动一位
    if (index < table->length + 1) {
        for (int i = table->length + 1; i > index; i--) {
            table->data[i] = table->data[i-1];
        }
    }
    table->data[index] = data;
    table->length ++;
    return OK;
}

3、顺序表遍历

//3、遍历线性表
void traverseTable(SqlTable table){
    
    printf("遍历表:");
    for (int i = 1; i <= table.length ; i ++) {
        printf("%d ",table.data[I]);
    }
    printf("\n");
}

4、顺序表取出值

//4 顺序表的取值
Status GetElem(SqlTable table, int index, ElemType *data){
    //存储位置不对
    if (index < 1 || index > table.length) {
        return ERROR;
    }
    *data = table.data[index];
    return OK;
}

5、顺序表删除数据

//5 顺序表删除
/*
 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
 操作结果: 删除L的第i个数据元素,L的长度减1
 */
Status deleteElem(SqlTable *table, int index){
    
    if (index < 1 || index > table->length) {
        return ERROR;
    }
    if (table->length == 0) {
        return ERROR;
    }
    for (int i = index; i < table->length; i++) {
        table->data[i] = table->data[i+1];
    }
    table->length --;
    return OK;
}

6、清空顺序表

//6 清空顺序表
/* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
void ClearTable(SqlTable *table){
    
    table->length = 0;
}

7、判断顺序表是否为空

//7 判断顺序表是否为空
/* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
Status tableEmpty(SqlTable table){
    
    if (table.length <= 0) {
        return S_TRUE;
    }
    else {
        return S_FALSE;
    }
}

8、获取顺序表长度

//8 获取顺序表长度ListEmpty元素个数 */
int tableLength(SqlTable table){
    return table.length;
}

9、顺序表查找元素并返回位置

//9 顺序表查找元素并返回位置
/* 初始条件:顺序线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int locateElem(SqlTable table, ElemType data){
    
    for (int i = 1; i <= table.length; i ++) {
        if (data == table.data[i]) {
            return I;
        }
    }
    return 0;
}

其它辅助代码

#include "stdlib.h"

#define MAX_SIZE 100
#define OK       1
#define ERROR    0
#define S_TRUE     1
#define S_FALSE    0

//ElemType 元素类型,这里设置为int
typedef int ElemType;
//状态码,返回操作是否成功,用OK或者ERROR
typedef int Status;

typedef struct{
    ElemType *data;
    int length;
} SqlTable;

int main(int argc, const char * argv[]) {
    printf("Hello, World!\n");
    
    //初始化
    SqlTable table;
    Status initStatus = initTable(&table);
    printf("线性表初始化是否成功 %d \n",initStatus);
    
    //插入数值
    for (int i = 1; i <= 10; i++) {
        insertTable(&table, i, i);
    }
    traverseTable(table);
    
    //取值
    ElemType data;
    GetElem(table, 6, &data);
    printf("顺序表第6个值是%d\n",data);
    
    //删除
    deleteElem(&table, 6);
    printf("删掉第6个值\n");
    traverseTable(table);
    
    //查找8在哪个位置
    int locate = locateElem(table, 8);
    printf("顺序表中8在第 %d 位\n",locate);
    
    //获取长度
    int length = tableLength(table);
    printf("获取长度是 %d\n",length);
    
    //清空
    ClearTable(&table);
    printf("清空表\n");
    
    //判断是否为空
    Status isEmpty = tableEmpty(table);
    printf("是否为空(1是0否):%d\n",isEmpty);
    
    traverseTable(table);
    
    printf("\n");
    return 0;
}

输出结果

输出结果.png

链表代码实现

与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。

如果只有一个值根本无法体现出各数据之间的逻辑关系。对此,链表的解决方案是,每个数据元素在存储时都配备一个指针,用于指向自己的直接后继元素。


链表示意图.png

于是,链表中每个数据的存储都由以下两部分组成:

  • 数据元素本身,其所在的区域称为数据域;
  • 指向直接后继元素的指针,所在的区域称为指针域;


    结点.png

代码实现如下:

typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

一个完整的链表需要由以下几部分构成:

  • 头指针:一个普通的指针,它的特点是永远指向链表第一个节点的位置。很明显,头指针用于指明链表的位置,便于后期找到链表并使用表中的数据;
  • 节点:链表中的节点又细分为头节点、首元节点和其他节点:
  • 头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。对于链表来说,头节点不是必须的,它的作用只是为了方便解决某些实际问题;
  • 首元节点:由于头节点(也就是空节点)的缘故,链表中称第一个存有数据的节点为首元节点。首元节点只是对链表中第一个存有数据节点的一个称谓,没有实际意义;
  • 其他节点:链表中其他的节点;

1、链表的初始化

//1、初始化单链表
Status InitList(LinkList *list){
    
    *list = malloc(sizeof(Node));
    if (*list == NULL) {
        return ERROR;
    }
    (*list)->next = NULL;
    return OK;
}

2、单链表插入数据

//2、单链表插入
Status ListInsert(LinkList *list, int index, ElemType data){
    
    LinkList p = *list;
    int i = 1;
    while (p && i < index) {
        p = p->next;
        I++;
    }
    if (i > index || !p) {
        return ERROR;
    }
    
    LinkList q = malloc(sizeof(Node));
    q->data = data;
    q->next = p->next;
    p->next = q;
    return OK;
}

3、链表遍历

//3、遍历链表
/* 初始条件:链表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
void ListTraverse(LinkList list){
    
    printf("遍历链表:");
    LinkList p = list->next;
    if (!p) {
        printf("空链表");
    }
    else {
        while (p) {
            printf("%d ",p->data);
            p = p->next;
        }
    }
    printf("\n");
}

4、链表取值

//4、单链表取值
Status GetElem(LinkList list, int index, ElemType *data){
    
    LinkList p = list;
    int i = 0;
    while (p && i < index) {
        p = p->next;
        I++;
    }
    if (i > index || !p) {
        return ERROR;
    }
    *data = p->data;
    return OK;
}

5、链表删除元素

//5、单链表删除元素
Status ListDelete(LinkList *list, int index, ElemType *data){
    
    LinkList p = *list;
    LinkList q = p->next;
    int i = 1;
    while (p && i < index) {
        p = q;
        q = p->next;
        I++;
    }
    if (i > index || !p || !q) {
        return ERROR;
    }
    p->next = q->next;
    *data = q->data;
    free(q);
    return OK;
}

6、清空链表

//6、清空链表
/* 初始条件:链表List已存在。操作结果:将List重置为空表 */
void ClearList(LinkList *list){
    
    LinkList p,q;
    p = (*list)->next;
    while (p) {
        q = p->next;
        free(p);
        p = q;
    }
    (*list)->next = NULL;
}

7、链表前插法

前插法.png
//7、单链表前插法
/* 随机产生n个元素值,建立带表头结点的单链线性表List(前插法)*/
void CreateListHead(LinkList *list, int n){
    
    *list = malloc(sizeof(Node));
    (*list)->next = NULL;
    
    LinkList p;
    for (int i = 1; i <= n; i ++) {
        p = malloc(sizeof(Node));
        p->data = I;
        p->next = (*list)->next;
        (*list)->next = p;
    }
}

8、链表后插法

后插法.png
//8、单链表后插法
/* 随机产生n个元素值,建立带表头结点的单链线性表List(后插法)*/
void CreateListTail(LinkList *list, int n){
    
    *list = malloc(sizeof(Node));
    LinkList p = *list;
    LinkList q;
    for (int i = 1; i <= n; i ++) {
        q = malloc(sizeof(Node));
        q->data = I;
        p->next = q;
        p = q;
    }
    p->next = NULL;
}

其它辅助代码

#include "stdlib.h"

#define OK       1
#define ERROR    0
#define S_TRUE   1
#define S_FALSE  0

typedef int ElemType;
typedef int Status;

typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

typedef struct Node * LinkList;

int main(int argc, const char * argv[]) {
    
    //初始化
    LinkList list;
    InitList(&list);
    printf("初始化链表\n");
    
    //插入数据
    for (int i = 1; i <= 10; i ++) {
        ListInsert(&list, 1, i);
    }
    printf("插入数据后\n");
    
    //遍历数据
    ListTraverse(list);
    
    //删除数据
    ElemType deleteData;
    ListDelete(&list, 6, &deleteData);
    printf("删除第6个元素:%d\n",deleteData);
    
    //遍历数据
    ListTraverse(list);
    
    //取出数据
    ElemType data;
    GetElem(list, 6, &data);
    printf("取出第6个数:%d\n",data);
    
    //清空链表
    ClearList(&list);
    printf("清空链表\n");
    //遍历数据
    ListTraverse(list);
    
    printf("前插法创建链表\n");
    CreateListHead(&list, 20);
    ListTraverse(list);
    
    printf("后插法创建链表\n");
    CreateListTail(&list, 20);
    ListTraverse(list);
    
    printf("\n");
    return 0;
}

输出结果

输出结果.png

如有不对的地方,请指正,谢谢您的阅读~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容