数据结构之顺序线性表

1.线性表的类型定义

  • 线性表是n个数据元素的有限序列
  • 在稍微复杂的线性表中,一个数据元素可以由若干个数据项组成,在这种情况下,常把数据元素称为记录,含有大量记录的线性表又称文件。
    在这里插入图片描述
  • 若将线性表记为:
    (a1,…,ai-1,ai,ai+1…an
    其中ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素

2.线性表的顺序表示和实现

  • 线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素
  • 只要确定了存储线性表的起始位置,线性表中任一数据元素都可以随机存起,所以线性表的顺序存储结构是一种随机存取的存储结构
  • 顺序线性表的定义
#define LIST_INIT_SIZE 100 //初始大小
#define LIST_INCREMENT 10   //分配的增量
typedef  int ElemType;
typedef struct
{
	ElemType* elem; //存储首地址
	int length;     //当前长度
	int listsize;   //当前分配的存储容量
}SqList;
  • 顺序线性表的初始化
bool InitList_Sq(SqList& L)
{
	L.elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType)); //分配存储空间
	if (!L.elem)  return false;  //分配失败
	L.length = 0;
	L.listsize = LIST_INIT_SIZE; 
	return true;
}
  • 顺序线性表的插入
    假设有n个元素,插入位置0移动n次,位置1移动n-1次,位置n移动0次。
    所以平均插入次数为(0+1+2+3…n)= n(n+1)/2
    设插入概率相同,为1/(n+1)
    得到时间复杂度为:
    O(n/2) = O(n)
bool ListInsert_Sq(SqList& L, int n, const ElemType& e)
{
	//检查i的合法值
	if (n<0 || n>L.length) return false;
	if (L.length >= L.listsize) {
		ElemType* newbase = (ElemType*)realloc(L.elem, (L.listsize + LIST_INCREMENT) * sizeof(ElemType));
		if (!newbase) return false;
		L.elem = newbase;  //取得新地址
		L.listsize += LIST_INCREMENT; //增加存储容量
	}
	for (int i = L.length - 1; i >= n; i--)
	{
		L.elem[i+1] = L.elem[i];
	}
	L.elem[n] = e;
	++L.length;
}
  • 顺序线性表的删除
    假设有n个元素,删除位置0移动n-1次,位置1移动n-2次,位置n-1移动0次。
    所以平均插入次数为(0+1+2+3…n-1)= n(n-1)/2
    设插入概率相同,为1/n
    得到时间复杂度为:
    O((n-1)/2) = O(n)
bool ListDelete_Sq(SqList& L, int n, ElemType& e)
{
	//检查i的合法值
	if (n<0 || n>L.length) return false;
	e = L.elem[n];
	for (int i = L.length - 1; i > n; --i)
	{
		L.elem[i-1] = L.elem[i];
	}
	L.length--;
	return false;
}
  • 将两个有序线性表合成一个新的有序线性表,并且该线性表依然有序
    该算法虽然有三个算法,但是下面两个循环只是处理第一个循环没有处理完成的情况,两者只会执行一个,所以实际是两个循环,时间复杂度为:
    O(Length(A)+Length(B))
void MergeList(const SqList& la, const SqList& lb, SqList& lc)
{
	InitList_Sq(lc);
	int i=0, j=0,k=0;
	while (i <= la.length - 1 && j <= lb.length - 1)
	{
		if (la.elem[i] < lb.elem[j])
		{
			ListInsert_Sq(lc, ++k, la.elem[i]);
			i++;
		}
		else
		{
			ListInsert_Sq(lc, ++k, lb.elem[j]);
			j++;
		}
	}
	//加入剩下la或者lb多余的数据
	while (i <= la.length - 1)
	{
		ListInsert_Sq(lc, ++k, la.elem[i]);
		i++;
	}
	while (j <= lb.length - 1)
	{
		ListInsert_Sq(lc, ++k, lb.elem[j]);
		j++;
	}
}
  • 从顺序线性表中查找,返回第一个匹配的索引
bool Compare(ElemType a, ElemType b)
{
	return a == b;
}
int LocateElem_Sq(const SqList& L, const ElemType e, bool(*compare)(ElemType, ElemType))
{
	for (int i = 0; i < L.length; ++i)
	{
		if ((*compare)(L.elem[i], e))
			return i;
	}
	return -1;
}
  • 求AUB存入B
    该算法在对b表的遍历中嵌套了对a表的遍历,所以时间复杂度为:
    O(Length(a)*Length(b))
void Union(SqList& La, const SqList& Lb)
{
	int k = La.length - 1;
	for (int i = 0; i < Lb.length; i++)
	{
		if (LocateElem_Sq(La, Lb.elem[i], Compare)==-1)
		{
			ListInsert_Sq(La, ++k,Lb.elem[i]);
		}
	}
}

3.优缺点

  • 优点:逻辑关系上相邻的两个元素的物理位置也相邻,因此不仅节省内存而且可以随机存取表中任一元素。
  • 缺点:对于插入和删除操作时间复杂度较大。
发布了33 篇原创文章 · 获赞 3 · 访问量 604

猜你喜欢

转载自blog.csdn.net/qq_43647628/article/details/104672245
今日推荐