数据结构学习2--线性表的设计与实现(一)

导语:本博客将带大家入门线性表,重点讲解一个顺序存储的财富库,代码比较详细。


1 线性表的基本概念

线性表(List)是零个或多个数据元素的集合

线性表中的数据元素之间是有顺序的

线性表中的数据元素个数是有限的

线性表中的数据元素的类型必须相同


最通俗的解释:我们最常见的数组、链表就是线性表。


2 线性表的操作

创建线性表

销毁线性表

清空线性表

将元素插入线性表

将元素从线性表中删除

获取线性表中某个位置的元素

获取线性表的长度


3 线性表顺序存储的设计

#ifndef  _SEQLIST_H__ 
#define  _SEQLIST_H__

typedef void SeqList; <span style="white-space:pre">		思考3.1:为什么要定义成void的类型???
typedef void SeqListNode;

//创建线性表
SeqList* SeqList_Create(int capacity);

int  SeqList_Create01(SeqList **handle, int capacity);

//销毁线性表
void SeqList_Destroy(SeqList* list);

//线性表清空
void SeqList_Clear(SeqList* list);

//获取线性表的实际长度
int SeqList_Length(SeqList* list);

//获取线性表的能力,也就是最大能存多少个数据
int SeqList_Capacity(SeqList* list);

//线性表的插入
int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);<span style="white-space:pre">	思考3.2:接口这样设计有什么好处?为什么要用pos???

//获取元素
SeqListNode* SeqList_Get(SeqList* list, int pos);

//删除元素
SeqListNode* SeqList_Delete(SeqList* list, int pos);


#endif  //_SEQLIST_H__



解答问题:在测试案例中会详细说明

3.1 大家都知道,线性表的顺序存储,也就是数组,只有数据域,但数据的类型有各种各样,今天你传一个int 明天传一个结构体,这可怎么办?用void就避免了这个问题,这个在后面的测试案例中会给大家详细说明。数据结构的重点就是把数据抽象出来。
3.2 这个问题在实现的时候具体说明。


4 线性表顺序存储的实现


#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#include "seqlist.h"

typedef struct _tag_SeqList
{
	int capacity;
	int length;<span style="white-space:pre">		//当前长度
	unsigned int *node; 
}TSeqList;



SeqList* SeqList_Create(int capacity)
{
	TSeqList *ret = NULL;
	if (capacity < 0)
	{
		return NULL;
	}
	ret = (TSeqList *)malloc(sizeof(TSeqList) + sizeof(unsigned int )*capacity );<span style="white-space:pre">	//思考4.1 为什么要这样做?
	if (ret == NULL)
	{
		return NULL;
	}
	memset(ret, 0, sizeof(sizeof(TSeqList)) + sizeof(unsigned int )*capacity );
	ret->node = (unsigned int *)(ret +1); //ret向后跳sizeof(TSeqList)<span style="white-space:pre">		//思考4.2
	ret->capacity = capacity;
	ret->length = 0;
	return ret;
}

void SeqList_Destroy(SeqList* list)
{
	if (list == NULL)
	{
		return ;
	}
	free(list);
	return ;
}

//链表清零 。。。长度为零
void SeqList_Clear(SeqList* list)
{
	TSeqList *tList = NULL; <span style="white-space:pre">	</span>//<span style="color:#3366ff;">/*思考4.3  请注意处理方式,几乎每个函数都是这样,思考为什么?定义TSeqList这个结构体类型为什么?是否多余?*/

	if (list == NULL)
	{
		return ;
	}
	tList  = (TSeqList *)list;
	tList->length = 0;
	return ;
}

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

//线性表的容量和线性表长度是不一样的
int SeqList_Capacity(SeqList* list)
{
	TSeqList *tList = NULL; 
	tList = (TSeqList *)list;
	if (list == NULL)
	{
		return -1;
	}

	return tList->capacity;
}

int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)<span style="white-space:pre">		//思考4.4
{
	int i = 0;
	TSeqList *tList = NULL; 
	tList  = (TSeqList *)list;

	if (list == NULL || node == NULL) 
	{
		return -1;
	}

	//查看是不是满了
	if (tList->length >= tList->capacity)
	{
		return -2;
	}

	//位置错误判断
	if (pos<0 || pos>=tList->capacity)
	{
		return -3;
	}

	//优化的容错。。。
	if (pos >=tList->length)
	{
		pos = tList->length;
	}

	//插入算法
	//从pos位置处开始,把数组后面的元素依此后移
	for(i=tList->length; i>pos; i--)
	{
		//把前的元素往后移
		tList->node[i] = tList->node[i-1];
	}
	//循环跳出以后,pos正好是,要出入的位置
	tList->node[pos] = (unsigned int)node;
	tList->length ++;
	return 0;
}

SeqListNode* SeqList_Get(SeqList* list, int pos)
{

	SeqListNode *ret = NULL;
	TSeqList *tList = NULL;
	tList = (TSeqList *)list;
	if (list == NULL || pos<0 || pos>=tList->length)
	{
		return NULL;
	}
	ret = (SeqListNode*)tList->node[pos];
	return ret;
}

SeqListNode* SeqList_Delete(SeqList* list, int pos)<span style="white-space:pre">			
{
	int					i;
	TSeqList				*tList = NULL;
	SeqListNode			*ret = NULL; 
	tList = (TSeqList *)list;

	if (list==NULL || pos<0 || pos>=tList->length)
	{
		return NULL;
	}
	
	//赋给a3之前,要把a3元素缓存下来
	ret = (SeqListNode *)tList->node[pos];
	//删除算法
	for (i=pos+1; i<tList->length; i++)
	{
		tList->node[i-1] = tList->node[i];
	}
	tList->length --;

	return ret;
}


开始解答问题:

4.1&1.2:
<span style="white-space:pre">		//sizeof(TSeqList)给 头节点分配内存空间,sizeof(unsigned int )*capacity给数组分配空间
</pre><pre name="code" class="cpp"><span style="white-space:pre">		//ret向后跳sizeof(TSeqList)就很好理解了。
</pre><pre name="code" class="cpp"><span style="white-space:pre">		再思考:ret->node = (unsigned int *)malloc(sizeof(unsigned int )*capacity)    也就是分开分配内存空间可不可以?可以,但是destory的时候就要注意了,防止内存泄漏

 
  
 
   
 
4.3: 为什么要把seqlist* 转换为 TSeqList* 类型???这个跟3.2问题有关,在测试程序会跟大家详细说。这里简单说一下,在main函数中我们会定义各种各样的业务节点,seqlist*是void*类型的,也就是一个函数接口可以接受各种参数,而转换为 TSeqList这个我们规定好的类型,就可以对她进行操作了。这个还会详细讲解。

4.4:




5 测试程序

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

#include "seqlist.h"

typedef struct _Teacher
{
	char name[64];
	int age;
}Teacher;

int main()
{
	int i = 0;
	SeqList *list = NULL;

	Teacher t1, t2, t3;
	t1.age = 31;
	t2.age = 32;
	t3.age = 33;

	list =  SeqList_Create(10);

	SeqList_Insert(list, (SeqListNode*)&t1, 0);<span style="white-space:pre">			//其实传的只是地址,跟你什么类型没有关系
	SeqList_Insert(list, (SeqListNode*)&t2, 0);
	SeqList_Insert(list, (SeqListNode*)&t3, 0);

	//循环遍历
	for (i=0; i<SeqList_Length(list); i++)
	{
		Teacher *tmp = (Teacher *) SeqList_Get(list, i);
		if (tmp != NULL)
		{
			printf("tmp:age:%d ", tmp->age);
		}
	}

	//循环删除

	for (i=0; i<SeqList_Length(list); i++)
	{
		SeqList_Delete(list, 0);
	}
	SeqList_Destroy(list);

	system("pause");
}


6 优点和缺点

优点:

无需为线性表中的逻辑关系增加额外的空间

可以快速的获取表中合法位置的元素

缺点:

插入和删除操作需要移动大量元素

当线性表长度变化较大时难以确定存储空间的容量


7 结束

是否发现与书本上的不一样?这是企业级的财富裤,下一篇博客将给大家带来线性表的链性存储,也是一套企业级的财富裤,后面几节课将带大家用c++的形式实现一遍,c++就不会向财富裤一样具体,只是罗列重点代码,希望大家吸收一下这个财富裤。


 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值