《数据结构复习笔记》线性表

简介:

  • 理解顺序表的逻辑与存储原理,并能实现简单顺序表
  • 掌握单链表的逻辑与存储原理,并能实现单链表
  • 掌握双链表的逻辑与存储原理
  • 掌握循环链表的逻辑与存储原理

1,什么是线性表

    线性表是具有相同特性的数据元素组成的一个有限序列。线性表作为一种最简单的数据结构,有如下几个特征:

  • 线性表中有且只有一个开始结点(头结点),这个开始节点没有前驱结点。
  • 线性表中有且只有一个末尾结点(尾结点),这个末尾节点没有后继结点。
  • 除去开始结点与末尾结点,其他结点都有一个前驱结点和后继结点。

    线性表在存储结构上有顺序存储和链式存储两种方式,但不管哪种存储方式,它们的结构都有如下特点:

  1. 均匀性:虽然不同数据表的数据元素可以是各种各样的,但对于同一个线性表来说,数据元素必须具有相同的数据类型和长度。
  2. 有序性:各数据元素在线性表中的位置只取决于它们的序号,数据元素之间的相对位置是线性的,即存在唯一的"第一个"和"最后一个"数据元素。除了第一个和最后一个外,其他元素前面均只有一个数据元素(直接前驱),后面均只有一个数据元素(直接后继)。

    数据结构的目的是算法结合,实现各种操作。线性表是一种比较灵活的数据结构,它的长度可根据需要增删,它也可以进行插入,删除等操作。可对线性表进行的基本操作如下:

  • 创建:create()
  • 初始化:Init()
  • 获取长度:GetLength()
  • 判断表是否为空:IsEmpty()
  • 获取元素:Get()
  • 插入:Insert()
  • 删除:Delete()
  • 清空表:Clear()

2,线性表的顺序存储(顺序表)

2.1 顺序存储的原理

    顺序存储,就是在存储器中分配一段连续的存储空间,逻辑上相邻的数据元素,其物理存储地址也是相邻的。

2.2 顺序存储的实现

    1,创建顺序表

    在创建顺序表时,需要先创建一个头结点来存放顺序表的长度,大小和地址等信息,然后再创建顺序表,同时将顺序表的地址保存在头结点中。

实现实录如下:

  1. 定义一个struct来保存顺序表的信息
  2. 为头结点分配空间
  3. 为顺序表分配空间,将顺序表空间地址保存在头结点中
  4. 将头结点地址返回给调用者。

具体代码如下:

typedef struct_tag_SeqList  //头结点
{
	int capacity;  //表容量
	int length;    //表长度
	int *node;     //node[capacity],为指针数组
}TSeqList;

//创建顺序表
SeqList* SeqList_Create(int capacity)  //返回值为SeqList* 类型,即顺序表的地址
{
	int ret;
	TSeqList *temp = NULL;
	temp=(TSeqList*)malloc(sizeof(TSeqList)); //为头结点分配空间
	if(temp==NULL)
	{
		ret = 1;
		printf("func SeqList_Create() error:%d\n",ret);
		return NULL;
	}
	memset(temp,0,sizeof(TSeqList));
	temp->capacity=capacity;
	temp->lenght=0;
	temp->node=(int*)malloc(sizeof(void*)*capacity);  //分配一个指针数组
	if(temp->node==NULL)
	{
		ret = 2;
		printf("func SeqList_Create error %d\n",ret);
		return NULL;
	}
	return temp;		//将分配好的顺序表地址返回
}

    2,求顺序表容量

    在实现顺序表时,一般将顺序表信息保存在头结点中,因此求顺序表容量时,可以直接从头结点中获取。代码实现如下:

int SeqList_Capacity(SeqList* list)
{
	TSeqList *temp = NULL;
	if(list == NULL)
	{
		return;
	}
	temp=(TSeqList *)list;
	return temp->capacity;
}

    3,求顺序表长度

//求顺序表长度
int SeqList_Length(SeqList* list)
{
	TSeqList *temp = NULL;
	if(list == NULL)
	{
		return;
	}
	temp = (TSeqList*)list;
	return temp->length;
}

    4,插入元素

    在线性表中插入元素时,元素和元素后面的元素都要后移。在插入过程中,需要考虑一些异常情况如下:

  • 当顺序表已满时,表中的元素无法向后移动,需要作出特别处理(例如不插入,或者申请新开辟一块更大的空间来存储这些元素)
  • 当插入的位置在空闲区域时,需要作出相应处理。

实现代码如下:

//插入元素
int SeqList_Insert(SeqList* list,SeqListNode *node,int pos)
//参数为顺序表地址,要插入的元素地址,插入位置
{
	int i;
	TSeqList *temp = NULL;
	//先做健壮性检查
	if(list == NULL || node == NULL)
	{
		return -1;
	}
	temp=(TSeqList *)list;
	//如果顺序表已满
	if(temp->length>=temp->capacity)
	{
		return -2;
	}
	
	//容错
	if(pos>temp->length)
		pos=temp->length;
	
	for(i=temp->length;i>pos;i--)
	{
		temp->node[i]=temp->node[i-1];
	}
	temp->node[i]=(int)node;
	temp->length++;
	return 0;
}

    5,删除元素

    从顺序表中删除某一个元素,则将某一个元素删除后,需要将后面的元素依次向前移动来补齐空位。

实现代码如下:

//删除元素
SeqList* SeqList_Delete(SeqList* list,int pos)
{
	int i;
	//先做健壮性检查
	TSeqList* tlist = NULL;
	SeqListNode * temp = NULL;
	tlist = (TSeqList *)list;
	if(list==NULL||pos<0||pos>=tlist->capacity)
	{
		printf("SeqList_Delete() error%d\n");
		return NULL
	}
	temp=(SeqListNode*)tlist->node[pos]; //要删除的元素
	
	for(i=pos+1;i<tlist->length;i++)
	{
		tlist->node[i-1]=tlist->node[i];
	}
	tlist->length--;
	return temp;
}

    6,查找某个位置上的元素

//查找某个位置上的元素
SeqList* SeqList_Get(SeqList * list,int pos)
{
	int i;
	TSeqList* tlist = NULL;
	SeqListNode* temp = NULL;
	tlist = (TSeqList*)list;
	if(list=NULL||pos<0||pos>tlist->capacity)
	{
		return NULL;
	}
	temp = (SeqListNode*)tlist->node[pos];
	return temp;
}

    7,清空表

    清空顺序表是将表中的内容全部置为0

//清空顺序表
void SeqList_Clear(SeqList*list)
{
	TSeqList*temp = NULL;
	if(list==NULL)
	{
		return;
	}
	temp=(TSeqList*)list;
	temp->length=0;
	memset(temp->node,0,(temp->capacity * sizeof(void*)));
	return;
}

    8,销毁表

    销毁表是将表整个销毁,无法再使用

//销毁表
void SeqList_Destroy(SeqList* list)
{
	TSeqList* temp = NULL;
	if(list == NULL)
	{
		return;
	}
	temp = (TSeqList*)list;
	if(temp->node!=NULL)
	{
		free(temp->node);
	}
	free(temp);
	return;
}

2,线性表的链式存储(链表)

2.1 链式存储的原理

    链式存储中每个节点都包含两个部分:存储元素本身的数据域和存储结点地址的指针域。与顺序表相比,在插入,删除元素方面,链表的效率要比顺序表高许多。

2.2 链式存储的实现

    1,创建链表

 
 
struct Header  //头结点
{
	int length;  //纪录链表大小
	struct Node* next;	//指向第一个节点的指针
}

struct Node   //结点
{
	int data;		//记录结点数据
	struct Node* next;		//指向下一个节点的指针
}

typedef struct Node List;  //将Node重命名为List
typedef struct Header pHead; 	//将Header重命名为pHead

//链表初始化
pHead* createList()
{
	pHead* ph =(pHead*)malloc(sizeof(pHead));
	ph->length = 0;
	ph->next = NULL;
	return ph;
}

    2,获取链表大小

int Size(pHead* ph)
{
	if(ph==Null)
	{	
		printf("参数传入有误");
		return 0 ;
	}
	return ph->length;
}

    3,插入元素

int Insert(pHead* ph,int pos,int val)
{
	//先做健壮性检查
	if(ph==NULL||pos<0||pos>ph->length)
	{
		printf("参数传入有误");
		return 0;
	}
	//在向链表中插入这个元素时,先找到这个元素
	List* pval=(List*)malloc(sizeof(List)); //先分配一块内存来存储要插入的数据
	pval->data=val;
	List *pCur = ph->next;			//当前指针指向头结点的第一个节点
	if(pos==0)
	{
		ph->next=pval;
		pval->next=pCur;
	}
	else
	{
		for(int i=1;i<pos;i++)
		{
			pCur=pCur->next;
		}
		pval->next=pCur->next;
		pCur->next=pval;
	}
	ph->length++;
	return 1;
}

    4,查找某个元素

    查找链表中的某个元素,其效率没有顺序表高,因为不管查找的元素在哪个位置,都需要将前面的元素都全部遍历才能找到它。

List* find(pHead*ph,int val)
{
	//先做健壮性检查
	if(ph == NULL)
	{
		printf("输入的参数有误");
		return NULL;
	}
	List *pTmp = ph->next;
	do{
		if(pTmp->data==val)
		{
			return pTmp;
		}
		pTmp=pTmp->next;
	}while(pTmp->next!=NULL);
	printf("没有值为%d的元素",val);
	return NULL;
}

    5,删除元素

    在删除元素时,首先将被删除元素与上下节点之间的连接断开,然后将这两个上下节点重新连接,这样元素就从链表中成功删除了。

List* Delete(pHead*ph,int val)
{
	//先做健壮性检查
	if(ph == NULL)
	{
		printf("链表为空,删除失败");
		return NULL;
	}
	//找到val值所在的节点
	List* pval=find(ph,val);
	if(pval == NULL)
	{
		printf("没有值为%d的元素",val);
		return NULL;
	}
	
	//遍历链表找到要删除的节点,并找出其前驱及后继结点
	List *pRe = ph->next;  //当前节点
	List *pCur = ph->NULL;
	if(pRe->data == pval)
	{
		ph->next=pRe->next;
		ph->length--;
		return pRe;
	}
	else
	{
		for(int i=0;i<ph->length;i++)
		{
			pCur=pRe->next;
			if(pCur->data == pval)
			{
				pRe->next = pCur->next;
				ph->length--;
				return pCur;
			}
			pRe = pRe->next;
		}
	}
}

    6,销毁链表

void Destory(pHead *ph)
{
	List *pCur=ph->next;
	list *pTmp;
	if(ph==NULL)
		printf("参数传入有误");
	
	while(pCur->next!=NULL)
	{
		pTmp = pCur->next;
		free(pCur);
		pCur=pTmp;
	}
	ph->length=0;
	ph->next=NULL;
}

    7,遍历打印链表

void print(pHead *ph)
{
	if(ph==NULL){
		printf("打印不了,这个链表是空的");
	}
	List *pTmp=ph->next;
	while(pTmp !=NULL)
	{
		printf("%d",p->data);
		pTmp = pTmp->next;
	}
	printf("\n");
}

3,双链表

4,循环链表

    实现起来都差不多,就不写了...

猜你喜欢

转载自blog.csdn.net/qq_39263663/article/details/80695899
今日推荐