数据结构C之顺序表


概念

线性表的顺序存储(物理)。它是用一组地址连续的存储单元依次存储线性表中的数据元素。它是一种随机存储的存储结构,通常用高级程序设计语言中的数组来描述线性表的顺序存储结构。

线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长。线性表L一般表示为L=(a1,a2,a3,…,an)。它是一种逻辑结构,表示元素之间一对一的相邻关系。

性质

  • 可以按序号随机存取(可以在O(1)时间内找到第i个元素)
  • 逻辑上相邻的元素在物理上也相邻
  • 表中元素的逻辑顺序与其物理顺序相同
  • 存储密度高(每一个结点只会存储数据元素,而不像链式表还要存放指针)
  • 拓展容量不方便
  • 插入删除元素不方便(均需要移动大量元素)

&是C++中的引用符号,用作函数形参是表明传递的是实参的一个引用(即实参的一个别名),这样在函数中对形参操作会影响到实参,通常用&来通过函数改变实参的值。如果没有&,则传递的只是实参的一个副本,在函数中对形参的操作不会影响到实参。正如例子中,对于L凡涉及到要通过函数修改的它时(如在表中插入元素ListInsert或删除元素ListDelete )L前都有&,不涉及修改变量时(如获取表中元素priorElem,GetElem)L前没有&。

类型定义

静态分配

数组的大小和空间已经固定,一旦空间占满,再加入新的数据就会溢出从而导致程序崩溃。

#define MaxSize 50 //定义线性表的最大长度
typedef struct {
    ElemType data[MaxSize];//静态数组存放的数据元素
    int length;//当前长度
}SqList;

动态分配

一旦存储空间占满,就会另外开辟出一块更大的空间用以替换原来的存储空间,从而达到扩充存储数组空间的目的。

#include <stdlib.h>//malloc free函数的头文件
#define InitSize 100
typedef struct {
    ElemType *data;//指示动态分配数组的指针。类似于空间首地址
    int Max,len;//数组的最大容量和当前有效存储单元的个数
}SqList1;
//初始动态分配语句
L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);

malloc()函数其实就在内存中找一片指定大小的空间,然后将这个空间的首地址范围给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc()函数中参数size的具体内容。我们这里malloc分配的内存空间在逻辑上连续的,而在物理上可以连续也可以不连续。对于我们程序员来说,我们关注的是逻辑上的连续,因为操作系统会帮我们安排内存分配,所以我们使用起来就可以当做是连续的。

我们需要将malloc函数返回的指针强制转型为自己所定义的数据元素类型指针。

关于引用

  • C语言中没有引用的存在,引用是在C++里面才有的神级操作。
  • 引用&就是给已经存在的变量新建了一个名字,如果程序对引用别名做了某些操作,其实就是对原目标进行了改动。
  • C语言中,被调用函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。

C语言中:

  1. &运算符:用于取一个对象的地址
  2. *运算符:作用于指针时表示访问指针所指向的对象
  3. 指针只能指向某种特定类型的对象,也就是说,每一个指针都必须指向某种特定的数据类型。

总结:

  • 变量a 本质上代表一个存储单元。CPU通过该存储单元的地址访问该存储单元中的数据。所以**a本来代表两个值:存储单元的地址和储单元中的数据。**于是就有了二异性。为了消除这种二义性,C语言规定a表示存储单元中的数据,&a表示存储单元的地址。

  • a存储单元中的数据可以是一个普通数值,也可以是另一个存储单元的地址,比如:a = &b; 语句就是将b的存储单元的地址存入a存储单元中。C语言规定 *a 代表a中存储的地址对应的存储单元中的数据,也就是访问 *a 就等于访问b,于是 *a提供了通过a访问b中的数据的手段。

  • a表示a对应的存储单元中的数据。

  • &a表示a对应的存储单元的地址。

  • *a 表示:首先,要求a对应的存储单元中的数据一定是另一个存储单元的地址。于是*a表示另一个存储单元中的数据。
    
  • 当a声明的类型是int*时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是一个整数数值;通过*a可以访问(读取或修改)这个数值。a == &*a 都是该存储单元的地址。
    当a声明的类型是int**时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是另外一个存储单元的地址,另外这个存储单元中存储的是一个整数数值;通过**a可以访问(读取或修改)这个数值。
    
int *p;
p = &c;//将c的地址赋值给指针变量p,我们称p为“指向c的指针”
int x;
x = *p;//将p指向的指针所对应的对象赋值给x,所以x就等于c的值。

基本操作

在C语言中,结构体的比较不能直接用“==”,而是需要依次对比各个分量来判断两个结构体是否相等。

#include <stdio.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0
#define FALSE1 2
#define OK 1
#define OVERFLOW -2
#define InitSize 10

typedef int ElemType;//数据类型为int型
typedef int Status;//函数类型,其值是函数结果状态码

typedef struct {
    ElemType *elem;//指示动态分配数组的指针
    int ListSize;//当前所能存储的最大容量
    int length;//当前有效存储单元的个数
}SeqList;

void DestroyList(SeqList *ptr);

/**
 * 创建顺序表
 */
 SeqList creatList(){
//    由系统分配list占用的内存
//    SeqList list;
//    return list;
    //自己动态分配的内存,需要在程序运行之前手动释放占用的内存空间。
    SeqList *list = (SeqList*) malloc(sizeof (SeqList));
    return *list;
 }
 /**
  * 初始化一个顺序表
  * @param L
  * @return -2(失败);1(成功)
  */
Status InitList(SeqList *L){
    //动态内存分配,返回的是指针型
    L->elem = (ElemType*) malloc(InitSize * sizeof (ElemType));
    //申请空间失败
    if(L->elem==NULL){
        return OVERFLOW;
    } else{
        L->length = 5;
        L->ListSize = InitSize;
        return OK;
    }
}
 /**
  * 增加动态数组的长度
  * @param L
  * @param len
  * @return -2(失败);1(成功)
  */
 Status IncreaseSize(SeqList *L,int len){
     int *p = L->elem;
    L->elem = (ElemType*) malloc(sizeof (ElemType)*(L->ListSize+len));
    if(L->elem==NULL){
        return OVERFLOW;
    }else{
        //将数据复制到新的区域
        for(int i=0;i<L->length;i++){
            L->elem[i]=p[i];
        }
        //顺序表最大长度增加len
        L->ListSize = L->ListSize + len;
        //释放原来的内存空间
        free(p);
        return OK;
    }
 }
 /**
  * 销毁顺序表
  * @param L
  */
 void DestroyList(SeqList *L){
     //重点释放顺序表的存储单元。
     //如果顺序表自身的内存也是动态分配的,需要手动释放。
     free(L->elem);//释放存储空间
     L->length = 0;
     L->ListSize = 0;
 }
 /**
  * 按位查找:在第i的位置的元素的值
  * @param L
  * @param i
  * @return
  * 时间复杂度:O(1)
  */
 ElemType GetElem(SeqList L,int i){
     return L.elem[i-1];
 }
 /**
  * 按值查找:查找第一个元素值为e的元素并返回其位序
  * @param L
  * @param e
  * @return
  * 时间复杂度:O(n)
  */
 int LocateElem(SeqList L,ElemType e){
     for(int i=0;i<L.length;i++){
         if(L.elem[i]==e){
             return i+1;
         }
     }
     return FALSE;
 }
 /**
  * 插入:在i位置插入元素e
  * @param L
  * @param i
  * @param e
  * @return
  */
 Status ListInsert(SeqList *L,int i,ElemType e){
     //判断i的范围是否有效
     if(i<1||i>L->length+1){
         return FALSE;
     }
     //当前存储空间已满不能插入
     if(L->length>=InitSize){
         return FALSE1;
     }
     //i位置之后的元素后移
     for(int j = L->length;j>=i;j--){
         L->elem[j]=L->elem[j-1];
     }
     //i处放入e
     L->elem[i-1]=e;
     L->length++;
     return TRUE;
 }
 /**
  * 删除第i个位置的元素,并用e返回删除元素的值
  * @param L
  * @param i
  * @param e
  * @return
  */
 Status ListDelete(SeqList *L,int i,ElemType *e){
     if(i<1||i>L->length){
         return FALSE;
     }
     *e = L->elem[i-1];
     for(int j=i;j<L->length;j++){
         L->elem[j-1]= L->elem[j];
     }
     L->length--;
     return TRUE;
 }

int main() {
//    SeqList L;
//    InitList(&L);
    SeqList L = creatList();
    printf("初始化结果:%d\n",InitList(&L));
    printf("增加动态数组结果:%d\n", IncreaseSize(&L,5));
    printf("插入元素结果:%d\n", ListInsert(&L,3,5));
    printf("按位查找结果:%d\n", GetElem(L,3));
    printf("按值查找结果:%d\n", LocateElem(L,5));
    int e = -1;
    printf("删除操作结果:%d\n", ListDelete(&L,3,&e));
    printf("删除元素的值为:%d\n", e);
    DestroyList(&L);
    return 0;
}


初始化结果:1
增加动态数组结果:1
插入元素结果:1
按位查找结果:5
按值查找结果:3
删除操作结果:1
删除元素的值为:5


今日推歌

—《月亮不会奔你而来》

我不摘月亮
我要它永远高悬天上皎洁流芳
他始终陌生
才允许诸多浪漫想象作为我理想
我要写诗给他
不要求送达
他可以接很多人的鲜花
不必无暇
他也能自在去漫步月球下
别去对号入座
谁爱意将真实灵魂蒸发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星回昭以烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值