数据结构中的顺序表的实现


前言


本期就进入到我们数据结构模块的分享了,数据结构其实是一门凌驾于各种语言上层的一门学科,所以数据结构完全可以脱离语言来学习,但不得不说的是各种语言确实赋予了数据结构新的各种实体实现形式,因此数据结构是我们灵活使用各种语言的一种手段,它们之间相辅相成;数据结构提高了语言处理业务的效率,语言使得数据结构灵活应用于各类开发;

一、初识数据结构

程序其实是数据结构和算法的总和;
数据结构:程序操作的数据对象的结构特性;
数据结构中我们需要掌握的主要内容有以下几个:
在这里插入图片描述
在学习每个模块之前必须把大体章程做到心中有数,这个也是我们为学习数据结构为自己建造的新的数据结构,哈哈哈;总之,就是讲求效率;

二、顺序表的实现(类型一)

这里必须强调一下:学习数据结构一定必须注意数据结构体的设置定义,一个好的数据结构体的定义,往往能够极大的提高书写数据结构以及使用数据结构;

1.顺序表的头文件

首先来看一下顺序表中的结构体的定义:
本结构体定义的优点是能够直接得到当前存放元素的个数以及顺序表的总长度;

#ifndef __SEQLIST_H__
#define __SEQLIST_H__

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

/* 存储数据对象类型 */
typedef int DataType;

/* 顺序表表头类型 */
typedef struct list
{
	DataType *pData;		//存储对象的空间首地址
	int tlen;				//最多存放元素的个数
	int clen;				//当前存放元素的个数
}SeqList;

extern SeqList *CreatSeqList(int MaxLen);		
extern int IsFullSeqList(SeqList *pTmpList);
extern int IsEmptySeqList(SeqList *pTmpList);
extern int InsertTailList(SeqList *pTmpList, DataType TmpData);
extern int ShowSeqList(SeqList *pTmpList);
extern int InsertPosData(SeqList *pTmpList, DataType TmpData, int Pos);
extern int DeleteSeqList(SeqList *pTmpList, int ptmp);
extern int UpdateSeqList(SeqList *pTmpList, DataType OldData, DataType NewData);
extern DataType *SearchSeqList(SeqList *pTmpList, DataType TmpData);
extern int ClearSeqList(SeqList *pTmpList);
extern int DestroySeqList(SeqList **pTmpList);

#endif

2.顺序表的实现

每个函数的功能均有标注,代码如下:

#include "Seqlist.h"

/* 创建一个顺序表 */
SeqList *CreatSeqList(int MaxLen)
{
	SeqList *pTmpList = NULL;

	pTmpList = malloc(sizeof(SeqList));	//为表头创建空间
	if (NULL == pTmpList)
	{
		perror("fail to pTmpList");
		return NULL;
	}

	pTmpList->tlen = MaxLen;
	pTmpList->clen = 0;
	pTmpList->pData = malloc(sizeof(DataType) * MaxLen);	//创建顺序表的数据存储空间
	if (NULL == pTmpList->pData)
	{
		perror("fail to malloc");
		return NULL;
	}

	return pTmpList;
}
/* 判断顺序表是否为满 */
int IsFullSeqList(SeqList *pTmpList)
{
	return pTmpList->tlen == pTmpList->clen ? 1 : 0;
}

/* 判断顺序表是否为空 */
int IsEmptySeqList(SeqList *pTmpList)
{
	return pTmpList->tlen == 0 ? 1 : 0;
}
/* 在表中插入一个数据 */
int InsertTailList(SeqList *pTmpList, DataType TmpData)
{
	if (IsFullSeqList(pTmpList))
	{
		return -1;
	}

	pTmpList->pData[pTmpList->clen] = TmpData;
	pTmpList->clen++;

	return 0;
}
/* 打印列表中的所有元素 */
int ShowSeqList(SeqList *pTmpList)
{
	int i = 0;
	for (i = 0;i < pTmpList->clen;++i)
	{
		printf("%d ", pTmpList->pData[i]);
	}
	putchar('\n');
	return 0;
}
/* 按照位置插入数据 */
int InsertPosData(SeqList *pTmpList, DataType TmpData, int Pos)
{
	int i = 0;

	if (IsFullSeqList(pTmpList) || (Pos < 0) || Pos > pTmpList->clen)
	{
		return -1;
	}
	
	for (i = pTmpList->clen;i > Pos;--i)
	{
		pTmpList->pData[i] = pTmpList->pData[i - 1];
	}
	
	pTmpList->pData[i] = TmpData;
	++pTmpList->clen;
	return 0;
}
/* 删除元素(只删除第一个),多次删除循环调用即可 */
int DeleteSeqList(SeqList *pTmpList, int ptmp)
{
	int i = 0;
	int j = 0;

	if ( IsEmptySeqList(pTmpList))
	{
		return -1;
	}

	for (j = 0;j < pTmpList->clen; j++)
	{
		if (pTmpList->pData[j] == ptmp)
		{
			for (i = j;i < pTmpList->clen - 1;++i)
			{
				pTmpList->pData[i] = pTmpList->pData[i + 1];
			}
			pTmpList->clen--;
			break;					//i--为删除所有的元素
		}
	}
	return 0;
}
/* 修改元素 */
int UpdateSeqList(SeqList *pTmpList, DataType OldData, DataType NewData)
{
	int i = 0;

	for (i = 0;i < pTmpList->clen; ++i)
	{
		if (pTmpList->pData[i] == OldData)
		{
			pTmpList->pData[i] = NewData;
		}
	}

	return 0;
}
/* 查找元素 */
DataType *SearchSeqList(SeqList *pTmpList, DataType TmpData)
{
//	int a[pTmpList->clen] = {0};
	int i = 0;
//	int j = 0;

	for (i = 0;i < pTmpList->clen;++i)
	{
		if (pTmpList->pData[i] == TmpData)
		{
			return &pTmpList->pData[i];
		}
	}
	return NULL;
}

/* 清零顺序表 */
int ClearSeqList(SeqList *pTmpList)
{
	pTmpList->clen = 0;
	return 0;
}
/* 销毁顺序表 */
int DestroySeqList(SeqList **pTmpList)
{
	free((*pTmpList)->pData);
	free(*pTmpList);
	*pTmpList = NULL;
	return 0;
}

三、顺序表的实现(类型二)

1 顺序表头文件

相比于上一种的类型定义形式,那么这个的缺点是必须手动宏定义顺序表的总长度,并且结构体只有存放顺序表最后一个元素个数(顺序表中元素总个数)的数据;其实两者从本质上来讲都是一样的,都是使用数组开辟一段连续的空间地址进行顺序存储;
头文件内容如下:

#ifndef __SQLIST_H__
#define __SQLIST_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef int data_t;

#define N	128

typedef struct sqlist_t {
	
	data_t data[N];
	int last;						//顺序表中实际最后一个元素的下标

}sqlist, *sqlink;

/*
 * 与上面的自定义结构体等价
typedef struct sqlist_t sqlist;		//自定义结构体
typedef struct sqlist_t *sqlink;	//自定义指针
*/

/* 创建一个顺序表(返回值为链表指针类型) */
extern sqlink list_create(void);

/* 清零线性表(只清零不释放空间) */
extern int list_clear(sqlink L);

/* 删除线性表(释放空间) */
extern int list_delete(sqlink L);

/* 判断线性表是否为空 */
extern int list_empty(sqlink L);

/* 获取线性表的长度 */
extern int list_length(sqlink L);

/* 得到val在线性表中的位置 */
extern int list_locate(sqlink L, data_t val);

/* 在线性表的pos处插入一个数据 */
extern int list_insert(sqlink L, data_t val, int pos);

/* 列出线性表中的所有元素 */
extern int list_show(sqlink L);

/* 将两个线性表进行合并(取并集) */
extern int list_merge(sqlink L1, sqlink L2);

/* 消除线性表中所有重复的元素 */
extern int list_purge(sqlink L);

/* 删除线性表中pos处的成员 */
extern int list_DeleteMember(sqlink L, int pos);

#endif

2 顺序表的实现

那么具体来看下如何实现:

#include "sqlist.h"

sqlink list_create(void)
{
	sqlink L;
	int i = 0;

	L = (sqlink)malloc(sizeof(sqlist));	//为线性表申请空间
	if (NULL == L)
	{
		perror("fail to malloc");
		return L;
	}

	memset(L, 0, sizeof(sqlist));		//清零线性表空间

	L->last = -1;						//将最后一个位置先置为-1

	return L;							//返回线性表地址
}

int list_clear(sqlink L)
{
	if (L == NULL)
	{
		return -1;
	}

	memset(L, 0, sizeof(sqlist));		//清零线性表空间

	L->last = -1;						//将最后一个位置先置为-1

	return 0;
}

int list_delete(sqlink L)
{
	if (NULL == L)
		return -1;

	free(L);
	L = NULL;
	return 0;
}

int list_empty(sqlink L)
{
	if (L->last == -1)
	{
		return 1;
	}

	return 0;
}

int list_length(sqlink L)
{
	if (L == NULL)
	{
		return -1;
	}
	return L->last + 1;
}

int list_locate(sqlink L, data_t val)
{
	int i = 0;

	for(i = 0; i <= L->last; ++i)
	{
		if (L->data[i] == val)
		{
			return i;
		}
	}

	return -1;
}

int list_insert(sqlink L, data_t val, int pos)
{
	int i = 0;

	if (N - 1 == L->last)				//判断线性表是否为满
	{
		printf("线性表满!\n");
		return 0;
	}

	if (pos < 0 || pos > L->last + 1)		//判断位置是否有效(此处按原理来讲pos > L->last表示可以在最后一个位置插入)
	{
		printf("插入位置无效!\n");
		return -1;
	}
	
	for (i = L->last; i >= pos; --i)	//将pos和pos之后的元素向后平移一个位置
	{
		L->data[i + 1] = L->data[i];
	}

	L->data[pos] = val;					//插入元素

	L->last += 1;						//最后下标+1

	return 0;
}

int list_show(sqlink L)
{
	int i = 0;


	if (L == NULL)
	{
		return -1;
	}
	if (-1 == L->last)
	{
		printf("线性表是空的!\n");
	}

	for (i = 0; i <= L->last; ++i)
	{
		printf("%d ", L->data[i]);
	}
	putchar('\n');

	return 0;
}

int list_DeleteMember(sqlink L, int pos)
{
	int i = 0;

	if (list_empty(L))
	{
		printf("线性表是空的!\n");
		return -1;
	}
	if (pos < 0 || pos > L->last)		//判断位置是否有效
	{
		printf("删除位置无效!\n");
		return -1;
	}

	for (i = pos; i < L->last; ++i)			//每个元素向前移动
	{
		L->data[i] = L->data[i + 1];
	}

	L->last--;

	return 0;
}

int list_merge(sqlink L1, sqlink L2)
{
	int i = 0;
	int j = 0;
	
	if (list_empty(L2))
	{
		printf("线性表是空的\n");
		return -1;
	}

	for(i = 0; i <= L2->last; ++i)
	{
		for(j = 0; j <= L1->last; ++j)
		{
			if (L1->data[i] == L2->data[j])
			{
				break;
			}
		}

		if (j == L1->last + 1)
		{
			L1->data[L1->last + 1] = L2->data[i];
			L1->last++;
		}
	}

	return 0;
}

int list_purge(sqlink L)
{
	int i = 0;
	int j = 0;

	if (list_empty(L))
	{
		printf("线性表是空的\n");
		return -1;
	}

	if (L->last == 0)
	{
		return 0;
	}

	while (i < L->last)
	{
		j = i + 1;
		while (j <= L->last)
		{
			if (L->data[i] == L->data[j])
			{
				list_DeleteMember(L, j);		//删除数据后j不需要+1
			}
			else 
			{
				j++;							//只有当没有删除的时候j需要+1
			}
		}
		i++;
	}

	return 0;
}

四、线性表(顺序表)的缺点

	线性表的顺序存储结构有存储密度高及能够随机存取等优点,
	但存在以下不足:
		(1)要求系统提供.-片较大的连续存储空间。	
		(2)插入、删除等运算耗时,.且存在元素在存储器中成片移动的现象;

总结

本期主要分享了数据结构的总体章程以及数据结构中顺序表的实现过程,那么给出了顺序存储的两种形式(定义结构体不同),当然,萝卜白菜各有所爱,我比较喜欢第一种,操作起来比较方便,因为它在结构体中直接存储了顺序表的总个数,显得整个顺序表具有整体性;但是两种形式在本质上气势上是一样的,都是顺序表且本质完全一致;小伙伴们学起来,加油!
最后,各位小伙伴们如果喜欢我的分享可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值