2024年最新【神秘海域】[动图] 顺序表千字破解~_神秘海域顺序(2),2024年最新威力加强版

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

实现代码:

// 顺序表初始化
void seqListInit(SeqList\* psl)
{
	assert(psl);

	psl->arr = NULL;
	psl->Size = psl->Capacity = 0;
}

创建一个顺序表验证一下:

创建出的顺序表,初始化成功。
🌈

顺序表尾插 及 容量检查🐟
// 顺序表容量检查
void checkCapacity(SeqList* psl);
// 顺序表尾插
void seqListPushBack(SeqList* psl, SLDataType x);

顺序表尾插,即 在顺序表最后一个数据之后存放数据
存放数据进入顺序表之前,要 先确保顺序表处于未满状态 的,不然数据无法存储。

所以在实现尾插之前,先 实现一个容量检查的函数
容量检查一般需要实现什么功能?

  1. 检查顺序表是否已满
  2. 如果顺序表已满,则扩容

具体实现如下:

void checkCapacity(SeqList\* psl)
{
	assert(psl);

	if(psl->Size == psl->Capacity)
	{// 如果存入的数据数量 与 顺序表的容量相等,就代表顺序表已满
		int newCapacity = psl->Capacity == 0 ? 2 : 2 \* psl->Capacity;    
		//如果当前容量等于0,代表新顺序表,新容量给 2
		//如果当前容量不等于0,代表非新顺序表,新容量给原来容量的两倍
		SLDataType\* tmp = (SLDataType\*)realloc(psl->arr, sizeof(SLDataType)\* newCapacity);
		// 先用tmp 指向 realloc 扩容出新空间,以防止扩容失败导致元数据丢失
		if(tmp == NULL)
		{// tmp 为空 表示扩容失败,退出程序
			printf("realloc fail!\n");
			exit(-1);
		}
		// tmp 不为空,再将tmp 赋于 psl->arr;
		psl->arr = tmp;  //psl->arr 指向扩容后的地址
		psl->Capacity = newCapacity;   // 容量改为扩容后的容量
	}
}

容量检查函数实现成功之后,就可以继续 实现尾插函数

先考虑一下,尾插都需要注意哪些事项

  1. 需要先用容量检查函数检查容量
  2. 需要在数组中 Size 的位置放入数据之后,Size 自增
    (因为数组下标从 0 开始,所以 Size 位置即为最后一个数据之后)

具体实现如下:

// 尾插
void seqListPushBack(SeqList\* psl, SLDataType x)
{
	assert(psl);    //断言保证传入的结构体的地址不为空

	checkCapacity(psl);    //容量检查函数检查容量

	psl->arr[psl->Size] = x;    // 将 x 尾插
	psl->Size++;    // 存储数据量加1 , Size也要加1
}

每实现一个接口,最好都要验证一下

尾插三个整型数据,验证

尾插三次,扩容两次,尾插成功.
🌈

顺序表打印🐟
// 顺序表打印
void seqListPrint(SeqList* psl);

尾插存放数据是可以了,但是如何实现顺序表的打印呢?

顺序表打印 接口的实现也是非常的简单:

// 顺序表打印
void seqListPrint(SeqList\* psl)
{
	assert(psl);

	for (int i = 0; i < psl->Size; i++)
	{
		printf("%d ", psl->arr[i]);
	}
	printf("\n");
}

验证:

🌈

顺序表尾删🐟
// 顺序表尾删
void seqListPopBack(SeqList* psl);

实现了顺序表的尾插,如果需要删除数据,对应的还有顺序表的尾删。

尾删需要注意几个点:

  1. 尾删需不需要将 需要删除的数据 的空间释放掉?
  2. 尾删需不需要将 需要删除的数据 赋值为 0 ?
  3. 尾删需不需要控制一下顺序表中的 Size
  4. 控制不控制 Size 的值,对顺序表来说有什么区别?

先来思考一下第一个问题,尾删需不需要将 需要删除的数据 的空间释放掉?
答案是,不需要,也无法释放。
因为 free 函数使用的条件是,mallocrealloc 函数开辟的整块空间,且传入的必须是是这块空间的首地址。所以无法释放其中的单独一块空间。

第二个问题,尾删需不需要将 需要删除的数据 赋值为 0 ?

答案同样是,不需要。
因为尾删之后,下次使用这块空间一定是在插入数据的时候,到时候会有新的数据将其覆盖,所以不需要将其赋值为 0`

第三个问题和第四个问题,一起思考一下:

尾删需不需要控制一下顺序表中的 Size
控制不控制 Size 的值,对顺序表来说有什么区别?

答案是,需要控制 Size 的值,防止 过多次使用尾删(使用次数比存放数据数量多) 导致 Size 变成负数。
Size 变成负数,会发生什么情况?

使用下面段 尾删函数试验一下:

// 顺序表尾删
void seqListPopBack(SeqList\* psl)
{
	assert(psl);

	psl->Size--;
}


尾插 3 个数据,但是尾删了 5 次
可以看到,顺序表中的 Size 变成了 -3
变成负数之后,如果继续进行其他操作,一定会发生错误
比如:
即使,尾插了两次,顺序表中还是无法输出数据,也就是说,再次尾插的两个数据并没有存放至顺序表中。
因为 这两次尾插,是从 Size 为 -3 的地方执行的,并没有从 0 位置开始,所以无法存入,同时也发生了越界现象

这里 VS 编译器 不报错,是因为 对于数组越界的情况, VS编译器 是抽查的。也就是说,并不是所有的越界情况 VS编译器 都能检查得到

所以尾删,需要对顺序表中的 Size 进行一个简单的控制:

// 顺序表尾删
void seqListPopBack(SeqList\* psl)
{
	assert(psl);
	
	if (psl->Size > 0)
	{// 当Size 大于零 再进行自减
		psl->Size--;
	}
}

这样就不会发生使 Size 减到负 的问题:

即使 尾删次数过多,Size 也不会变为负数,也就不会发生其他操作错误的情况。
🌈

顺序表头插🐟
// 顺序表头插
void seqListPushFront(SeqList* psl, SLDataType x);

顺序表的尾插比较简单,只需要在顺序表 Size 位置放入数据就可以了。

头插呢?头插需要注意到什么问题?
顺序表本质上是 数组实现的,那么需要在数组的首位置插入数据就不仅仅是插入数据那么简单。需要先将数组中的数据向后移动一位,然后才能将新数据放入首位置
演示:

那么来实现头插一下:

// 头插
void seqListPushFront(SeqList\* psl, SLDataType x)
{
	assert(psl);    //断言保证传入的结构体的地址不为空

	checkCapacity(psl);    //插入前 首先检查容量

	// 方法一:
	int end = psl->Size;
	while (end)
	{
		psl->arr[end] = psl->arr[end - 1];
		end--;
	}

	// 方法二:
	/\*int end = psl->Size - 1;
 while (end >= 0)
 {
 psl->arr[end + 1] = psl->arr[end];
 end--;
 }\*/

	// 这两个方法其实一样的,只是对 末尾元素的位置控制方式不同
	

	psl->arr[0] = x;    // 将 x 尾插
	psl->Size++;    // 存储数据量加1 , Size 加1
}

验证:

头插成功。
🌈

顺序表头删🐟
// 顺序表头删
void seqListPopFront(SeqList* psl);

实现了头插,接着来实现一下头删

头删和头插的思路相似,不过只是将从 1 ~ Size-1 位置的数据向前移动一位。
不过,头删同样需要保证 Size > 0 才能执行。
演示:

头删实现:

// 顺序表头删
void seqListPopFront(SeqList\* psl)
{
	assert(psl);

	if (psl->Size > 0)
	{
		int begin = 1;
		while (begin < psl->Size)
		{
			psl->arr[begin] = psl->arr[begin - 1];
			begin++;
		}

		psl->Size--;
	}
}

验证:

即使删除过多,也不会出错。
🌈

顺序表查找🐟
int seqListFind(SeqList* psl, SLDataType x);

查找顺序表中某个数据所在的位置,可以用到顺序表查找的操作。
因为顺序表的本质是数组,所以 只需要将顺序表从头到尾遍历一遍,找到值就返回 其所在的位置 ,找不到就返回 -1

查找的实现:

// 顺序表查找
int seqListFind(SeqList\* psl, SLDataType x)
{
	assert(psl);

	for (int i = 0; i < psl->Size; i++)
	{
		if (x == psl->arr[i])
			return i;
	}

	return -1;
}

验证:

验证了,一般情况、边界情况、未找到情况,均返回正确。
🌈

指定位置插入🐟
void seqListInsert(SeqList* psl, size_t pos, SLDataType x);

尾插、头插都实现了,接下来就实现一下指定 pos 位置插入数据。
在指定 pos 位置插入数据,和前插有一点相似,需要将数据向后移一位。

我们可以用相似的方法移动,不过是从 pos 位置以后的数据向后移动一位。
不过首先还是要检查容量:

// 指定位置插入
void seqListInsert(SeqList\* psl, size\_t pos, SLDataType x)
{
	assert(psl);
	
	SeqListCheckCapacity(psl);    // 检查容量
	
	size\_t end = psl->Size;
	while(end > pos)
	{
		psl->arr[end] = psl->arr[end - 1];
		end--;
	}
	
	psl->arr[pos] = x;
	psl->Size++;
}

验证一下:

在插入之前可以使用 查找函数 获取一下 pos 位置


pos 位置插入数据 成功了。

但是真的成功了吗?
这个函数,现在还有没有什么问题?
比如,当我在 20 这个位置插入,会发生什么呢?
看一下:

20 位置插入数据没有报错,但是输出的时候输出的是随机值
而且,在实际的操作中,程序是延迟了一会才结束的
为什么?

因为,顺序表中存放的数据数量只有 9 个,占用的位置只有0 ~ 8,如果在 20 位置插入数据,超出了顺序表应有的位置,这个时候 Size 照常自增
那么输出的时候就会输出 0 ~ 9 位置的数据,而 9 位置并没有赋予数据,所以输出的是随机值。

这就提出了一个问题:当传入的 pos 位置比实际应该插入的位置大,怎么解决?
其实也很简单,如果 pos 位置过大,直接结束 插入函数 就可以了。

那么改进后的代码就是:

// 指定位置插入
void seqListInsert(SeqList\* psl, size\_t pos, SLDataType x)
{
	assert(psl);
	
	checkCapacity(psl);    // 检查容量
	
	if(pos > psl->Size)
	{// 尾插的位置是 Size,如果大于Size,就代表pos过大
		printf("Insert fail. Pos > Size! Pos = %d \n", pos);
		return;
	}
	
	size\_t end = psl->Size;
	while(end > pos)
	{
		psl->arr[end] = psl->arr[end - 1];
		end--;
	}
	
	psl->arr[pos] = x;
	psl->Size++;
}

现在再执行一遍代码:

pos 大于 Size 时,会输出警告,并停止插入。
🌈

调用指定位置插入的 尾插 和 头插🪸

那么。
既然插入函数可以指定位置插入数据,是不是就说明 头插 尾插 都可以直接调用插入函数 实现呢?
答案当然是可以的。
所以 尾插头插 函数就可以改进为:

// 尾插
void seqListPushBack(SeqList\* psl, SLDataType x)
{
	assert(psl);    //断言保证传入的结构体的地址不为空
	/\*
 checkCapacity(psl); //容量检查函数检查容量

 psl->arr[psl->Size] = x; // 将 x 尾插
 psl->Size++; // 存储数据量加1 , Size也要加1
 \*/
	
	seqListInsert(psl, psl->Size, x);    //在 Size 位置插入数据,就是尾插
}

// 头插
void seqListPushFront(SeqList\* psl, SLDataType x)
{
	assert(psl);    //断言保证传入的结构体的地址不为空
	
	/\*
 checkCapacity(psl); //插入前 首先检查容量

 // 方法一:
 int end = psl->Size;
 while (end)
 {
 psl->arr[end] = psl->arr[end - 1];
 end--;
 }

 // 方法二:
 int end = psl->Size - 1;
 while (end >= 0)
 {
 psl->arr[end + 1] = psl->arr[end];
 end--;
 }

 // 这两个方法其实一样的,只是对 末尾元素的位置控制方式不同
 

 psl->arr[0] = x; // 将 x 尾插
 psl->Size++; // 存储数据量加1 , Size 加1
 \*/
	seqListInsert(psl, 0, x);   // 在 0 位置插入数据,就是头插
}

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

int end = psl->Size - 1;
while (end >= 0)
{
psl->arr[end + 1] = psl->arr[end];
end–;
}

// 这两个方法其实一样的,只是对 末尾元素的位置控制方式不同

psl->arr[0] = x; // 将 x 尾插
psl->Size++; // 存储数据量加1 , Size 加1
*/
seqListInsert(psl, 0, x); // 在 0 位置插入数据,就是头插
}




[外链图片转存中...(img-8hPTy8sB-1715667165250)]
[外链图片转存中...(img-F5pwB1t3-1715667165250)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值