顺序表链表知识点总结

使用顺序表存储时的优势就是可以随机访问任意一个数据,因为其底层就是数组实现;

使用链表的优势则是插入、删除等操作非常方便。具体的不同如下表:

1、顺序表

顺序表的底层就是数组,这时用数组存储数据,那么就会有静态数组、动态数组的区别。在实际应用场景中,静态数组比较呆板,在初始化时指定数组的大小之后就不能改变;动态数组就很灵活,在空间不够时可以通过扩容操作从而满足需求,所以动态数组使用的比较多。

顺序表的结构:

typedef struct SqList{
    SLDataType* a;
    int size;//表示顺序表的大小
    int compacity;//表示数组的容量
}SQlist

这里的结构,需要注意SLDataType是为了方便对不同数据类型的顺序表做一个修改,例如整数顺序表、字符顺序表。

顺序表在初始化时需要注意,有两种方式:给一定初始空间和不给初始空间,这两种方式都可以。两种方式对应的扩容方法的书写也就有所差别。下面给一种初始化数组大小为4个SLDataType的方式:

void SLInit(SL* qs)

{

	//初始化,给数组的容量设置为4

	SLDataType* tmp = (SLDataType*)malloc(4 * sizeof(SLDataType));

	if (tmp==NULL)

	{

		perror("malloc");

		return 1;

	}

	qs->a = tmp;

	tmp = NULL;

	qs->size = 0;

	qs->compacity = 4;

}

这里对传的参数做一个解释。传参分为传值和传地址两种,举这里的顺序表为例。顺序表是一个结构体,其内部有SLDataType指针和两个整数。我们在调用函数时,是为了改变这个结构体,那么就需要传递这个结构体的指针。当然还有另一种方式,就是调用函数返回一个新的结构体,然后在主函数里用一个新的结构体去接收。后一种方式是较为麻烦的,这里就用前一种方式。

其他的对顺序表所做的操作,这里不再多赘述;一般对顺序表有几大操作:初始化和销毁,头插尾插,头删尾删,任意位置插入和删除,判断为空。一般在向一个数据结构中插入数据时要判断是否为满,但这里可以扩容,就变成了判断是否为满,满了之后进行扩容;在一个数据结构中删除一个数据时要判断是否为空。

void dila_sl(SL* qs) 

{

	if (qs->size == qs->compacity)

	{

		//当size等于compacity时扩容

		SLDataType* tmp = (SLDataType*)realloc(qs->a, (size_t)(2 * (qs->compacity) * sizeof(SLDataType)) );

		if (tmp == NULL)

		{

			perror("realloc");

			return 1;

		}

		qs->a = tmp;

		tmp = NULL;

		qs->compacity *= 2;

	}

}



int SLIsEmpty(SL* qs)

{

	if (qs->size==0)

	{

		return 0;//size=0表示该顺序表为空

	}

	return 1;

}







void SLDestory(SL* qs)

{

	//释放前要判断顺序表是否为空

	assert(qs);

	qs->size = 0;

	qs->compacity = 0;

	free(qs->a);

	qs->a = NULL;



}



void SLPushBack(SL* qs, SLDataType data)

{

	//从尾部插入数据

	assert(qs);

	//空间是否足够

	//不够要进行扩容

	dila_sl(qs);

	//扩容后空间足够,直接在尾部插入数据

	qs->a[qs->size] = data;

	(qs->size)++;

}



void SLPushFront(SL* qs, SLDataType data)

{

	//从头部插入数据

	assert(qs);

	//空间是否足够

	//不够要进行扩容

	dila_sl(qs);



	//扩容后在头部插入数据

	//从后往前往右移一位

	for (int i = qs->size; i > 0; i--)

	{

		qs->a[i] = qs->a[i - 1];

	}

	qs->a[0] = data;

	(qs->size)++;





}



void SLPopBack(SL* qs)

{

	//在尾部删除

	assert(qs);

	//size的值大于0时进行删除,否则什么也不做

	if (SLIsEmpty(qs))

	{

		qs->size--;

	}

	

}



void SLPopFront(SL* qs)

{

	//在头部删除

	//size的值大于0时进行删除,否则什么也不做

	assert(qs);

	if (SLIsEmpty(qs))

	{

		for (int i = 1; i < qs->size ; i++)

		{

			qs->a[i - 1]=qs->a[i] ;



		}

		qs->size--;



	}

}



void SLInsert(SL* qs, int index, SLDataType data)

{

	assert(qs);

	//限制index的范围 index>=0 && index<=size

	assert(index >= 0 && index <= qs->size);

	//判断需不需要扩容

	dila_sl(qs);

	//容量足够时直接插入



	for (int i = qs->size-1; i >index-1 ; i--)

	{

		qs->a[i + 1] = qs->a[i];

	}

	qs->a[index] = data;

	qs->size++;





}



void SLDelete(SL* qs, int index)

{

	assert(qs);

	//限制index的范围 index>=0 && index<=size

	assert(index >= 0 && index <= qs->size);

	if (SLIsEmpty(qs))

	{

		//非空才进行删除,否则不做任何事情

		for (int i = index; i <qs->size-1 ; i++)

		{

			qs->a[i] = qs->a[i + 1];

		}

		qs->size--;



	}

}



void SLPrint(SL* qs)

{

	for (int i = 0; i < qs->size; i++)

	{

		printf("%d ", qs->a[i]);

	}

	printf("\n");

}

2、链表

链表根据连接方式可分为单向链表、双向链表,根据是否带头分为带头链表、不带头链表,根据是否循环分为循环链表、非循环链表,总共2^3=8种链表。最常用的是单向不带头非循环链表和双向带头循环链表,前者主要作为其他数据结构的底层基础使用,例如链式队列,在oj题中没有特殊说明的链表也是单向不带头非循环链表;后者则是在实际应用的过程中使用。

单链表的结构:分为数据域和指针域(这里的指针的声明,不能用SLNode*,因为typedef的名字在结构体声明之后生效,只能用struct SListNode*)

typedef struct SListNode
{
	SLDataType data;
	struct SListNode* next;
} SLNode;

其他的一些操作,也是初始化与销毁、头插尾插、头删尾删、指定位置插入删除等等,这里需要注意的就是指针的改动,一些特殊情况,如首节点为空的情况;其他的如删除需要判断是否为空等和顺序表一致,下面是详细代码:

SLNode* SLNodeCreate(SLDataType data)

{

	SLNode* pNode = (SLNode*)malloc(sizeof(SLNode));

	pNode->data = data;

	pNode->next = NULL;

	return pNode;

}



void SLNodePushBack(SLNode** pphead, SLDataType data)

{

	SLNode* pNewNode= SLNodeCreate(data);

	//处理首节点为空的情况

	if (*pphead==NULL)

	{

		(*pphead) = pNewNode;

		return;

	}

	SLNode* pcur = *pphead;

	while (pcur->next!=NULL)

	{

		pcur = pcur->next;

	}

	pcur->next = pNewNode;



}



void SLNodePushFront(SLNode** pphead, SLDataType data)

{

	SLNode* pNewNode = SLNodeCreate(data);

	//处理首节点为空的情况

	if (*pphead == NULL)

	{

		(*pphead) = pNewNode;

		return;

	}



	pNewNode->next = (*pphead);

	*pphead = pNewNode;



}



void SLNodePrint(SLNode** pphead)

{

	SLNode* pcur = (*pphead);

	while (pcur)

	{

		printf("%d->", pcur->data);

		pcur = pcur->next;

	}

	printf("NULL\n");



}



void SLNodeDeleteBack(SLNode** pphead)

{

	assert(pphead);

	assert(*pphead);

	//需要找最后一个节点之前的那个节点

	SLNode* pcur = (*pphead)->next;

	SLNode* pfor = *pphead;

	if (!pcur)

	{

		//如果pcur为空,说明只有一个节点,直接删除就好了

		free(*pphead);

		*pphead = NULL; 

		return;

	}

	while (pcur->next)

	{

		pfor = pcur;

		pcur = pcur->next;

	}

	free(pcur);

	pcur = NULL;

	pfor->next = NULL;

}



void SLNodeDeleteFront(SLNode** pphead)

{

	assert(pphead);

	assert(*pphead);

	SLNode* pcur = (*pphead);

	*pphead = (*pphead)->next;

	free(pcur);

	pcur = NULL;



}



SLNode* SLNodeFind(SLNode** pphead, SLDataType data)

{

	assert(pphead);

	assert(*pphead);



	SLNode* pcur = *pphead;

	while (pcur)

	{

		if (pcur->data ==data)

		{

			return pcur;

 

		}

		pcur = pcur->next;

	}

	return NULL;

}



void SLInsertForward(SLNode** pphead, SLNode* pos, SLDataType data)

{

	assert(pphead);

	assert(*pphead);

	if (pos==NULL)

	{

		SLNodePushBack(pphead,data);

		// 就是尾插

		return;

	}



	SLNode* pcur = *pphead;

	SLNode* pnewnode = SLNodeCreate(data);

	if (pcur==pos)

	{

		SLNodePushFront(pphead, data); 

		return;

	}

	while (pcur->next != pos)

	{

		pcur = pcur->next;

	}

	pnewnode->next = pos;

	pcur->next = pnewnode;

}



void SLInsertBehind(SLNode** pphead, SLNode* pos, SLDataType data)

{

	assert(pphead);

	assert(*pphead);

	assert(pos);//pos为空,要在空后面添加节点,不合理

	/*SLNode* pcur = *pphead;



	while (pcur!=pos)

	{

		pcur = pcur->next;

	}*/

	SLNode* pnewnode = SLNodeCreate(data);

	pnewnode->next = pos->next;

	pos->next = pnewnode;

}



void SLTErase(SLNode** pphead, SLNode* pos)

{

	assert(pphead);

	assert(*pphead);

	assert(pos);

	SLNode* pcur = *pphead;

	if (pcur== pos)

	{

		SLNodeDeleteFront(pphead);

		//第一个元素便是pos所在的元素时,直接头删

		return;

	}

	while (pcur->next!=pos)

	{

		pcur = pcur->next;

	}

	pcur->next = pos->next;

	free(pos);

	pos = NULL;

}



void SLTEraseAfter(SLNode** pphead, SLNode* pos)

{

	assert(pphead);

	assert(*pphead);

	assert(pos);

	if (pos->next==NULL)

	{

		//什么也不做

		return;

	}

	SLNode* pcur = pos->next;

	pos->next = pcur->next;

	free(pcur);

	pcur = NULL;

}



void SLDestory(SLNode** pphead)

{

	SLNode* pcur = *pphead;

	while (pcur!=NULL)

	{

		SLNode* ptmp = pcur;

		pcur = pcur->next;

		free(ptmp);

		ptmp = NULL;

	}

	

	*pphead = NULL;

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值