数据结构学习记录02——线性表之顺序存储结构

1.线性表的本质

  线性表是零个或多个数据元素的集合,其数据元素之间是有顺序的,个数是有限的,并且类型必须相同。
  书上的定义:线性表是由n(n≥0)个数据特性相同的元素构成的有限序列。
  线性表中,1)a0为第一个元素,只有一个后继;2)an为最后一个元素,只有一个前驱;3)其它元素ai,既有前驱,也有后继,线性表能够逐项访问和顺序存取。

2.线性表的相关操作

  假设我们要实现一个图书信息管理系统,每种图书仅包括三部分信息:ISBN、书名和价格,那么这个系统应具备以下功能:
  (1)查找:根据指定的ISBN或书名查找图书,并返回该书在表中的位置;
  (2)插入:插入一种新的图书及信息;
  (3)删除:删除一种图书及信息;
  (4)修改:根据指定的ISBN或书名,修改其价格;
  (5)排序:将书按价格由高到低进行排序;
  (6)计数:统计表中书的数量。
  实现上述功能,我们应首先根据图书表的特点将其抽象为一个线性表,将每本书作为线性表中的一个元素,然后可以采用适当的存储结构来表示该线性表,在此基础上设计完成相应的算法。

3.线性表的常用操作

  1.创建线性表;
  2.销毁线性表;
  3.清空线性表;
  4.将元素插入线性表;
  5.将元素从线性表中删除;
  6.获取线性表中某个元素的位置;
  7.获取线性表的长度。

  线性表的顺序存储结构,是指用一段地址连续的存储单元依次存储线性表的数据元素。在C语言中,我们可以使用动态分配的一维数组来实现顺序存储结构,其表示为:

typedef unsigned long long int TSeqListNode;			//在64位机中,该变量以为8字节 

typedef struct _tag_SeqList
{
	int capacity;						//线性表最大容量
	int length;							//当前线性表的长度
	TSeqListNode* node;					//动态地申请指针的空间 
} TSeqList;

  接着便是线性表各操作的讲解与代码实现。

3.1创建线性表

  在创建线性表之前,为了避免误操作,我们要对数据进行封装,其操作为:

typedef void SeqList;						//对数据进行封装,避免误操作 
typedef void SeqListNode;

  首先,我们先令线性表为空,若最大容量大于等于0,我们就通过 malloc来动态申请内存空间,接着我们就初始化容量,长度,数据指针的指向,最后返回创建的线性表。操作如下:

SeqList* SeqList_Create(int capacity)					
{
	TSeqList* ret = NULL;
	
	if (capacity >= 0)									//若容量大于0,则可以动态地申请内存空间 
	{
		ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode) * capacity);
	}
	
	if (ret != NULL)
	{
		ret -> capacity = capacity;
		ret -> length = 0;
		ret -> node = (TSeqListNode*)(ret + 1);			//指向头后面的位置,指向sizeof(TSeqListNode) * capacity 
	}
	
	return ret;
}

  在动态申请内存空间时,我们申请了线性表指针和数据指针,在初始化数据时,我们将指针指向数据,所以就加了1。

3.2销毁线性表

  销毁线性表的操作很简单,直接free就行,操作如下:

void SeqList_Destroy(SeqList* list)						
{
	free(list);
}

3.3清空线性表

  清空线性表就是让创建好的线性表回到初始状态,其操作为:

void SeqList_Clear(SeqList* list)						
{
	TSeqList* sList = (TSeqList*)list;
	
	if (sList != NULL)
	{
		sList -> length = 0;
	}
}

3.4获取线性表长度

  获取线性表的长度的方法就是直接返回创建的线性表的长度,其代码如下:

int SeqList_Length(SeqList* list)						
{
	TSeqList* sList = (TSeqList*)list;
	int ret = -1;
	
	if (sList != NULL)
	{
		ret = sList -> length;
	}
	
	return ret;
}

3.5获取线性表的容量

  获取线性表容量的方法和获取长度的方法一样,代码如下:

int SeqList_Capacity(SeqList* list)						
{
	TSeqList* sList = (TSeqList*)list;
	int ret = -1;
	
	if (sList != NULL)									//判断线性表是否合法 
	{
		ret = sList -> capacity;
	}
	
	return ret;
}

3.6插入元素

  线性表的插入元素操作,从道理上来说,和以前小学上体育课站队一样,当排好一列后,如果体育老师想让一个同学插进来,则会先叫原来位置及之后的同学向后挪动一个位置,然后让那个同学插进来,如图所示。
在这里插入图片描述
  当我们在操作线性表时,插入一个元素的步骤如下:
  (1)判断线性表是否合法;
  (2)判断插入位置是否合法;
  (3)把最后一个元素到插入位置的元素后移一个位置;
  (4)将新元素插入;
  (5)线性表长度加1.
  操作如下:

int SeqList_Insert(SeqList* list, SeqListNode* node, int pos) 
{
	TSeqList* sList = (TSeqList*)list;
	int ret = (sList != NULL);
	int i = 0;
	
	ret = ret && (sList -> length + 1 <= sList -> capacity);
	ret = ret && (pos >= 0);
	
	if (ret)
	{
		if (pos >= sList -> length)
		{
			pos = sList -> length;
		}
		
		for (i = sList -> length; i > pos; i--)
		{
			sList -> node[i] = sList -> node[i - 1];
		}
		
		sList -> node[i] = (TSeqListNode)node;
		sList -> length++;
	}
	
	return ret;
}

3.7获取元素

  获取一个元素的操作步骤为:
  (1)判断线性表是否合法;
  (2)判断位置是否合法;
  (3)直接通过数组下标的方式获取元素。

SeqListNode* SeqList_Get(SeqList* list, int pos)			
{
	TSeqList* sList = (TSeqList*)list;
	SeqListNode* ret = NULL;
	
	if ((sList != NULL) && (pos >= 0) && (pos < sList -> length))
	{
		ret = (SeqListNode*)(sList -> node[pos]);
	}
	
	return ret;
}

3.8删除元素

  接着插入元素的例子,如果体育老师又想把那个同学调到另一列去,那么就会先叫他出列,然后让其他同学先后挪动一个位置,如图所示。
在这里插入图片描述
  在操作线性表时,步骤为:
  (1)判断线性表是否合法;
  (2)判断删除位置是否合法;
  (3)将元素取出;
  (4)将删除位置后的元素分别向前移动一个位置;
  (5)线性表长度减1。

SeqListNode* SeqList_Delete(SeqList* list, int pos)				
{
	TSeqList* sList = (TSeqList*)list;
	SeqListNode* ret = SeqList_Get(list, pos);
	int i = 0;
	
	if (ret != NULL)											//返回值不为空,则线性表是合法的 
	{
		for (i = pos + 1; i < sList -> length; i++)
		{
			sList -> node[i - 1] = sList -> node[i];
		}
		sList -> length--;
	}
	
	return ret;
}

4.测试

  在写完了上述操作后,开始对其进行测试。

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

int main(int argc, char *argv[]) 
{
	SeqList* list = SeqList_Create(5);
	
	int i = 0;
	int j = 1;
	int k = 2;
	int x = 3;
	int y = 4;
	int z = 5;
	int index = 0;						//循环变量 
	
	SeqList_Insert(list, &i, 0);
	SeqList_Insert(list, &j, 0); 
	SeqList_Insert(list, &k, 0);
	SeqList_Insert(list, &x, 0);
	SeqList_Insert(list, &y, 0);
	SeqList_Insert(list, &z, 0);
	
	for (index = 0; index < SeqList_Length(list); index++)
	{
		int* p = (int*)SeqList_Get(list, index);
		printf("%d\n", *p);
	}
	
	printf("\n");
	
	while(SeqList_Length(list) > 0)
	{
		int* p = (int*)SeqList_Delete(list, 0);
		printf("%d\n", *p);
	}
	
	SeqList_Destroy(list);
	
	return 0;
}

  我们在创建了线性表后,先插入5个元素到线性表中,然后在观察每个位置的元素是什么,接着通过删除操作,观察每一次删除的元素是什么,结果如下:
在这里插入图片描述

5.分析与总结

  写完顺序存储结构后,我们来分析一下算法的效率:
  (1)创建:O(1)
  (2)销毁:O(1)
  (3)清空:O(1)
  (4)获取长度:O(1)
  (5)获取容量:O(1)
  (6)插入元素:最好情况下为O(1),最坏情况下为O(n)
  (7)获取元素:O(1)
  (8)删除元素:最好情况下为O(1),最坏情况下为O(n)

  由此,我们可以总结出顺序存储结构的优点为:
  1.无需为线性表中的逻辑关系增加额外的空间;
  2.可以快速地获取表中合法位置的元素;
  缺点为:
  1.插入和删除操作需要移动大量的元素;
  2.当线性表长度变化较大时难以确定存储空间的容量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值