数据结构—顺序表的剩余基本操作(C语言详细解读版3/3)

本文提供了C语言实现的顺序表(SqList)操作,包括初始化、销毁、清空、求长度、判空、插入元素、删除元素以及获取元素位置等方法。示例代码详细解释了每个操作的逻辑和注意事项,特别强调了元素插入和删除时的位置计算。
摘要由CSDN通过智能技术生成

本部分操作

  1. 删除目标位置的一个元素
  2. 获取目标位置的元素
  3. 获取某元素的第一次出现的位置,没有就返回-1
/* [Daily Coding] Fall in love coding! */
/*
顺序表 (SqList, Sequence List),即顺序线性表。
*/

// 顺序表的初始化

#include"stdio.h"  
#include"malloc.h"  // malloc

#include"stdlib.h"  // exit

#define List_InitSize 100
#define List_Increment 10

typedef struct{
	int *elem;  // 存储空间基址
	
	int length;  // 当前长度
	int listsize; // 当前分配的存储容量(以sizeof(int)为单位)
}SqList;

// 【C段代码】调用示例:
// SqList l; 
// Init_SqList03(&l);
void Init_SqList03(SqList *L)
{
	L->elem = (int*)malloc(List_InitSize * sizeof(int));
	if (!L->elem)
	{
		exit(-2);
	}
		
	L->length = 0;
	L->listsize = List_InitSize;
}

void DestroyList_Sq(SqList *L)
{
	free(L->elem);		// free((*L).elem);
	L->elem = NULL;		// (*L).elem = NULL;  // 置空防止野指针 
	L->length = 0;		// (*L).length = 0; 
	L->listsize = 0;	// (*L).listsize = 0; 
} 

// 清除 
int ClearList_Sq(SqList *L)
{
	// 直接把L->length置为0即可,这样在下一次插入操作的时候,之前的元素就被覆盖了。(其实,在计算机内部,当你申请了一段内存用于存放元素的时候,这个内存中已经存放了一些随机数。这些随机数会在插入操作时被覆盖。)
	L->length = 0;
	return 0;
}

/*
// 清除顺序表:无返回值版 
void ClearList(SqList *L)
{
	L->length = 0;
}
*/

// 求长度 
int ListLength_Sq(SqList *L)
{
	// 直接返回顺序表中元素的长度即可。注意,顺序表的长度和顺序表的容量是两个概念。顺序表的容量是表示这个表能存放多少元素。顺序表的长度表示这个顺序表中已经存在了多少元素。
	return L->length;
}

// 判空 
int ListIsEmpty_Sq(SqList *L)
{
	if(L->length == 0)
		return 1;
	else
		return 0;
}

// 画个图,形象地说一下这个问题。 
int ListInsert_Sq(SqList *L, int i, int e)
{
	// 这里的i,要想清楚它的含义。这里的i是指,在第i位之前(i从1开始)。比如,在第0号位插入,就是在第1号位之前插入,即,i=1。 
	// * 假设顺序表初始长度为10,目前里面已经有了1个元素。我们可以插入的位置,用计算机语言描述插入位置的下标可以是(0号位,1号位。不能插入2号位,因为是顺序存储,不能跳)
	// * 那么,用大众的思维,我们如果要插入0号位。那就是说,在第1个位置之前插入,即i=1。对应的,该位置的地址可以利用语句 [(*L).elem + i - 1]表示。
	// * 并且,如果我们要插入在第1号位,在本例中,1号位就是顺序表的最后1个位置的后面。那就是说,在第2个位置插入。那么,2怎么来的呢?
	// ** 2应该是当前顺序表内元素的个数+1.换句话说,在最后一位插入,就是在当前顺序表的length + 1位插入。 
	// ** 比如,现在顺序表中有3个元素。我想在最后1位之后插入。那就是获取L的length(也就是3),在这个位置后面插入就行了,即i要再增加1位,变为4,因为对应的代码应该延续上面的,有一个减1的操作。 [(*L).elem + 4 - 1]
	// * 所以,这里我们要判断的是,待插入位置,不能小于1(大众思维);不能大于length+1(比如已存3个元素,我们最多在第4个位置插入) 
	
	if((i < 1) || i > L->length + 1)
		return 0;
	
	if (L->length >= L->listsize)
	{
		int *newbase = (int*)realloc(L->elem, (L->listsize + List_Increment) * sizeof(int));
		if (!newbase)
		{
			exit(-2);
		}
		
		L->elem = newbase;
		L->listsize = L->listsize + List_Increment;
	}
	
	int *q = L->elem + i - 1;  // q指向待插入的位置
	int *p; 
	for (p = L->elem + L->length - 1; p >= q; p--)
	{
		*(p + 1) = *(p);
	}
	// 腾出位置后,在q位置插入元素e
	*q = e;
	// 插入元素后,长度别忘记加啦~ 
	L->length++;
	
	return 0; 
}

void ListTraverse_Sq(SqList *L)
{
	int i;
	for (i = 0; i < L->length; i++)
	{
		printf("%d ", *(L->elem + i));  // L->elem指向的是元素数组的首地址
	}
	printf("\n");
}

// i是从用户角度开始的(i从1开始)
// 如果i从0开始,我们称它从程序员角度开始的 
void ListDelete_Sq(SqList *L, int i, int *e)
{
	// 首先特判:判断参数的合理性
	if (i < 1 || i > L->length)  // 因为i最后需要减1(按照用户的角度,i是1开始的) 
		return;
	
	// 注意,不能这样写,因为括号内就是一个地址: int *p = &(L->elem + i - 1);
	int *p = L->elem + i - 1;  // 此时,p拿到的是待删除元素的地址
	
	*e = *p;
	
	int *q = L->elem + L->length - 1;
	
	// 把指向待删除目标的指针指向元素的下一位,往上移动,覆盖掉当前位。
	// 循环进行,直到顺序表中最后一位元素也被挪动了位置。 
	while(p < q)
	{
		*p = *(p + 1);  // 这里注意,需要在前面加上* 
		p++;
	}
	
	// 最后别忘记把length更新哦~
	L->length--; 
	
	return;
}


// 删除元素(version 2.0):修改了其中的循环语句体,其他语句都一样 
// i是从用户角度开始的(i从1开始)
// 如果i从0开始,我们称它从程序员角度开始的 
void ListDelete_Sq_v2(SqList *L, int i, int *e)
{
	// 首先特判:判断参数的合理性
	if (i < 1 || i > L->length)  // 因为i最后需要减1(按照用户的角度,i是1开始的) 
		return;
	
	// 注意,不能这样写,因为括号内就是一个地址: int *p = &(L->elem + i - 1);
	int *p = L->elem + i - 1;  // 此时,p拿到的是待删除元素的地址
	
	*e = *p;
	
	int *q = L->elem + L->length - 1;
	
	// 把指向待删除目标的指针指向元素的下一位,往上移动,覆盖掉当前位。
	// 循环进行,直到顺序表中最后一位元素也被挪动了位置。 
	
	for (p++; p <= q; p++)
	{
		*(p - 1) = *p;
	}
	
	// 最后别忘记把length更新哦~
	L->length--; 
	
	return;
}

// 获取固定位置的元素
void ListGetElem_Sq(SqList *L, int i, int *e)
{
	// 特判:i
	if (i < 1 || i > L->length)
		return;
		
	// 或者可以使用 *e = L->elem[i-1]; 
	*e = *(L->elem + i - 1);
	 
	return;
} 

// 获取某元素的第一次出现的位置,没有就返回-1 
void ListGetPos_Sq(SqList *L, int *p, int e)
{
	int i;
	for (i = 1; i <= L->length; i++)
	{
		if (L->elem[i - 1] == e)
		{
			*p = i;
			return;
		}
	}
	
	*p = -1;
	 
	return;
} 

int main()
{
	SqList Lc;  // 操作符为 "." 
	Init_SqList03(&Lc);
	
	printf("Lc.length=%d, Lc.listsize=%d\n", Lc.length, Lc.listsize);
	
	ListInsert_Sq(&Lc, 1, 30);
	printf("Lc.length=%d, Lc.listsize=%d\n", Lc.length, Lc.listsize);
	ListTraverse_Sq(&Lc);
	
	
	ListInsert_Sq(&Lc, 1, 40);
	printf("Lc.length=%d, Lc.listsize=%d\n", Lc.length, Lc.listsize);
	ListTraverse_Sq(&Lc);
	
	ListInsert_Sq(&Lc, 1, 20);
	printf("Lc.length=%d, Lc.listsize=%d\n", Lc.length, Lc.listsize);
	ListTraverse_Sq(&Lc);
	
	ListInsert_Sq(&Lc, 1, 50);
	printf("Lc.length=%d, Lc.listsize=%d\n", Lc.length, Lc.listsize);
	ListTraverse_Sq(&Lc);
	
	printf("---------------\n"); 
	
	// 测试:删除目标位置的一个元素 
	int elemDel;
	int posDel = 2;
	ListDelete_Sq_v2(&Lc, posDel, &elemDel);  // 删除掉第2个元素 
	printf("The elem on Delete position(%d-th) is %d.\n", posDel, elemDel);
	printf("---------------\n");
	
	ListTraverse_Sq(&Lc);
	// 测试:获取目标位置的元素 
	int targetE;
	int posWant = 3;
	ListGetElem_Sq(&Lc, posWant, &targetE);
	printf("The elem on Wanted position(%d-th) is %d.\n", posWant, targetE);
	
	ListTraverse_Sq(&Lc);
	
	// 测试:获取某元素的第一次出现的位置,没有就返回-1 
	int posElem;
	int someElem = 40;
	ListGetPos_Sq(&Lc, &posElem, someElem);
	printf("The position of the someElem(%d) is %d.\n", someElem, posElem);
	
	return 0; 
}

在这里插入图片描述

ok, 小例子结束,聊聊整个代码的具体情况。

  • 3/3版本,其实也意味着顺序表的全部功能都已实现。但是本部分的重点是根据元素获取位置,或者根据位置操作元素(已在文章最开始进行了阐述)。为了方便测试,所以把前面的1/3,2/3的代码都保留了下来。大家可以按照编号顺序,一个一个啃。很快就能吃掉这个顺序表啦~

初稿完成时间:2023年02月10日

预估有效期限:forever

(水平有限,欢迎评论补充、沟通探讨^_^,progress together。)

-完-

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值