【数据结构】不定长顺序表

不定长顺序表,顾名思义,就是顺序表的长度不定,可以存放任意多个数据。与定长顺序表相比,它可以在顺序表长度不够用时自己进行扩容。那么在设计不定长的顺序表时,存放数据的数组就不能是固定长度的了,这时我们可以考虑用动态数组来代替,进行扩容时就可以动态申请内存,将数据存放到动态数组中。同时加入一个变量 list_size 来存放总的格子数(总容量)。结构如图:

【数据结构】不定长顺序表_第1张图片

结构体声明如下:

typedef int ELEM_TYPE;
#define MAX_SIZE 10


typedef struct Dsqlist
{
	ELEM_TYPE* data;//指向malloc申请来的那块连续空间
	int length;//有效长度大小
	int list_size;//总容量大小
}Dsqlist, *PDsqlist;

不定长顺序表最重要的就是初始化操作和扩容操作,其余增删查改与定长顺序表没有太大区别。

初始化函数:

从堆区申请内存,将结构体成员变量赋初值

void Init_dsqlist(PDsqlist p)
{
	assert p != NULL;
	p->data = (ELEM_TYPE*)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
	assert(p->data!= NULL);

	p->length = 0;
	p->list_size = MAX_SIZE;
}

扩容函数:

首先判断顺序表有没有存满,如果满了的话,调用realloc函数重新分配空间,完成扩容。

判满函数:(当 p->length == p->list_size 时说明顺序表已满)

//判满操作
bool IsFull(PDsqlist p)
{
	return p->length == p->list_size;
}

扩容:

扩容方式:

  1. 先看原空间后面有没有足够的空间分配,有的话直接续上。
  2. 后面如果没有足够的空间,重新找一块足够大的内存,重新分配。将原有内存数据拷贝过来,然后释放掉。
  3. 后面连续闲置空间不够,且重新找一块也找不到,则realloc失败

【数据结构】不定长顺序表_第2张图片

代码如下 :

void Inc(PDsqlist p)
{
	//realloc  (第二个参数:需要的是  新开辟内存的总大小(以字节为单位))
	assert(p->data != NULL);
	ELEM_TYPE * tmp = (ELEM_TYPE*)realloc(p->data, (p->list_size*sizeof(ELEM_TYPE)) * 2);

	if(tmp == NULL)
	{
		printf("内存扩容失败\n");
		return Destroy(p);
	}
	p->data = tmp;

	//p->length不变,扩容不会涉及到有效长度的改变
	p->list_size *= 2;

}

其余增删查改函数:

//任意位置插入数据  
bool Insert_pos(PDsqlist p, int pos, int val)
{
	assert(p!=NULL);//确保不定长顺序表头结点存在
	if(p == NULL)
		return false;

	assert(pos >=0 && pos<=p->length);//确保插入位置合法
	if(pos<0 || pos>p->length)
		return false;

	if(IsFull(p))//如果空间满了 则扩容,让用户感觉到好似无限空间
	{
		Inc(p);//扩容函数
	}

	//此时 执行到这一行代码  可以保证 有足够的空间
	for(int i=p->length-1; i>=pos; i--)
	{
		p->data[i+1] = p->data[i];
	}

	//此时 执行到这一行代码  可以保证 已经将待插入位置空出来了
	p->data[pos] = val;
	p->length++;

	return true;
}

//任意位置删除数据
bool Del_pos(PDsqlist p, int pos)
{
	assert(p!=NULL);//确保不定长顺序表头结点存在
	if(p == NULL)
		return false;

	assert(pos >=0 && poslength);//确保删除位置合法
	if(pos<0 || pos>=p->length)
		return false;

	if(IsEmpty(p))//如果空间为NULL
	{
		return false;
	}

	//i一开始指向第一个挪动的数据下标   那最后一个需要挪动的数据下标p->length-1
	for(int i=pos+1; i<=p->length-1; i++)
	{
		p->data[i-1] = p->data[i];
	}

	//此时可以保证    数据全部向前覆盖完毕
	p->length--;

	return true;
}

//按值删除(如果存在重复数据,则删除val值首次出现的位置)
bool Del_val(PDsqlist p, int val)
{
	//assert

	int index = Search(p, val);
	if(index == -1)
	{
		return false;
	}

	return Del_pos(p, index);
}

//查询元素所在位置(如果存在重复数据,则返回val值首次出现的位置)
int Search(PDsqlist p, int val)
{
	//assert
	for(int i=0; ilength; i++)
	{
		if(p->data[i] == val)
		{
			return i;
		}
	}

	return -1;
}

//判空操作
bool IsEmpty(PDsqlist p)
{
	return p->length == 0;
}

//判满操作
bool IsFull(PDsqlist p)
{
	return p->length == p->list_size;
}

//清空
void Clear(PDsqlist p)
{
	p->length = 0;
}

//销毁
void Destroy(PDsqlist p)
{
	//p->length = 0;
	free(p->data);
}


//打印
void Show(PDsqlist p)
{
	for(int i=0; ilength; i++)
	{
		printf("%d ", p->data[i]);
	}
	printf("\n");
}

 顺序表总结:

  1. 逻辑简单,代码好实现
  2. 逻辑上相邻,物理上也相邻
  3. 任意位置插入数据的时间复杂度 O(n) (挪动数据),除了尾插 O(1) (尾插不需要挪动数据)
  4. 任意位置插入数据的时间复杂度 O(n) (挪动数据),除了尾删 O(1) (尾删不需要挪动数据)
  5. 随机访问数组中的数据时间复杂度 O(1) ,因为有数组下标的存在

你可能感兴趣的:(数据结构,数据结构)