【数据结构】顺序表(C语言)

顺序表

一、顺序表的基本概念

了解顺序表,首先要了解线性表的概念。

线性表:对于一组拥有N个数据的线性表:除了首元素a0无直接前驱,以及尾元素无直接后继,其它任何一个元素ai,有且仅有一个直接前驱a(i-1),有且只有一个直接后继。

满足这种数学关系的一组数据,其中的数据是一个挨着一个的,我们称之为一对一关系,是线性的。如果数据之间的关系不是一对一的,就是非线性的。

顺序表:顺序存储的线性表。

顺序表就是将数据存储到一片连续的内存中,在C语言环境下,它可以是具名的栈数组,也可以是匿名的堆数组。

顺序表的存储方式不仅仅要提供数据的存储空间,而且必须要能体现数据之间的逻辑的关系,比如,如果甲乙丙依次排队,乙前面是甲,后面是丙,那么存储位置必须按照这样的逻辑把甲存放在乙后面,把丙排在乙后面。

当采用顺序存储的方式存储数据时,唯一能够表达数据本身逻辑关系的就是存储位置。

通俗地说,顺序表的存储是连续的,即必须从首元素的位置开始按顺序往后存储,不能跳过中间的位置往后存储。

注意:上图只是为了让读者更好的理解顺序表而举的一个例子,并不是说其元素也要按照大小顺序有序存放。顺序表并没有要求存放的元素应该是有序的,它存放的元素可以是有序的,也可以是无序的,关于这一点我相信不少初学者都会有疑惑,所以笔者在这里做个特别说明。

二、顺序表的基本操作

  1. 顺序表的设计

为方便对顺序表进行管理,需要一个专门管理顺序表的结构体,该结构体中一般包含以下几个成员:

(1)顺序表总容量
(2)顺序表当前尾元素下标
(3)顺序表指针

以下是管理结构体示例代码:

typedef struct SequenceList{
    int cap;//顺序表容量,类似于数组的容量
    int last;//当前尾元素下标
    int *data;//存放顺序表数据,以整型数据为例 
}SqList;
SqList *SL = NULL;//定义一个顺序表指针
  1. 顺序表的初始化

建立一个空的、不包含元素的顺序表,需要初始化顺序表的容量,尾元素下标,申请分配存储数据的空间,由于一开始顺序表没有元素,尾元素下标置位-1;以下是初始化顺序表的示例代码,以传参的方式初始化顺序表。

//成功返回true,失败返回false
bool SqList_Init(SqList* &SL,int cap)
{
    if(SL = (SqList *)malloc(sizeof(SqList)) == NULL)
        return false;//malloc分配空间失败,返回false
    //内存分配成功
    SL->cap = cap;
    SL->last = -1;
    
    //cap为顺序表元素最大总数,cap * sizeof(int) 为需要分配的存储空间
    if(SL->data = (int *)malloc(cap * sizeof(int)) == NULL)
    {//SL->data申请分配空间失败,即顺序表初始化失败,需要释放SL,并返回false
        free(SL);
        SL = NULL;//防止SL成为野指针
        return false;
    }
    return true;  
}
  1. 判空

bool SqList_IsEmpty(SqList* SL)
{
    if(SL == NULL || (SL->last == -1))
        return true;
     return false;    
}
  1. 增删节点

在顺序表中增加一个数据,可以有多种方式,可以在原数组的头部、尾部或者按下标增删数据,也可以按值删除数据。以下分别为三种方式的增删数据示例代码,以及按值删除数据的代码。

//从头部增加
bool SqList_Head_Add(SqList* &SL,int data)
{
    if(SL->last == (SL->cap-1))//顺序表已满
        return false;
    if(SL->last == -1)//顺序表为空
    {
        SL->data[0] = data;
        SL->last++;
        return true;
    }
    //顺序表不为空
    int i;
    for(i = last;i>=0;i--)//将原有数据全部往后挪一位
    {
        SL->data[i+1] = SL->data[i];
    }
    SL->data[0] = data;
    SL->last++;
    return true;
}

//从头部删除
bool SqList_Head_Dele(SqList* &SL)
{
    if(SL->last == -1)//顺序表为空
        return false;
    
    //不为空
    //1.只有一个元素,直接让尾元素下标自减
    if(SL->last == 0)
    {
        SL->last--;
        return true;;
    }
    
    //2.两个及两个以上的元素
    int i;
    for(i = 0;i<SL->last;i++)//直接将原有数据全部往前挪一位
    {
        SL->data[i] = SL->data[i+1];
    }
    SL->last--;
    return true;
}

//从尾部增加
bool SqList_Tail_Add(SqList* &SL,int data)
{
    if(SL->last == (SL->cap-1))//顺序表已满
        return false;
    SL->last++;
    SL->data[last] = data;
    return true;
}

//从尾部删除
bool SqList_Tail_Dele(SqList* &SL)
{
    if(SL->last == -1)//顺序表为空
        return false;
    SL->last--;
    return true;
}

//从任意位置增加,参数 pos 为指定增加元素的位置
bool SqList_Add(SqList* &SL,int data,int pos)
{
    if(SL->last == (SL->cap-1))//顺序表已满
        return false;
    
    //插入位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
    if(pos > (SL->cap-1) || pos > (SL->last+1) || pos < 0)
        return false;
    
    //在尾元素位置以及尾元素之前的任意位置插入元素
    if(pos <= SL->last)
    {
        int i;
        for(i = SL->last;i >= pos;i--)//第pos个元素到尾元素,整体往后移一位
            SL->data[i+1] = SL->dara[i];
        SL->data[pos] = data;//元素后移之后,在第pos个元素的位置增加新元素
        SL->last++;//增加新元素后,尾元素下标加1
        return true;
    }
    
    //在尾元素之后插入新元素,即 pos = SL->last + 1
    if(pos > SL->last)
    {
        SL->data[pos] = data;//直接在第pos个位置增加新元素
        SL->last++;//尾元素加1
        return true;
    }        
}

//从任意位置删除,参数 pos 为删除指定元素的位置
bool SqList_Dele(SqList* &SL,int pos)
{
    if(SL->last == -1)//顺序表为空
        return false;
    
    //删除元素的位置,不能大于顺序表的容量,也不能破坏顺序表的线性关系
    if(pos > (SL->cap-1) || pos > SL->last || pos < 0)
        return false;
    
    //在尾元素之前的位置删除元素,不包括尾元素的位置
    if(pos < LS->last)
    {
        int i;
        for(i = pos;i < SL->last;i++)//从第pos位开始(包括第pos位),整体往前移动一位
            SL->data[i] = SL->data[i+1];
        SL->last--;//尾元素下标减1
        return true;
    }
    
    //删除的元素为尾元素
    if(pos == LS->last)
    {
        SL->last--;//直接让尾元素下标减1
        return true;
    }
}

//按值删除指定元素
bool SqList_Del_By_Val(SqList* &SL,int val)
{
    if(SL->last == -1)//顺序表为空
        return false;
    int i,cnt = 0,index = -1;
    for(i = 0;i <= SL->last;i++)
    {
        if(SL->data[i] == val)
        {
            index = i;//记录要删除的值的下标
            cnt++;//有可能存在多个相同的val,记录有多少个Val
        }
    }
    if(index == -1)//没有找到值为val的元素
        return false;
    
    SqList_Dele(SL,index);//按下标删除指定元素
    while(--cnt)//如果有多个相同值,递归调用自己删除值为Val的元素
    {
        SqList_Del_By_Val(SL,val);
    }
    return true;
}
  1. 销毁顺序表

顺序表不再需要使用的时候,应该释放其所占用的空间,这也叫顺序表的销毁。

void SqList_Destroy(SqList* &SL)
{
    if(SL == NULL)//顺序表为空
        return;

    //不为空
    //怎么初始化的,就要怎么释放
    free(SL->data);
    SL->data = NULL;
    
    free(SL);
    SL = NULL;

}

三、顺序表的优缺点

由于顺序存储的逻辑关系是用物理位置来表达的,因此增删数据需要成片的移动数据,这对数据节点的增删操作很不友好。当然,优缺点也有其优点,总结顺序表的特点如下:

优点:

(1)不需要多余的信息来记录数据之间的关系,存储密度高
(2)所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数据,可以跟数组一样直接访问第i个节点,如顺序表的第i个节点数据为SL->daata[i]。

缺点:

(1)插入、删除时需要保持数据的线性逻辑关系,需要成片地移动数据
(2)当数据节点较多时,需要移动一大片连续内存空间
(3)当频繁操作顺序表的数据节点时,内存的释放和分配不灵活

四、关于传引用

小伙伴们应该都注意到了,笔者在函数传参时使用了bool SqList_Init(SqList* &SL,int cap)这样形式传递指针参数,初学者可能对此感到困惑,我就不再这里做详细说明了,感兴趣的小伙伴可以到以下链接学习解惑:https://blog.csdn.net/weixin_46637351/article/details/126046567

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
顺序表是一种线性表,它的物理存储结构是连续的。在 C 语言中,可以用数组来实现顺序表。以下是一个简单的顺序表的 C 语言实现: ```c #include <stdio.h> #define MAXSIZE 100 // 定义顺序表的最大长度 typedef struct { int data[MAXSIZE]; // 存储数据元素的数组 int length; // 当前顺序表的长度 } SeqList; // 初始化顺序表 void InitList(SeqList *L) { L->length = 0; } // 获取顺序表中指定位置的元素 int GetElem(SeqList L, int i) { if (i < 1 || i > L.length) { printf("Error: index out of range.\n"); return -1; } return L.data[i-1]; } // 在顺序表中插入元素 void ListInsert(SeqList *L, int i, int e) { int j; if (L->length == MAXSIZE) { printf("Error: list is full.\n"); return; } if (i < 1 || i > L->length+1) { printf("Error: index out of range.\n"); return; } for (j = L->length; j >= i; j--) { L->data[j] = L->data[j-1]; } L->data[i-1] = e; L->length++; } // 删除顺序表中指定位置的元素 void ListDelete(SeqList *L, int i) { int j; if (i < 1 || i > L->length) { printf("Error: index out of range.\n"); return; } for (j = i; j < L->length; j++) { L->data[j-1] = L->data[j]; } L->length--; } // 输出顺序表中的元素 void PrintList(SeqList L) { int i; for (i = 0; i < L.length; i++) { printf("%d ", L.data[i]); } printf("\n"); } int main() { SeqList L; InitList(&L); ListInsert(&L, 1, 1); ListInsert(&L, 2, 2); ListInsert(&L, 3, 3); ListInsert(&L, 4, 4); ListInsert(&L, 5, 5); PrintList(L); ListDelete(&L, 3); PrintList(L); printf("%d\n", GetElem(L, 2)); return 0; } ``` 在这个例子中,我们定义了一个结构体 `SeqList`,其中包含一个数组 `data` 和一个表示顺序表长度的变量 `length`。然后我们定义了一些操作函数来对顺序表进行初始化、插入、删除、获取元素和输出顺序表的元素。在 `main()` 函数中,我们创建了一个顺序表 `L`,并对它进行了一些操作,最后输出了结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值