[数据结构]-使用C语言实现顺序表操作
顺序表的抽象数据类型
ADT 线性表(List)
Data
ElemType *elem; //声明基地址
int length; //线性表中的长度
int maxsize; //线性表最大存储单元
Operation
int initSql(sqList *L); //初始化顺序表
int insertElem(sqList *L, int item, ElemType e); //插入元素
int getElem(sqList *L,int item,ElemType *e); //获取元素
int searchElem(sqList *L,ElemType e); //查找元素
int deleteElem(sqList *L,int item); //删除元素 c
void showList(sqList *L); //显示所有数据
int popBack(sqList *L); //尾删
int popHead(sqList *L); //头删
int pushBack(sqList *L,ElemType e); //尾插
int pushHead(sqList *L,ElemType e); //头插
endADT
顺序表的C语言代码的实现
引入头文件
//====================================================================
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
//====================================================================
#include<malloc.h> // 引入malloc函数
malloc函数
功能:声明一段连续的内存空间
返回值:
成功 内存空间的指针
失败 返回 null
定义常量
//====================================================================
// 定义返回值的状态
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
# define MAXSIZE 100 // 设置线性表最大长度c
typedef int ElemType;
//====================================================================
一开始看到这个ElemType的时候不太理解他是做什么的,毕竟是跟着课本学的,上面既没有给出定义方式也没有给出解释。后来参考各种资料发现了ElemType的具体情况。typedef是用来定义别名的声明,就是将int类型定义一个别名叫做ElemType。我个人感觉有点像是把int类型重映射到ElemType 。那这个声明有什么作用能,一般顺序表中的元素是同一类型的,如果我们在接下来的代码块中直接使用int类型的话,如果哪天我想换成char类型,那岂不是得把所有int类型的声明都要更改成char类型。所以为了方便,在这里找一个代理ElemType,来代替int行使权力。如果我想将数据改成char类型的只需要把定义由typedef int ElemType; 改成 typedef char ElemType; 就欧耶了。
声明一个顺序表的结构体
//====================================================================
typedef struct {
ElemType *elem; //声明基地址
int length; //线性表中的长度
int maxsize; //线性表最大存储单元
}sqList;
//====================================================================
struct ==> 声明一个结构体;typedef ==> 定义一个别名,别名叫做sqList
初始化方法
//====================================================================
int initSql(sqList *L); //初始化顺序表
int insertElem(sqList *L, int item, ElemType e); //插入元素
int getElem(sqList *L,int item,ElemType *e); //获取元素
int searchElem(sqList *L,ElemType e); //查找元素
int deleteElem(sqList *L,int item); //删除元素 c
void showList(sqList *L); //显示所有数据
int popBack(sqList *L); //尾删
int popHead(sqList *L); //头删
int pushBack(sqList *L,ElemType e); //尾插
int pushHead(sqList *L,ElemType e); //头插
//====================================================================
初始化一个顺序表
//====================================================================
int initSql(sqList *L){
/**************************************
* SqList L: 顺序表对象
***************************************/
L->elem = (ElemType *)malloc(MAXSIZE*sizeof(ElemType));
if(!L->elem){
return OVERFLOW; //-2
}else{
L->length = 0;
L->maxsize = MAXSIZE;
return OK; //1
}
}
//====================================================================
*是取地址的意思 将形参L的地址作为初始化方法的参数传进来(L是一个结构体);OK OVERFLOW 是上面定义的全局变量 ; malloc 在上面引入了#include<malloc.h> 功能:分配内存空间,并返回一个指向这个内存空间的指针 ==> L.elem
将一个值插入顺序表
//====================================================================
int insertElem(sqList *L,int item, ElemType e){
/**************************************
* SqList L: 顺序表对象
* int item:要插入的值的item
* ElemType e:将值赋给哪个变量 在这里因为int重定向给ElemType ==> int *a
* 为什么不用*?
* 因为他是一个元素值,我不想让顺序表的对应位的指针指向这个变量,我想让顺序表的对应的值就是这个值
***************************************/
int i;
// item 在否?
if(item<1||item>L->length+1){
return ERROR;
}
// length够否?
if(L->length >= L->maxsize){
return OVERFLOW;
}
//item后的全体数据 后撤1位
for(i=L->length;i>item-1;i--){
/*
* 数组从0开始,所以要-1;
* 从最后一位向前依次挪,挪到item-1位,不可以从item位-1开始向后挪;
* length-1其实默认可顺序表还有剩余空间,前面已经把没空给排出来
*/
L->elem[i] = L->elem[i-1];
}
// 挪完以后留出空了,这时候就可以放数据了c
L->elem[item-1] = e;
L->length++;
return OK;
}
//====================================================================
判断item在否?的条件, 1<= item <= L.length+1 因为我可以往第一位插也可以往最后一位插
从一个顺序表中取值
//====================================================================
int getElem(sqList *L,int item,ElemType *e){
/**************************************
* SqList L: 顺序表对象
* int item:要获取的值的item
* ElemType *e:将值赋给哪个变量 在这里因为int重定向给ElemType ==> int *a
* 为什么用*? 这就又用到哪个映射的名词,相当于e的指针指向了item位的元素的地址(取值应该都可以这么用)
***************************************/
// item 在否?
if(item<1||item>L->length) {
return ERROR; //0
}else{
*e = L->elem[item-1];
}
}
//====================================================================
先判断这个item有没有超出这个表的范围,可以的话将他赋值给e
从一个顺序表中查找数据
//====================================================================
int searchElem(sqList *L,ElemType e){
int i;
// 遍历
for(i=0;i<L->length;i++){
if(L->elem[i] == e){
return i;
}
}
return FALSE;
}
//====================================================================
从一个顺序表中删除元素
//====================================================================
int deleteElem(sqList *L,int item){
int j;
// item在否?
if(item<1||item>L->length){
return ERROR;
}
//全体前移,这时候就得从item-1+1的位置开始向前移动。
for(j=item;j<=L->length+1;j++){
L->elem[j-1] = L->elem[j];
L->length = L->length-1;
}
return OK;
}
//====================================================================
展示顺序表的所有数据
//====================================================================
void showList(sqList *L){
int i;
for( i=0; i<L->length; i++){
printf("%d",L->elem[i]);
}
printf("\n");
}
//====================================================================
特殊玩法
头插法插入数据
从数据表的头部进行插入数据
//====================================================================
int pushHead(sqList *L,ElemType e){
int i;
if(L->length>=L->maxsize){
return OVERFLOW;
}else{
for(i=L->length;i>0;i--){
L->elem[i] = L->elem[i-1];
}
L->elem[0] = e;
L->length++;
}
}
//====================================================================
尾插法插入数据
从数据表的尾部进行插入数据
//====================================================================
int pushBack(sqList *L,ElemType e){
if(L->length>=L->maxsize){
return OVERFLOW;
}else{
L->elem[L->length]=e;
L->length++;
}
}
//====================================================================
头删除
从数据表的头部进行删除数据
//====================================================================
int popHead(sqList *L){
int i;
if(L->length==0){
return ERROR;
}else{
for(i=0;i<L->length-1;i++){
L->elem[i] = L->elem[i+1];
}
L->length--;
}
}
//====================================================================
尾删除
从数据表的尾部进行删除数据
//====================================================================
int popBack(sqList *L){
if(L->length==0){
return ERROR;
}else{
L->length--;
}
}
//====================================================================
主方法
void main(){
ElemType returnN;
int i;
sqList L;
if(initSql(&L)==1) {
printf("初始化完成\n");
printf("初始化(initSql)elem=%d\n",L.elem);
printf("初始化(initSql)length=%d\n",L.length);
}
if(insertElem(&L,1,1)==1){
printf("插入完成\n");
printf("插入数据(insertElem)elem=%d\n",L.elem);
printf("插入数据(insertElem)length=%d\n",L.length);
}else{
printf("插入失败");
}
i = searchElem(&L,1);
printf("查询数据(searchElem)1的位置item=%d\n",i);
getElem(&L,1,&returnN);
printf("获取数据(getElem)第1位的数据e=%d\n",returnN);
deleteElem(&L,1);
printf("删除数据(deleteElem)此时length = %d\n",L.length);
}
总结
在写算法的时候,也有许多需要注意的地方。众所周知测试的小Bug在实际中可能会被无限放大。
比如:
1、每次要添加数据的时候,需要判断顺序表有没有满员,想指定插入的地方存不存在。增加之前,该元素要插入的位置后面的数据整体后移。
2、每次要删除数据的时候,需要判断顺序表有没有元素可以删,要指定删除的地方有没有元素。删除之后,该元素的后面所有元素整体前移。
这么看的话本来很难懂得东西就毕竟好理解了,因为是第一次使用C语言的指针和结构体,所以中间也踩了不少坑。
比如:
1、使用指针调用方法的时候,不能用 "."只能使用 ->(指向结构体的指针)。
2、定义方法
int initSql(sqList *L); *用来声明地址
3、使用方法
initSql(sqList &L); &用来取地址