数据结构是计算机专业必学的一门课程,如果说算法是一个程序的操作动作,那么数据结构就是一个程序的灵魂,它让一段程序在运行过程中,拥有足够强大的“内力”进行代码的实现。
数据结构的三要素分为逻辑结构,存储结构和运算。而本篇文章将要介绍的线性表,是数据结构入门内容,也正因为是一个入门内容,它的重要性也不言而喻。将数据结构中的线性表学习透彻,将在数据结构之后的学习中打好坚实的基础。
本篇文章将要介绍的,是线性表(逻辑结构)中的顺序结构(存储结构),是限线性表的基础之一,而在下列程序中,将要通过对顺序表进行初始化、判断空满、输出、追加、插入、删除、逆序、升序排序(冒泡排序)的分段过程,对整段代码进行分析解剖。
第一部分:创建一个结构体,使该结构体中有顺序表的首地址、长度和有效元素的个数。顺序表的首地址是一个指针,类似于数组中的第一个元素的地址,作为整个顺序表中的“导航”;分别定义长度和有效元素个数的目的,在于之后判断顺序表满或空的情况,便于灵活操作。
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<stdbool.h>
struct SqList
{
int * pBase; //顺序表的首地址
int length; //顺序表的长度
int cnt; //有效元素个数
};
第二部分:提前对所需操作的函数进行函数声明,对顺序表进行初始化、判断空满、输出、追加、插入、删除、逆序、升序排序(冒泡排序)的操作,也便于为具体代码的书写时理清思路。
void init_sl(struct SqList * pSl,int len); //初始化顺序表
bool is_empty(struct SqList * pSl); //判断是顺序表是否为空
bool is_full(struct SqList * pSl); //判断顺序表是否为满
void show_sl(struct SqList * pSl); //输出顺序表的元素
bool appand(struct SqList * pSl, int val); //在顺序表中追加元素
bool insert_sl(struct SqList * pSl, int pos, int val); //在顺序表中插入一个元素
bool delete_sl(struct SqList * pSl, int pos, int * pVal); //在顺序表中删除一个元素,并将删除元素存储下来
void inverse_sl(struct SqList * pSl); //将顺序表中的元素逆序
void sort_sl(struct SqList * pSl); //对顺序表中的元素进行排序
第三部分:将结构体变量实例化为sl,并进行各类操作。要注意的是,在形参列表中对顺序表的取地址操作符是必要的,原因是sl本身是一个顺序表,取地址类似于数组中取下标地址的操作。
int main(void)
{
struct SqList sl;
int val;
init_sl(&sl,7);
appand(&sl,1);
appand(&sl,3);
appand(&sl,2);
appand(&sl,5);
appand(&sl,4);
appand(&sl,7);
appand(&sl,6);
printf("初始顺序表为:\n");
show_sl(&sl);
if(delete_sl(&sl,4,&val))
{
printf("删除成功,删除的元素为:%d\n",val);
}
else
{
printf("删除失败!\n");
}
printf("删除元素后的顺序表为:\n");
show_sl(&sl);
insert_sl(&sl, 6, 100);
printf("插入一个元素后的顺序表为:\n");
show_sl(&sl);
sort_sl(&sl);
printf("升序排序后的顺序表为:\n");
show_sl(&sl);
inverse_sl(&sl);
printf("逆序后的顺序表为:\n");
show_sl(&sl);
return 0;
}
第四部分:各类操作的具体代码实现。
1.顺序表的初始化 void init_sl(struct SqList * pSl, int len);
首先对顺序表的首地址进行动态内存的分配,这时要使用c语言内置的malloc函数,其调用格式是(元素数据类型*)malloc(sizeof(元素数据类型)*定义的顺序表长度)。分配操作结束后,对分配情况进行判断,若顺序表首地址为空,则动态内存分配失败;若不为空,则对顺序表进行初始化,使其初始长度为定义长度,有效元素个数为0。
2.顺序表的空满判断 bool is_empty(struct SqList * pSl) ; bool is_full(struct SqList * pSl);
空判断:判断顺序表的有效元素个数是否为0,若为0则返回true,否则返回false。
满判断:判断顺序表中的有效元素个数是否与顺序表的长度相等,若相等则返回true,否则返回false。
3.输出 void show_sl(struct SqList * pSl);
首先判断顺序表是否为空,若为空则无法进行输出操作,否则,输出(类似于数组的输出操作)。
4.追加 bool appand_sl(struct SqList * pSl, int val);
函数的形参列表中需要多传入一个val,成为追加在顺序表中的元素。追加操作中,首先判断顺序表是否已满,若已满则无法进行后续操作,返回false,否则,使将要追加的元素赋值给,以有效元素为下标的顺序表元素,最后使有效元素个数加一,返回true。
5.插入 bool insert_sl(struct SqList *pSl, int pos, int val);
函数的形参列表中需要多传入插入元素的位置(需要插入的指定元素的位置之前)和插入的元素的值。判断顺序表是否为满,若为满则无法进行插入操作,返回false;判断插入的位置是否符合逻辑,即插入的位置不能在第0个元素之前(pos<1),也不能在最后一个有效元素的下一个位置之后(pos>pSl->cnt+1),返回false;否则,将插入元素之后的元素,从最后一个元素开始依此往后移动一个位置,使得将要插入的位置空出后,把待插入的元素插入移动出的空位中,返回true。
6.删除 bool delete_sl(struct SqList *pSl, int pos, int * pVal);
函数的形参列表中引入一个可以存储被删除元素的值的变量,便于在最终操作中查看所删除的值(该形参 int * pVal 可有可无,据需求而定)。与插入操作判断类似,要判断顺序表是否为空,若为空则返回false;判断删除元素的位置是否符合逻辑,无法删除“第零个元素”(pos<1),也无法删除有效元素之后的“元素”(pos>pSl->cnt),返回false;将待删除的元素存放在*pVal中,所谓的删除操作,实则使将待删除的元素的位置之后的元素,由近到远地依此向前移动一个位置,将待删除的元素覆盖,即为所谓删除,删除后有效元素个数减一,返回true。
7.逆序 void inverse_sl(struct SqList * pSl);
通过得到整个顺序表首部和尾部下标,展开逆序操作。首先通过while循环,当首部作为的下标一直小于尾部作为的下标时,进入循环,进行首尾元素交换的操作,并通过首部作为的下标加一,尾部作为的下标减一,达到代码健壮性的目的。
8.升序排序(冒泡排序) void sort_sl(struct SqList * pSl);
比较简单的算法内容,在此不作赘述分析。
void init_sl(struct SqList * pSl, int len)
{
pSl->pBase=(int *)malloc(sizeof(int)*len);
if(NULL==pSl->pBase)
{
printf("动态内存分配失败,程序终止!\n");
exit(-1);
}
pSl->length=len;
pSl->cnt=0;
return;
}
bool is_empty(struct SqList * pSl)
{
if(0==pSl->cnt)
{
return true;
}
else
{
return false;
}
}
bool is_full(struct SqList * pSl)
{
if(pSl->cnt==pSl->length)
{
return true;
}
else
{
return false;
}
}
void show_sl(struct SqList * pSl)
{
if(is_empty(pSl))
{
printf("顺序表为空!\n");
}
for(int i=0;i<pSl->cnt;++i)
{
printf("%d",pSl->pBase[i]);
printf("\n");
}
}
bool appand(struct SqList * pSl, int val)
{
if(is_full(pSl))
{
return false;
}
pSl->pBase[pSl->cnt]=val;
pSl->cnt++;
return true;
}
bool insert_sl(struct SqList * pSl, int pos, int val)
{
if(is_full(pSl))
{
return false;
}
if(pos<1||pos>pSl->cnt+1)
{
return false;
}
for(int i=pSl->cnt-1;i>=pos-1;--i)
{
pSl->pBase[i+1]=pSl->pBase[i];
}
pSl->pBase[pos-1]=val;
pSl->cnt++;
return true;
}
bool delete_sl(struct SqList * pSl, int pos, int * pVal)
{
if(pos<1||pos>pSl->cnt)
{
return false;
}
if(is_empty(pSl))
{
return false;
}
* pVal=pSl->pBase[pos-1];
for(int i=pos;i<pSl->cnt;++i)
{
pSl->pBase[i-1]=pSl->pBase[i];
}
pSl->cnt--;
return true;
}
void inverse_sl(struct SqList * pSl)
{
int start=0;
int end=pSl->cnt-1;
if(is_empty(pSl))
{
printf("顺序表为空!\n");
}
while(start<end)
{
int t=pSl->pBase[start];
pSl->pBase[start]=pSl->pBase[end];
pSl->pBase[end]=t;
start++;
end--;
}
}
void sort_sl(struct SqList * pSl)
{
if(is_empty(pSl))
{
printf("顺序表为空!\n");
}
for(int i=0;i<pSl->cnt-1;i++)
{
for(int j=0;j<pSl->cnt-i-1;j++)
{
if(pSl->pBase[j]>pSl->pBase[j+1])
{
int t=pSl->pBase[j];
pSl->pBase[j]=pSl->pBase[j+1];
pSl->pBase[j+1]=t;
}
}
}
return;
}
以上所有的操作细节和代码具体实现的思想以叙述完毕,希望对各位读者有所帮助。