C语言---线性表、顺序表

线性表

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

前驱和后驱

顺序表

初始化

顺序表插入元素

顺序表删除元素

顺序表更改元素

示例代码


线性表

线性表,数据结构中最简单的一种存储结构,专门用于存储逻辑关系为"一对一"的数据。

线性表,基于数据在实际物理空间中的存储状态,又可细分为顺序表(顺序存储结构)和链表(链式存储结构)

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

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

如图 1 所示,这是一组具有“一对一”关系的数据,我们接下来采用线性表将其储存到物理空间中

首先,用“一根线儿”把它们按照顺序“串”起来,如图 2 所示:

图 2 中,左侧是“串”起来的数据,右侧是空闲的物理空间。把这“一串儿”数据放置到物理空间,我们可 以选择以下两种方式,如图 3 所示

图 3a) 是多数人想到的存储方式,而图 3b) 却少有人想到。我们知道,数据存储的成功与否,取决于是 否能将数据完整地复原成它本来的样子。如果把图 3a) 和图 3b) 线的一头扯起,你会发现数据的位置依旧没有发生改变(和图 1 一样)。因此可以认定,这两种存储方式都是正确的

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

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

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

图 3 中我们可以看出,线性表存储数据可细分为以下 2 种:

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

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

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

前驱和后驱

数据结构中,一组数据中的每个个体被称为“数据元素”(简称“元素”)。例如,图 1 显示的这组数据, 其中 1、2、3、4 和 5 都是这组数据中的一个元素。 另外,对于具有“一对一”逻辑关系的数据,我们一直在用“某一元素的左侧(前边)或右侧(后边)”这样不专业的词,其实线性表中有更准确的术语:

某一元素的左侧相邻元素称为“直接前驱”,位于此元素左侧的所有元素都统称为“前驱元素”;

某一元素的右侧相邻元素称为“直接后继”,位于此元素右侧的所有元素都统称为“后继元素”;

以图 1 数据中的元素 3 来说,它的直接前驱是 2 ,此元素的前驱元素有 2 个,分别是 1 和 2;同理,此 元素的直接后继是 4 ,后继元素也有 2 个,分别是 4 和 5。如图 4 所示:

顺序表

顺序表,全名顺序存储结构,是线性表的一种。线性表用于存储逻辑关系为“一对一”的数据,顺序表自然也不例外

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

例如,使用顺序表存储集合 {1,2,3,4,5} ,数据最终的存储状态如图 1 所示:

由此我们可以得出,将“具有 '一对一' 逻辑关系的数据按照次序连续存储到一整块物理空间上”的存储结 构就是顺序存储结构

通过观察图 1 中数据的存储状态,我们可以发现,顺序表存储数据同数组非常接近。其实,顺序表存储 数据使用的就是数组

初始化

使用顺序表存储数据之前,除了要申请足够大小的物理空间之外,为了方便后期使用表中的数据,顺序表还需要实时记录以下 2 项数据:

  1. 顺序表申请的存储容量;

  2. 顺序表的长度,也就是表中存储数据元素的个数;

提示:正常状态下,顺序表申请的存储容量要大于顺序表的长度

顺序表插入元素

向已有顺序表中插入数据元素,根据插入位置的不同,可分为以下 3 种情况:

  1. 插入到顺序表的表头;

  2. 在表的中间位置插入元素;

  3. 尾随顺序表中已有元素,作为顺序表中的最后一个元素;

虽然数据元素插入顺序表中的位置有所不同,但是都使用的是同一种方式去解决,即:通过遍历,找到数据元素要插入的位置,然后做如下两步工作:将要插入位置元素以及后续的元素整体向后移动一个位置;将元素放到腾出来的位置上;

例如,在 {1,2,3,4,5} 的第 3 个位置上插入元素 6,实现过程如下:

注意

在实现后续元素整体后移的过程,目标位置其实是有数据的,还是 3,只是下一步新插入元素时会把旧元素直接覆盖

顺序表删除元素

从顺序表中删除指定元素,实现起来非常简单,只需找到目标元素,并将其后续所有元素整体前移1个位置即可

后续元素整体前移一个位置,会直接将目标元素删除,可间接实现删除元素的目的

例如,从 {1,2,3,4,5} 中删除元素 3 的过程如图 4 所示:

顺序表更改元素

顺序表更改元素的实现过程是:

  1. 找到目标元素;

  2. 直接修改该元素的值;

示例代码

#include "Table.h" 
int main() 
{ 
    Table t = creatTable(5); 
    setTable(&t); 
    displayTable(t); 
    displayTable(*addNum(&t, 66, 2)); 
    displayTable(*delNum(&t, findWithNum(t, 66))); 
    changeNum(&t, 2, 99); 
    displayTable(t); 
    return 0; 
}
#ifndef _TABLE_ 
#define _TABLE_ 
/* 一 定义顺序表 */ 
// 结构体 Table:表类型 
typedef struct 
{ 
    int * head; // 1 指针 存储申请的内存的首地址 
    int length; // 2 长度 记录当前顺序表元素个数 
    int size; // 3 大小 记录当前顺序表最大长度 
}Table; 
/* 函数声明 */ 
// 创建顺序表 
// 参数: 初始长度 
Table creatTable(int SIZE); 
// 给所有元素初值 
// 参数: 表指针 
Table* setTable(Table* ptable); 
// 插入元素 
// 参数: 表指针 数值 位置(下标) 
Table* addNum(Table* ptable, int num, int pos); 
// 删除元素 按下标删 返回指针 
// 参数: 表指针 位置(下标) 
Table* delNum(Table* ptable, int pos); 
// 查找元素 按数值查 返回下标 
// 参数: 表 数值 
int findWithNum(Table table, int num); 
// 更改元素 按下标改 返回指针 
// 参数: 表指针 下标 值 
Table* changeNum(Table* ptable, int pos, int num); 
// 输出所有元素的值 
// 参数: 表 
void displayTable(Table table); 
#endif 
​
#include "Table.h" 
#include <stdio.h> 
#include <stdlib.h> 
/* 函数定义 */ 
// 创建顺序表
// 参数: 初始长度 
Table creatTable(int SIZE) 
{ 
    Table table; 
    // 创建一个空的顺序表,动态申请存储空间 
    table.head = (int*)malloc(sizeof(int)*SIZE); 
    // 判断: 如果申请失败 
    if (!table.head) 
    { 
        printf("申请失败!\n"); 
        exit(0); 
    }
    table.length = 0; // 长度初始化为 0 
    table.size = SIZE; // 大小初始化为 SIZE 
    return table; 
}
// 给所有元素初值 
// 参数: 表指针 
Table* setTable(Table* ptable) 
{ 
    for (size_t i = 0; i < ptable->size; i++) 
    { 
        // 给值 
        ptable->head[i] = i+10; 
        // 改长度 
        ptable->length++; 
    }
    return ptable; 
}
// 插入元素 
// 参数: 表指针 数值 位置(下标) 
Table* addNum(Table* ptable, int num, int pos) 
{ 
    // 判断插入操作是否存在问题 
    // 插入元素的位置不在插入范围(pos == ptable->length 刚好插在最后) 
    // pos: 0 --- ptable->length 
    if (pos > ptable->length || pos < 0) 
    { 
        printf("插入位置不对\n"); 
        return ptable; 
    }
    // 1 判断是否有存储空间 
    // 2 如果不够 那就扩容 
    if (ptable->length == ptable->size) 
    { 
        // 1 记录原来的内存 
        int* ptemp = ptable->head; 
        // 2 申请新的内存(2倍) 
        ptable->head = (int*)calloc(sizeof(int), ptable->size *= 2); 
        if (!ptable->head) 
        { 
            ptable->head = ptemp; 
            printf("申请内存失败 插入失败 未添加元素\n");
            return ptable; 
        }
        // 3 拷贝原来的内存 
        for (size_t n = 0; n < ptable->length; n++) 
        { 
            ptable->head[n] = ptemp[n]; 
        }
        // 4 释放原来的内存 
        free(ptemp); 
        // 5 置空ptemp 
        ptemp = NULL; 
    }
    // 插入 
    // 1 后移(先移动后面的) 
    for (int i = ptable->length; i >= pos; i--) 
    { 
        ptable->head[i + 1] = ptable->head[i]; 
    }
    // 2 插入 
    ptable->head[pos] = num; 
    // 3 改长度 
    ptable->length++; 
    return ptable; 
}
// 删除元素 按下标删 返回指针 
// 参数: 表指针 位置(下标) 
Table* delNum(Table* ptable, int pos) 
{ 
    if (pos >= ptable->length || pos < 0) 
    { 
    printf("删除位置不对\n"); 
    return ptable; 
    }
    // 删除 
    for (int i = pos; i < ptable->length; i++) 
    { 
        ptable->head[i] = ptable->head[i + 1]; 
    }
    ptable->length--; 
    // 是否缩减大小 (作业) 
    return ptable; 
}
// 查找元素 按数值查 返回下标 
// 参数: 表 数值 
int findWithNum(Table table, int num) 
{ 
    for (int i = 0; i < table.length; i++) 
    { 
        if (num == table.head[i]) 
        { 
            return i; 
        } 
    }
    return -1; 
}
// 更改元素 按下标改 返回指针 
// 参数: 表指针 下标 值 
Table* changeNum(Table* ptable, int pos, int num) 
{ 
    if (pos > ptable->length - 1 || pos < 0) 
    { 
        printf("修改失败\n"); 
        return ptable; 
    }
    ptable->head[pos] = num; 
    return ptable; 
}
    // 输出所有元素的值 
    // 参数: 表 
void displayTable(Table table) 
{ 
    printf("顺序表中存储的元素是:\n"); 
    for (size_t i = 0; i < table.length; i++) 
    { 
        printf("%d ", table.head[i]); 
    }
    printf("\ntable.size = %d , table.length = %d:\n\n", table.size, 
    table.length); 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值