C语言数据结构——线性表的顺序存储结构

本文详细介绍了线性表的基本概念,包括定义、性质及其顺序存储结构的设计与实现。探讨了线性表中数据元素的有序性和有限性,以及线性表能够逐项和顺序存取的特点。同时,深入讲解了顺序存储结构的优缺点,以及如何通过头插法和尾插法在链表中插入元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线性表的基本概念

<1>定义

线性表是0个或者多个数据元素的集合
线性表中的数据元素之间是有序的
线性表中的数据元素个数是有限的
线性表中的数据元素类型必须相同

<2>性质

a0为线性表的第一个元素,只有一个后继
an为线性表的最后一个元素,只有一个前驱
除这二者外的其他元素ai,既有前驱,又有后继
线性表能够逐项和顺序存取

线性表的顺序存储结构

1.基本概念

线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素
在这里插入图片描述

2.设计与实现

在写程序之前,先说明一下单链表的头插法和尾插法
头插法:从一个空表开始,重复读入数据,生成新的结点,将读入的数据存放到新的结点的数据域中,然后将新结点插入到当前链表的表结点之后。
尾插法:从一个空表开始,重复读入数据,生成新的结点,将读入的数据存放到新的结点的数据域中,然后将新结点插入到当前链表的表结点之后。

首先先创造一个整体的框架,先自定一个header文件,然后把想要实现的方法写入,然后直接在主程序中导入此头文件
header.h

#ifndef CPRIMERPLUS_HEADER_H
#define CPRIMERPLUS_HEADER_H
typedef void SeqList;//声明一下线性表
typedef void SeqListNode;//声明组成结点的结构体

SeqList *seqList_Create(int capacity);//创建一张线性表

void SeqList_Destroy(SeqList *list);//销毁一张线性表

void SeqList_Clear(SeqList *list);//清空结点元素

int SeqList_Length(SeqList *list);//返回线性表的长度

int SeqList_Insert(SeqList *list,SeqListNode *node,int pos);//向SeqList链表pos的位置插入一个结点node

SeqListNode *list_Get(SeqList *list , int pos);//获取pos位置元素

SeqListNode *list_Delete(SeqList *list,int pos);//删除pos位置元素

#endif //CPRIMERPLUS_HEADER_H

头文件总体的思路写好后,下面再把主程序中的使用写出来

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "header.h"
typedef struct Teacher{
    int age;
    char *name;
}Teacher;
int main(){
    int ret = 0;
    SeqList *list = NULL;
    Teacher t1,t2,t3,t4,t5;
    t1.age = 30;
    t2.age = 31;
    t3.age = 32;
    t4.age = 33;
    t5.age = 34;

    list = seqList_Create(10);
    ret = SeqList_Insert(list, (SeqListNode *) &t1, 0);//头插法
    ret = SeqList_Insert(list, (SeqListNode *) &t2, 0);
    ret = SeqList_Insert(list, (SeqListNode *) &t3, 0);
    ret = SeqList_Insert(list, (SeqListNode *) &t4, 0);
    ret = SeqList_Insert(list, (SeqListNode *) &t5, 0);

    //遍历单链表,获取结点
    for (int i = 0; i < SeqList_Length(list); ++i) {
        Teacher *t = (Teacher *) list_Get(list, i);
        if (t == NULL) {
            return 0;
        }
        printf("t->age:%d", t->age);
    }

    //删除链表中的结点
    for (int j = 0; j < SeqList_Length(list); ++j) {
        list_Delete(list, 0);
    }
    system("pause");
}

然后最后再把细节写好,主要采用的还是头插法插入。
在同一个directory下创建具体方法的实现即可。
思路:
1.先把链表的结构写好,可以用struct,也可以用typedef struct起别名

typedef struct SeqList{
    int length;//链表实际长度
    int capacity;//分配最大的容量
    unsigned int *node;//所有的结点所形成的数组
    // (动态库内的结点,有多少分配多少,是动态的,所以要用指针指向内存空间内的结点)
}tSeqList;

2.在链表的create方法中,先让链表初始空间为Nul,然后为其动态分配存储空间,因为我们是动态链表,然后分配的类型和node的类型一致即可,分配完链表的空间后,为分配好的空间引入memset方法
void *memset(void *s,int c,size_t n)
总的作用:将已开辟内存空间s的首n个字节的值设为值c。

然后根据capacity分配node的空间,方法都是一样的

最后去给容量和结点赋初值,第一个函数也就写完了

SeqList *seqList_Create(int capacity){
    tSeqList *tmp = NULL;//既可以在栈分配内存空间,也可以在堆分配内存空间,但是让初始的链表为空
    tmp = (tSeqList *) malloc(sizeof(tSeqList));
    if(tmp ==NULL) {
        printf("func SeqList_Create error:%d",0);
        return NULL;
    }

    memset(tmp, 0, sizeof(tSeqList));//总的作用:将已开辟内存空间tmp的首sizeof(tSeqList)个字节的值设为值0


//    根据capacity分配结点空间
    tmp->node = (unsigned int *) malloc(sizeof(unsigned int *) * capacity);
    if (tmp->node == NULL) {
        printf("func SeqList_Create error:%d",0);
        return NULL;
    }
    tmp->capacity = capacity;
    tmp->length = 0;
    return tmp;

}

3.下面我们开始写destroy函数,因为我们之前开辟了两次内存空间(seqlist和node),所以也应该是先释放后开辟的空间

void SeqList_Destroy(SeqList *list){
    tSeqList *tList = NULL;
    if (list == NULL) {
        return;
    }
    tList = (tSeqList *) list;
    if (tList->node != NULL) {
        free(tList->node);
    }
    free(tList);
}

4.然后写clear清空链表函数,但是不是销毁free掉,而是让链表长度回归为0

//清空链表
void SeqList_Clear(SeqList *list){
    tSeqList *tList = NULL;
    if (list == NULL) {
        return;
    }
    tList = (tSeqList *) list;
    tList->length = 0;
}

5.然后求取链表长度

//求链表长度
int SeqList_Length(SeqList *list){
    tSeqList *tList = NULL;
    if (list == NULL) {
        return 0;
    }
    tList = (tSeqList *) list;
    return tList->length;
}

6.然后写元素插入。

思路:
1.判断线性表是否合法
2.判断插入位置是否合法
3.把最后一个元素插入进去之后,要把后面的往后挪动一个单位
4.将新元素插入
5.线性表长度+1

元素插入分为两个阶段,第一个阶段是元素的后移,第二个阶段是给空出的位置进行赋值
元素后移:一般的情况下都好说,要是后移之后超过capacity,要判断length==capacity?或者甚至超过,如果超过立刻return一个提示消息,这是第一种元素后移的bug。第二种元素后移的bug就是,pos的位置,也就是插入位置比length还大,那我们就要进行容错修正,这时就比较自由了,可以重新定义,或者写一个容错修正的算法。

int SeqList_Insert(SeqList *list,SeqListNode *node,int pos){
    int ret = 0;
    tSeqList *tList = NULL;
    if (list == NULL || node == NULL || pos < 0) {
        return ret;
    }
    tList = (tSeqList *) list;
    //判断插入元素是否满了
    if (tList->length == tList->capacity) {
        printf("满了");
    }
//    容错修正,pos>length
    if (pos > tList->length) {
        pos = tList->length;
    }
    //1.元素后移
    int i = 0;
    for (i = (*tList).length; i > pos; --i) {
        tList->node[i] = (*tList).node[i - 1];
    }
    //2.插入元素
    tList->node[i] = (unsigned int) node;
    tList->length++;
}

7.获取元素,获取元素就比插入元素稍微好写一点,要是也要对插入位置pos进行判断。

SeqListNode *list_Get(SeqList *list , int pos){
    int i = 0, ret = 0;
    tSeqList *tList = NULL;
    if (list == NULL || pos < 0) {
        ret = -1;
        return ret;
    }
    tList = (tSeqList *) list;
    return tList->node[pos];
}

8.删除元素
假设我们删除掉第n个结点,首先我们要把这个结点的位置缓存出来,然后把后面的结点依次赋值过来。

SeqListNode *list_Delete(SeqList *list,int pos){
    SeqListNode *ret = 0;
    tSeqList *tList = NULL;
    if (list == NULL || pos < 0) {
        printf("产生了错误");
        return NULL;
    }
    tList = (tSeqList *) list;
    ret = (SeqListNode *) tList->node[pos];
    for (int i = pos+1; i <tList->length; ++i) {
        tList->node[i - 1] = tList->node[i];
    }
    tList->length--;
    return ret;
}

3.优点和缺点

优点:无需为线性表中的逻辑关系增加额外的空间,可以快速的获取表中合法位置的元素
缺点:插入和删除需要移动大量的结点,当线性表长度较大的时候难以确定存储空间的容量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值