一文解决数据结构之线性表的顺序表达(C语言实现)

线性表

数据结构可以分为两类:线性结构非线性结构
线性结构: 是一个有序数据元素的集合。如:线性表,栈,队列,双队列,数组,串;
非线性结构: 是一个结点前后可能有多个前驱和后继。如:图,二维数组,多维数组,广义表,树(二叉树等)。
所以,这里这个线性表是线性结构,也就是说线性表中的元素只有一个前驱,后一个后继 (咳咳,什么废话) ,同样,这样是有两种数据的储存结构:顺序存储链式存储。即:前驱和后继的连接上有物理上的连续与分离的区别,所以,线性表也就分为:顺序表链表

下面我们先简绍一下线性表里面的一些小知识点

前驱元素

若A元素在B元素的前面,则称A为B的前驱元素。

后继元素

若B元素在A元素的后面,则称B为A的后继元素。

线性表的特征

线性表的特征是数据元素之间具有一种“一对一”的逻辑关系。

  1. 第一个数据元素没有前驱,该数据元素被称为头结点。
  2. 最后一个数据元素没有后继,该数据元素被称为尾结点。
  3. 除了第一个和最后一个数据元素外,其他数据元素有且仅有一个前驱和一个后继。

下面我们来分别实现这两种线性表

顺序表

顺序表即线性表的顺序表示指的就是用一组地址连续的存储单元依次存储线性表的数据元素。
在这里插入图片描述
由于存储的物理位置是连续的,当得知第一个元素的物理位置时,我们就可以推测出每一个元素的位置。如同上图,d是表中每个元素需占的存储单元。
所以,这个数据元素的位置存储地址便是符合等差数列的通项公式:
L O C ( a i + 1 ) = L O C ( a i ) + d LOC(a_{i+1}) = LOC(a_{i})+d LOC(ai+1)=LOC(ai)+d
L O C ( a i ) = L O C ( a 1 ) + ( i − 1 ) ∗ d LOC(a_{i}) = LOC(a_{1})+(i - 1)*d LOC(ai)=LOC(a1)+(i1)d

顺序表的实现

实现顺序表,我们先要从实现表的各项功能开始,下面就设计该顺序表的ADT(抽象数据类型),这里的顺序表就使用数组来进行实现储存定义元素。

ADT设计

定义数据元素
#include<stdio.h>
//这里定义一下ID数据的最大长度,20吧
#define MAXSIZE 20

//下面这里自定义元素类型elemType
//这个顺序表就用来存储学生信息,包含学生ID与姓名
typedef struct {
    
    
	int ID;
	char name;
}elemType;
顺序表的数组实现
//这里的连续空间实现就是用数组
//定义表的入口
typedef struct {
    
    
	ElemType data[MAXSIZE];
	//定义记录表的当前长度
	int length;
}sqList;

下面就开始编写实现顺序表的各类功能

清除表中元素
//清楚表中元素
//因为这里对表的初始化是要对表内元素的进行改变
//这里是要把地址传入,所以是传地址,而不是传形参
int clearList(sqList *sl){
    
    
	//将顺序表的长度重置为0
	sl -> length = 0;
	return 0;
}
初始化表

上面已经实现了对顺序表的清除,这里清除顺序表只是将顺序表的长度设置为零,而这里的初始化表的操作是先将顺序表给清空,然后再给每一元素进行赋予初值。

//给函数初始化
//也是传地址
void initList(sqList *sl){
    
    
	//清除表:将顺序表的长度重置为0
	clearList(sl);
	//遍历顺序表,并将元素的值赋为初始值(ID:0;name:'^')
	for(i = 0; i < MAXSIZE; i++){
    
    
	sl -> data[i].ID = 0;
	sl -> data[i].name = '^';
	}
	return 0;
}
判断表是否为空
//判断表是否为空
//这里只需要查询中数据的值,所以只需传值调用
int listEmpty(sqList sl){
    
    
   if(0 == sl.length){
    
    
   	printf("List is empty! \n");
   	//为空的话返回值为1
   	return 1;
   }
   //不为空返回0
   return 0;
}
返回表的长度
int listLength(sqList sl){
    
    
	printf("List Length is %d ! \n", sl.length);
	return sl.length;
}
遍历表中元素
void listTraveral(sqList sl){
    
    
	printf("List length is:%d \n",sl.length);
    for(i=0; i<sl.length; i++){
    
    
        printf("data[%d].ID = %d, data[%d].name = %c\n",i,sl.data[i].ID,i,sl.data[i].name);
    }
}
向顺序表中插入元素

由于顺序表的物理空间是连续的,所以我们假如要插入元素,则我们需要把这个表的最后一个元素先向后移动一个存储元素单元,这样就会腾出一个空位,就可以把待插入元素,放置进去。
在这里插入图片描述

这里当我们进行插入元素时,我们先要进行安全性检查与位置检测

//插入元素
//因为这里我们要改变原始的表,所以这里我们要传值
int listInsert(sqList *sl,int pos,elemTpye e){
    
    
    //安全性检测
    //当表满了后,避免插入造成栈溢出
    if(sl -> length == MAXSIZE){
    
    
        printf("List is Full!\n");
        return 2;
    }
    //位置检测
    //插入是否符合顺序表的物理连续(最后一个元素是a[3]但是我们插入数值到a[5],将中间的a[4]给空缺,这是不允许的)
    //范围应该在1<= pos <= sl->length+1
    if(pos<1 || pos > (sl->length+1){
    
    
        printf("Bad Position !\n");
        return 3;
    }
    //插入操作
    //注意,这里因为数组是物理存储空间连续的,所以我们应该先移动最后一个元素
    for(int i = sl->length;i == pos; i--){
    
    
        sl->data[i] = sl->data[i-1];
    }
    sl->data[pos-1] = e;
    
    sl->length++;
    
    printf("Insert OK!\n");
}
删除元素

删除元素与插入元素类似,只是前移与后置的区别,另外当所有的元素向前移动后,最后一个内存空间就会空缺出来,所以我们也要把表的长度进行删减。
在这里插入图片描述

//删除元素
//同样,这里需要运用传值,以改变原表中值
int listDelete(sqList *sl, int pos, elemType *e){
    
    
    //安全性检测
    if(sl->length == 0){
    
    
        printf("List is empty!\n");
        return 4;
    }
    //位置检测
	if(pos<1 || pos>(sl->length)){
    
    
        printf("Bad position!\n");
        return 5;
    }
    
    //删除操作?
    *e = sl->data[pos-1];
    
    int i = 0;
    for(i=pos; i<=sl->length; i++){
    
    
        sl->data[i-1] = sl->data[i];
    }
    
    sl->length--;
    printf("Delete OK!\n");
    
    return 0;
}
索引元素
elemType listGet(sqList sl; int pos){
    
    
    elemType e;
    
    return sl.data[pos-1];
}
定位列表中的元素
int listLocate(sqList sl, elemType e){
    
    
    int i = 0;
    for(i=0;i<sl.length;i++){
    
    
        if(sl.data[i].ID == e.ID && sl.data[i].name == e.name){
    
    
            printf(" Locate : %d\n",i+1);
			return i+1;
        }
    }    
    return -1;
}
通过顺序导出前驱元素

由于这里的d是2,(存储数据元素的最小单位)所以我们这里就是位置值pos-2

elemType listPriorPos(sqList sl, int pos){
    
    
	// Safety test
	if(pos<=1 || pos >sl.length)
		return ;
	else
		return sl.data[pos - 2];
	
}
通过元素导出前驱元素

和上面类似,只是导出的时候代为元素

elemType listPriorElem(sqList sl, elemType e){
    
    
	int pos = listLocate(sl,e);
	
	if(pos<=1)
		return ;
	else
		return sl.data[pos - 2];	
}
通过顺序导出后继元素

这个和上述的操作是类似的

elemType listNextPos(sqList sl, int pos){
    
    
	if(pos < 1 || pos >= sl.length)
		return ;
	else
		return sl.data[pos];	
}
通过元素导出后继元素
elemType listNextElem(sqList sl, elemType e){
    
    
	int pos = listLocate(sl,e);
	
	if(pos < 1 || pos >= sl.length)
		return ;
	else
		return sl.data[pos]  ;
}

上面就是顺序表的全部内容了,之后我们还会编写线性表的链式表达。

猜你喜欢

转载自blog.csdn.net/qq_50459047/article/details/120620449