数据结构2——线性表之顺序表

一、顺序表的概念及特点(优缺点)

顺序表是一种线性结构,由一组连续的内存空间依次存储数据元素,其每一个元素在内存中的物理地址相邻。顺序表具有以下特点:

  1. 容量固定:顺序表在创建时就需要指定容量,无法动态扩容或缩容;

  2. 索引访问:顺序表中的元素可以直接通过索引访问,时间复杂度为 O(1);

  3. 插入删除开销较大:在顺序表中插入或删除元素需要移动其他元素,时间复杂度为 O(n);

  4. 适合大量随机访问:由于元素在内存中的存储位置是连续的,所以适合进行大量的随机访问操作。

顺序表可以用数组来实现,每个元素在数组中的下标就是该元素在顺序表中的索引。

二、顺序表的存储

顺序表的存储方式可以分为 静态存储 和 动态存储 两种。

静态存储:即预先给其分配适当的空间,但在以后得程序中不可更改。若想更改,可以通过宏定义的方式定义一个变量,将其作为数组的大小,在之后可以通过改变这个宏变量的方式改变数组的大小。

动态存储:即不预先给数组分配空间,在插入数据时判断数组是否还有剩余空间(或者最初数组是否已经分配空间),在酌情动态给数组分配适当空间。

 1.顺序表的静态存储
#define N 7
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType array[N];	// 定长数组
	size_t size;			// 有效数组的个数
}SL;
2.顺序表的动态存储
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;  // a是指向动态开辟的数组的指针
	int size;		// 有效数据个数
	int capacity;	// 空间容量
}SL;

静态存储存在明显的缺点:

        不确定事先要给多大空间--(给大浪费,给小不够)。

而动态存储能够很好的解决这个问题,所以更推荐动态存储的存储方式。

 在接下来我们也是用动态的存储方式来实现顺序表的一些简单操作。

三、顺序表的简单操作

直接看函数声明 ↓ (增删查改)

// 顺序表的初始化
void SlInit(SL* ps1);
// 顺序表的销毁
void SLDestroy(SL* ps1);
// 打印顺序表
void SLPrint(SL* ps1);
// 检查数组空间
void SLCheckCapacity(SL* ps1);

// 尾插
void SLPushBack(SL* ps1, SLDataType x);
// 头插
void SLPushFront(SL* ps1, SLDataType x);
// 尾删
void SLPopBack(SL* ps1);
// 头删
void SLPopFront(SL* ps1);

// 指定位置插、删
void SLInsert(SL* ps1, int pos, SLDataType x);
void SLErase(SL* ps1, int pos);

// 查找
int SLFind(SL* ps1, SLDataType x);

四、顺序表功能的实现

1.初始化
void SLInit(SL* ps1)
{
	assert(ps1);        // 断言:判断指向结构体的指针是否为空指针

	ps1->a = NULL;        // 指向数的组指针初始化为NULL
	ps1->size = 0;        // 数组已经存储的数据个数 初始置为 0
	ps1->capacity = 0;    // 数组的大小 初始值置为 0

}
2.插入数据

在插入数据前,要先检查数组是否还有剩余空间 --- SLCheckCapacity

void SLCheckCapacity(SL* ps1)
{
	assert(ps1);        // 同理,检查结构体指针是否为空,若为空则直接报错

	if (ps1->size == ps1->capacity)    // 判断数组是否已经存满了
	{
        /*
            这里预先定义一个整形变量 newCapacity,用来将要开辟的新空间的大小(其实就是数组的
            类型的几倍)
        */
		int newCapacity = ps1->capacity == 0 ? 4 : ps1->capacity * 2; 
        // 这里用 realloc 开辟空间
		SLDataType* tmp = (SLDataType*)realloc(ps1->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)    // 检查空间开辟成功了没
		{
			perror("realloc fail");
			return;
		}

		ps1->a = tmp;        // 把新空间给 数组
		ps1->capacity = newCapacity;    // 更新数组大小
	}
}

然后在插入的函数里直接调用 SLChekCapacity 即可 ↓

尾插 --- SLPushBack:
// 尾插
void SLPushBack(SL* ps1, SLDataType x)
{
	assert(ps1);        // 同上

	SLCheckCapacity(ps1);    // 直接调用检查数组空间的函数

	ps1->a[ps1->size] = x;    // ps1->size 就是数组的最后一个数据的下标+1,所以直接在size处插入数据就可以了
	ps1->size++;            // 数据插进来后,要将size更新
}
头插 --- SLPushFront:
// 头插
void SLPushFront(SL* ps1, SLDataType x)
{
	assert(ps1);

	SLCheckCapacity(ps1);

	//挪动数据(把数据挨个向后移一位)
	int end = ps1->size - 1;        // end 标记下一个要挪动的数据
	while (end >= 0)
	{
		ps1->a[end + 1] = ps1->a[end];   // 这里要注意:向后挪动数据要从最后一位开始从后往前挪
		--end;
	}

	ps1->a[0] = x;        // 在数组的下标0处插入数据,因为前面已经将数据挨个向后移动一位了,所以此时 下标0 处是空着的
	ps1->size++;        // 更新size
}
3.删除数据
        尾删 --- SLPopBack:
// 尾删
void SLPopBack(SL* ps1)
{
	assert(ps1);

	// 暴力检查 -- 检查顺序表是否为空
	assert(ps1->size > 0);

    // 顺序表的删除数据特别简单,只需要把数组存储的数据的个数减一下就好了
	ps1->size--;
}
        头删 --- SLPopFront:
// 头删
void SLPopFront(SL* ps1)
{
	//暴力检查
	assert(ps1->size > 0);

	int begin = 1;
    // 将数据挨个向前移,直到把第一个数据覆盖
	while (begin < ps1->size)
	{
		ps1->a[begin - 1] = ps1->a[begin];
		begin++;
	}

	ps1->size--;    // 跟新size
}
4.查找
// 查找指定数据,返回下标
int SLFind(SL* ps1, SLDataType x)
{
	assert(ps1);

	for (int i = 0; i < ps1->size; i++)    // 循环遍历数组 匹配 目标值,找到返回下标
	{
		if (ps1->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

        有了 SLFind 函数,就可以先查找数据,然后再用 SLErase 删除指定数据了

5.删除指定位置的数据
void SLErase(SL* ps1, int pos)
{
	assert(ps1);
	assert(pos >= 0 && pos < ps1->size);    // 判断 指定位置的合理性

	// 挪动覆盖, 同头删类似,将指定位置后面的数据挨个向前移一位
	int begin = pos;
	while (begin < ps1->size)
	{
		ps1->a[begin - 1] = ps1->a[begin];
		begin++;
	}
	ps1->size--;   
}
6.在指定位置插入数据
void SLInsert(SL* ps1, int pos, SLDataType x)
{
	assert(ps1);
	assert(pos >= 0 && pos <= ps1->size);    // 判断 下标 pos 的合理性

	SLCheckCapacity(ps1);

	//挪动数据, 同头插类似
	int end = ps1->size - 1;
	while (end >= pos)
	{
		ps1->a[end + 1] = ps1->a[end];
		--end;
	}

	ps1->a[pos] = x;
	ps1->size++;
}
7. 打印顺序表
// 打印

// 循环遍历数组,把数据挨个printf一下
void SLPrint(SL* ps1)
{
	assert(ps1);
	for (int i = 0; i < ps1->size; i++)
	{
		printf("%d ", ps1->a[i]);
	}
	printf("\n");
}
8.销毁顺序表

        最后,在退出程序时要把结构体内容重新置为空并释放掉内存。

// 销毁
void SLDestroy(SL* ps1)
{
	assert(ps1);

	if (ps1->a != NULL)    // 判断数组是否为空
	{
		free(ps1->a);        //   释放掉分配给数组的空间
		ps1->a = NULL;        // 将结构体的内容置空
		ps1->size = 0;
		ps1->capacity = 0;
	}
}

至此,顺序表的一些简单操作就都实现啦。

总代码 ↓

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

typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;		// 有效数据个数
	int capacity;	// 空间容量
}SL;

void SlInit(SL* ps1);
void SLDestroy(SL* ps1);
void SLPrint(SL* ps1);

void SLCheckCapacity(SL* ps1);

// 尾插
void SLPushBack(SL* ps1, SLDataType x);
// 头插
void SLPushFront(SL* ps1, SLDataType x);
// 尾删
void SLPopBack(SL* ps1);
// 头删
void SLPopFront(SL* ps1);
// 指定位置插、删
void SLInsert(SL* ps1, int pos, SLDataType x);
void SLErase(SL* ps1, int pos);

// 查找
int SLFind(SL* ps1, SLDataType x);


void SLInit(SL* ps1)
{
	assert(ps1);

	ps1->a = NULL;
	ps1->size = 0;
	ps1->capacity = 0;

}

void SLDestroy(SL* ps1)
{
	assert(ps1);

	if (ps1->a != NULL)
	{
		free(ps1->a);
		ps1->a = NULL;
		ps1->size = 0;
		ps1->capacity = 0;
	}
}

void SLPrint(SL* ps1)
{
	assert(ps1);
	for (int i = 0; i < ps1->size; i++)
	{
		printf("%d ", ps1->a[i]);
	}
	printf("\n");
}

void SLCheckCapacity(SL* ps1)
{
	assert(ps1);

	if (ps1->size == ps1->capacity)
	{
		int newCapacity = ps1->capacity == 0 ? 4 : ps1->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps1->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		ps1->a = tmp;
		ps1->capacity = newCapacity;
	}
}

void SLPushBack(SL* ps1, SLDataType x)
{
	assert(ps1);

	SLCheckCapacity(ps1); 

	ps1->a[ps1->size] = x;
	ps1->size++;
}

void SLPushFront(SL* ps1, SLDataType x)
{
	assert(ps1);

	SLCheckCapacity(ps1);

	//挪动数据(把数据挨个向后移一位)
	int end = ps1->size - 1;
	while (end >= 0)
	{
		ps1->a[end + 1] = ps1->a[end];
		--end;
	}

	ps1->a[0] = x;
	ps1->size++;
}

void SLPopBack(SL* ps1)
{
	assert(ps1);

	//温柔检查
	/*if (ps1->size == 0)
	{
		return;
	}*/

	//暴力检查
	assert(ps1->size > 0);

	ps1->size--;
}

void SLPopFront(SL* ps1)
{
	//暴力检查
	assert(ps1->size > 0);

	int begin = 1;
	while (begin < ps1->size)
	{
		ps1->a[begin - 1] = ps1->a[begin];
		begin++;
	}

	ps1->size--;
}

void SLInsert(SL* ps1, int pos, SLDataType x)
{
	assert(ps1);
	assert(pos >= 0 && pos <= ps1->size);

	SLCheckCapacity(ps1);

	//挪动数据
	int end = ps1->size - 1;
	while (end >= pos)
	{
		ps1->a[end + 1] = ps1->a[end];
		--end;
	}

	ps1->a[pos] = x;
	ps1->size++;
}

void SLErase(SL* ps1, int pos)
{
	assert(ps1);
	assert(pos >= 0 && pos < ps1->size);

	// 挪动覆盖
	int begin = pos;
	while (begin < ps1->size)
	{
		ps1->a[begin - 1] = ps1->a[begin];
		begin++;
	}
	ps1->size--;
}

int SLFind(SL* ps1, SLDataType x)
{
	assert(ps1);

	for (int i = 0; i < ps1->size; i++)
	{
		if (ps1->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}




// TestSL 是测试功能函数
void TestSL1()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPushBack(&s1, 5);
	SLPushBack(&s1, 6);
	SLPushBack(&s1, 7);
	SLPushBack(&s1, 8);
	SLPushBack(&s1, 9);

	SLPrint(&s1);

	SLPushFront(&s1, 10);
	SLPushFront(&s1, 20);
	SLPushFront(&s1, 30);
	SLPushFront(&s1, 40);

	SLPrint(&s1);

	SLDestroy(&s1);
}

void TestSL2()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPushBack(&s1, 5);
	
	SLPrint(&s1);

	SLPopBack(&s1);
	
	SLPrint(&s1);

	SLDestroy(&s1);
}

void TestSL3()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPushBack(&s1, 5);

	SLPrint(&s1);

	SLPopFront(&s1);
	SLPopFront(&s1);
	SLPopFront(&s1);
	SLPopFront(&s1);

	SLPrint(&s1);
}

void TestSL4()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPushBack(&s1, 5);

	SLPrint(&s1);

	SLInsert(&s1, 4, 10);

	SLPrint(&s1);

	SLErase(&s1, 2);

	SLPrint(&s1);

	int pos = SLFind(&s1, 3);
	if (pos != -1)
	{
		SLErase(&s1, pos+1);
	}
	SLPrint(&s1);
}

// 主函数
int main()
{
	//TestSL1();
	//TestSL2();
	//TestSL3();
	TestSL4();

	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值