【数据结构】之线性表(顺序存储结构)

数据结构与算法 专栏收录该内容
6 篇文章 0 订阅

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

    线性表是《数据结构》课程最开始的一章的内容,是在大学计算机专业必学的课程。我们刚接触线性表时,可能会被它的几种表的结构搞得晕头转向,它有两种基本存储结构:一种是顺序存储结构,另一种是链式存储结构。接下来,我们先来看看线性表的顺序存储结构吧。

    首先,线性表的顺序存储结构用图来表示,它是这样的:

    这张图很直观的说明了线性表的结构,图中元素都是一个挨着一个,表示线性表中(顺序存储结构)数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。顺序存储结构,指的是用一组地址连续的存储单元依次存储线性表的数据元素,称为线性表的顺序存储结构或顺序映像(sequential mapping)。它以“物理位置相邻”来表示线性表中数据元素间的逻辑关系,可随机存取表中任一元素。

    既然它的地址是相邻的,我们就可以通过地址计算公式来计算出某一个元素存在的地址。下面的计算的公式:

    之前考试的时候,在线性表那一章肯定会遇到这样的题目,也是非常简单、基础的一道题,套公式就可以很快的计算出来。题目是这样的:某线性表采用顺序存储结构,每个元素占 4 个存储单元,首地址为 100,则第 12 个元素的 存储地址为( )?

    这题直接套用上面的公式: 100 + (12 - 1)* 4 = 144,所以此题的答案是:144。好了,其他就不做多的介绍了,我们直接看代码吧。

一、数组实现方式

    线性表的数据结构,其中 MAX 表示线性表的数组元素个数,就是线性表的最大容量,length 表示线性表当前的长度。这里有一个必要条件,就是 length <= MAX

#define MAX 20
typedef int ElemType;
typedef struct
{
	ElemType data[MAX];	
	int length;
}SqList;

初始化线性表,即使线性表的长度为 0

/** 初始化线性表 **/
void InitList(SqList *L)
{
	L->length = 0;
}

 获取线性表中的元素值,传入的是线性表中元素的位置(下标)

/** 获取元素 **/
int Get(SqList L, ElemType *e, int i)
{
	if(L.length ==0 || i<1 || i>L.length)
		return -1;
	*e = L.data[i-1];
	return *e;
}

向表中插入数据

/** 插入元素 **/
int Insert(SqList *L, ElemType e, int i)
{
	int j;
	if(L->length == MAX)
		return -1;
	if(i<1 || i>L->length+1)
		return -1;

	if(i<= L->length)
	{	
		for(j=L->length; j>i-1; j--)
		{
			L->data[j] = L->data[j-1];
		}
	}

	L->data[i-1] = e;
	L->length++;
	return 1;
}

删除线性表中指定位置的元素

/** 删除指定下标的元素 **/
int Delete(SqList *L,int i)
{
	int j;
	if(L->length == 0)
		return -1;
	if(i<1 || i>L->length +1)
		return -1;

	if(i<L->length)
	{
		for(j=i; j<L->length; j++)
		{
			L->data[j-1] = L->data[j];
		}
	}
	L->length--;
	return 1;
}

获取当前表的长度

/** 线性表的长度 **/
int Length(SqList *L)
{
	return L->length;
}

清除线性表

/** 清除线性表 **/
void Clear(SqList *L)
{
	L->length = 0;
}

打印表中的元素

/** 打印线性表 **/
void PrintList(SqList *L)
{
	int i;
	for(i=0; i<L->length; i++)
	{
		printf("%d ",L->data[i]);
	}
	printf("\n");
}

最后,写一个 main() 方法来测试一下线性表的元素获取、插入、删除等操作。以下是完整的代码:

#include <stdio.h>
 
/**
 * 数据结构 - 线性表的顺序存储
 * 博客:https://vvcat.blog.csdn.net/
 **/
#define MAX 20
typedef int ElemType;
typedef struct
{
	ElemType data[MAX];	
	int length;
}SqList;
 
/** 初始化线性表 **/
void InitList(SqList *L)
{
	L->length = 0;
}
 
/** 获取元素 **/
int Get(SqList L, ElemType *e, int i)
{
	if(L.length ==0 || i<1 || i>L.length)
		return -1;
	*e = L.data[i-1];
	return *e;
}
 
/** 插入元素 **/
int Insert(SqList *L, ElemType e, int i)
{
	int j;
	if(L->length == MAX || i<1 || i>L->length+1)
		return -1;
 
	// 如果插入的位置不在末尾,要移动元素
	if(i<= L->length)
	{	
		for(j=L->length; j>i-1; j--)
		{
			L->data[j] = L->data[j-1];
		}
	}
 
	L->data[i-1] = e;
	L->length++;
	return 1;
}
 
/** 删除指定下标的元素 **/
int Delete(SqList *L,int i)
{
	int j;
	if(L->length == 0 || i<1 || i>L->length +1)
		return -1;
 
	// 如果删除位置不在末尾,移动元素
	if(i<L->length)
	{
		for(j=i; j<L->length; j++)
		{
			L->data[j-1] = L->data[j];
		}
	}
 
	L->length--;
	return 1;
}
 
/** 清除线性表 **/
void Clear(SqList *L)
{
	L->length = 0;
}
 
/** 线性表的长度 **/
int Length(SqList *L)
{
	return L->length;
}
 
/** 打印线性表 **/
void PrintList(SqList *L)
{
	int i;
	for(i=0; i<L->length; i++)
	{
		printf("%d ",L->data[i]);
	}
	printf("\n");
}
 
/**
 * 数据结构 - 线性表的顺序存储
 * 博客:https://vvcat.blog.csdn.net/
 * 博主:威威喵
 **/
void main()
{
	int i;
	int status = 0;
	int arr[10] = {3,7,11,6,5,14,36,9,4,19}; 
	SqList list;
	ElemType e;
 
	InitList(&list);
 
	/** 插入 10 个数据 **/
	for(i=1; i<=10; i++)
	{	
		Insert(&list, arr[i-1], i);
	}
	printf("线性表的顺序存储,长度 = %d \n", Length(&list));
	PrintList(&list);
	printf("\n");
 
	/** 在位置 5 插入一个数据 **/ 
	Insert(&list, 45, 5);
	printf("插入元素 45,长度为 = %d \n", Length(&list));
	PrintList(&list);
	printf("\n");
 
	/** 获取下标为 5 的元素的值 **/
	printf("获取的元素为 %d \n", Get(list, &e, 5));
	printf("\n");
 
	/** 删除位置 5 的数据 **/ 
	Delete(&list, 5);
	printf("删除元素 45,长度为 = %d \n", Length(&list));
	PrintList(&list);
	printf("\n");
 
	/** 清空数据 **/ 
	Clear(&list);
	printf("清除,长度为 = %d \n", Length(&list));
}

其运行结果为:

二、动态扩容

    线性表的另一种实现方法:动态扩容的线性表,因为上面的数组实现方式,其数组大小是固定的,如果数据添加满了话,再添加数据则会导致数组溢出,不容易进行动态扩容。以下是完整代码如下:

#include <stdio.h>
#include <stdlib.h>
 
/**
 * 数据结构 - 线性表的顺序存储
 * 博客:https://vvcat.blog.csdn.net/
 **/
#define LIST_SIZE 10
#define CAPACITY 5
typedef int ElemType;
typedef struct
{
	ElemType *data;	
	int length;
	int size;
}SqList;
 
/** 动态的初始化线性表 **/
int InitList(SqList *L)
{
	// 申请分配的内存空间大小
	L->data = (ElemType *)malloc(sizeof(ElemType) * LIST_SIZE);
	if(L->data == NULL)
		return -1;
	L->length = 0;
	L->size = LIST_SIZE;
	return 1;
}
 
/** 获取元素 **/
int Get(SqList L, ElemType *e, int i)
{
	if(L.length ==0 || i<1 || i>L.size)
		return -1;
	*e = L.data[i-1];
	return *e;
}
 
/** 插入元素 **/
int Insert(SqList *L, ElemType e, int i)
{
	ElemType *p = NULL;
	ElemType *q = NULL;

	if(i<1 || i>L->size)
		return -1;

	/** 如果内存空间不足,动态的扩容,每次扩容 CAPACITY 个大小 **/
	if(L->length == LIST_SIZE)
	{
		L->data = (ElemType *)realloc(L->data, sizeof(ElemType) * (CAPACITY + LIST_SIZE));
		L->size += CAPACITY;
		if(!L->data)
		{
			printf("扩容失败!");
			return -1;
		}
	}

	p = &L->data[i-1];//插入的位置
	for(q=&L->data[L->length-1]; q>=p; q--)
	{
		*(q+1) = *q;	
	}
	*p = e;
 
	L->length++;
	return 1;
}
 
/** 删除指定下标的元素 **/
int Delete(SqList *L, ElemType *e, int i)
{
	ElemType *p = NULL;
	ElemType *q = NULL;

	if(L->length == 0 || i<1 || i>L->size)
		return -1;
 
	p = &L->data[i-1];//删除的位置
	*e = *p;
	q = &L->data[L->length-1];
	while(p<=q)
	{
		*p = *(p+1);
		p++;
	}

	L->length--;
	return 1;
}
 
/** 清除线性表 **/
void Clear(SqList *L)
{
	while(!L->data)
	{
		free(L->data);
	}
	L->length = 0;
	L->size = 0;
}

/** 判断是否为空表 **/
int Empty(SqList *L)
{
	if(L->length == 0)
		return -1;
	else
		return 1;
}
 
/** 线性表的长度 **/
int Length(SqList *L)
{
	return L->length;
}

/** 线性表的大小 **/
int Size(SqList *L)
{
	return L->size;
}
 
/** 打印线性表 **/
void PrintList(SqList *L)
{
	int i;
	for(i=0; i<L->length; i++)
	{
		printf("%d ",L->data[i]);
	}
	printf("\n");
}
 
/**
 * 数据结构 - 线性表的顺序存储
 * 博客:https://vvcat.blog.csdn.net/
 * 博主:威威喵
 **/
void main()
{
	int i;
	int status = 0;
	int arr[10] = {3,7,11,6,5,14,36,9,4,19}; 
	SqList list;
	ElemType e;
 
	InitList(&list);
 
	/** 插入 10 个数据 **/
	for(i=1; i<=10; i++)
	{	
		Insert(&list, arr[i-1], i);
	}
	printf("线性表的顺序存储,Length = %d \n", Length(&list));
	printf("线性表的顺序存储,Size = %d \n", Size(&list));
	PrintList(&list);
	printf("\n");
 
	/** 在位置 5 插入一个数据 **/ 
	Insert(&list, 45, 5);
	Insert(&list, 95, 9);
	printf("插入元素 45、95,长度为 = %d \n", Length(&list));
	printf("线性表的顺序存储,Size = %d \n", Size(&list));
	PrintList(&list);
	printf("\n");
 
	/** 获取下标为 5 的元素的值 **/
	printf("获取的元素为 %d \n", Get(list, &e, 5));
	printf("\n");
 
	/** 删除位置 5 的数据 **/ 
	Delete(&list, &e, 5);
	printf("删除元素 45,长度为 = %d \n", Length(&list));
	printf("线性表的顺序存储,Size = %d \n", Size(&list));
	PrintList(&list);
	printf("\n");
 
	/** 清空数据 **/ 
	Clear(&list);
	printf("清除,长度为 = %d \n", Length(&list));
	printf("线性表的顺序存储,Size = %d \n", Size(&list));
}

以上代码的运行结果:

     线性表的动态扩容,就如上面的例子,刚开始添加了 10 个元素,线性表已经满了,然后每次扩容的个数是 5 个,所以再次插入数据时,会以每 5 个空间大小进行扩容操作。

三、学生信息表

    以下代码是我个人写的一个练手的线性表顺序存储结构的案例,主要是通过输入学生的用户信息(包括学号、姓名),然后使用线性表来保存起来。

学生信息数据结构与学生表的数据结构如下:

/** 学生信息的数据结构 **/
typedef struct
{
	char number[20]; // 学号
	char name[30]; // 姓名
}Student;

/** 学生表的数据结构 **/
typedef struct
{
	Student student[MAXSIZE];
	int length;
}SqList;

这里,我使用的是数组的方式,没用动态扩容,其中定义了 MAXSIZE 为 20 个,用于保存学生信息。完整代码如下:

#include <stdio.h>
 
#define MAXSIZE 20 // 定义存储学生信息的最大容量
#define OK 1;
#define ERROR -1;
typedef int Status;
 
/**
 * 数据结构 - 线性表的顺序存储
 * 博客:https://vvcat.blog.csdn.net/
 **/
/** 学生信息的数据结构 **/
typedef struct
{
	char number[20]; // 学号
	char name[30]; // 姓名
}Student;
 
/** 学生表的数据结构 **/
typedef struct
{
	Student student[MAXSIZE];
	int length;
}SqList;
 
/** 初始化 **/
void Init(SqList *L)
{
	L->length = 0;
}
 
/** 插入学生信息 **/
Status Insert(SqList *L, Student stu, int i)
{
	int k;
 
	if(L->length == MAXSIZE)
	{
		printf("插入失败,表溢出! \n");
		return ERROR;
	}
 
	if(i<1 || i>L->length+1)// 下标从 0 开始
	{
		printf("插入失败,下标异常! \n");
		return ERROR;
	}
 
	/** 如果插入位置不在表尾,移动表中元素 **/
	if(i <= L->length)
	{
		for(k=L->length; k>=i-1; k--)
		{
			L->student[k] = L->student[k-1];
		}
	}
 
	L->student[i-1] = stu;
	L->length++;
 
//	printf("插入的数据为:学号:%s 姓名:%s \n", stu.number, stu.name);
 
	printf("插入成功! \n");
	return OK;
}
 
/** 删除学生信息 **/
Status Delete(SqList *L, int i)
{
	int k;
 
	if(L->length == 0)
	{
		printf("表中空数据! \n");
		return ERROR;
	}
	if(i<1 || i>L->length+1)
	{
		printf("删除失败,下标异常! \n");
		return ERROR;
	}
 
	if(i <= L->length)
	{
		for(k=i-1; k<=L->length; k++)
		{
			L->student[k] = L->student[k+1];
		}
	}
 
	printf("删除成功! \n");
 
	L->length--;
	return OK;
}
 
/** 获取学生信息 **/
Student Get(SqList *L, int i)
{
	if(L->length == 0)
	{
		printf("空表! \n");
	}
	else if(i<1 || i>L->length)
	{
		printf("下标异常! \n");
	}
	else
	{
		return  L->student[i-1];
	}
}
 
int Length(SqList *L)
{
	return L->length;
}
 
/** 打印表 **/
void Print(SqList *L)
{
	int i;
	for(i=0; i<L->length; i++)
	{
		printf("学号:%s 姓名:%s \n", L->student[i].number, L->student[i].name);
	}
}
 
void main()
{
	int i=1;
	int n;
 
	SqList sqList;
	Student stu, stu2, stu3;
 
	printf("请输入学生的人数:");
	scanf("%d", &n);
 
	/** 初始化 **/
	Init(&sqList);
 
	while(i<=n)
	{
		printf("学号:");
		scanf("%s", stu.number);
		printf("姓名:");
		scanf("%s", stu.name);
		
		Insert(&sqList, stu, i);
		i++;
	}
	Print(&sqList);

	/** 插入数据 **/
	printf("插入学号:");
	scanf("%s", stu2.number);
	printf("插入姓名:");
	scanf("%s", stu2.name);
	Insert(&sqList, stu2, 2);
	Print(&sqList);
 
	/** 删除数据 **/
	Delete(&sqList, 2);
	Print(&sqList);
 
	/** 获取数据 **/
	stu3 = Get(&sqList, 1);
	printf("学号:%s \n", stu3.number);
	printf("姓名:%s \n", stu3.name);
}

当然了,你也可以改为动态扩容的方式。好了,下面是案例运行结果:

  • 5
    点赞
  • 1
    评论
  • 16
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值