数据结构--链表的实现(复习)

单链表的定义不同于顺序表,顺序表是整体定义的,需要定义顺序表的容量和当前包含多少元素。而链表只需要定义单个元素的种类和指向下一个元素的指针即可。(相比之下,同样是借助结构体,顺序表的结构体是整个链表,而链表的结构体只能代表其中的一个节点。

typedef int SLTDatatype;
typedef struct SLTNode
{
	SLTDatatype data;
	SLTNode* next;
}SLTNode;
打印出链表中的每一个元素:
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d-> ", cur->data);
		cur = cur->next;
	}
}

向链表中尾插一个节点:想要尾插一个节点,就需要先为其申请一块空间,并在这块空间上对节点初始化,将代表节点的结构体中的数值赋为我想要的值,将其指针置为空。然后想办法找到当前链表的尾节点,将新初始化的节点的地址赋给尾节点的指针。

void SListPushBack(SLTNode** pphead, SLTDateType x)//在本函数中,需要切切实实的对头指针进行改变,因此,在传参时,对于指针,就要进行传址调用,对于指针进行传址调用就需要
//调用二级指针。(过去传址调用调用的是指针,那是因为只需要调用元素的地址即可,而这次需要调用的是指针的地址)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//这里为新插入的节点申请一块空间,这块空间没有必要与前面的链表相联系
	newnode->data = x;
	newnode->next = NULL;//将新节点的值赋为x,其指针指向空

	if (*pphead == NULL)//当原链表的头节点即为空时,即原链表为空
	{
		*pphead = newnode;
	}
	else
	{
		// 找到尾节点,由于单链表只能单向查找,因此当想到达最后一个节点时,只能从前往后一个一个节点的来
		SLTNode* tail = *pphead;//相当于拷贝一份头节点的地址
		while (tail->next != NULL)
		{
			tail = tail->next;
		}//将该拷贝的指针不断向后移动,知道其移动到尾节点为止

		tail->next = newnode;//将新插入的节点与链表对接
	}	
}

向内存中为单链表申请一个新节点的空间:首先当然是向内存中申请一块结构体大小的空间。然后要向这块空间中填充细节, 即结构体的值和结构体指针,最后再返回指向这块空间的指针。(定义了该函数之后,以后再对单链表增加节点就可以把繁琐的申请空间的操作省略成SLTNode*newnode=BuyListNode(SLTDatetype x);

SLTNode* BuyListNode(x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

在单链表中头插一个节点:

void SListPushFront(SLTNode** phead, SLTDateType x)
{
	SLTNode* newnode = BuyListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

尾删单链表中的元素:

void SListPopBack(SLTNode** pphead)
{
	assert(*pphead != NULL);//首先要断言准备要删除节点的单链表不为空,否则没必要删除了
	if ((*pphead)->next == NULL)//如果链表中只有一个节点,那就删除唯一的那个节点就可以了
	{
		free(*pphead);
		*pphead = NULL;
	}
	else//如果有多个,则找到最后一个节点的位置,并将其删去即可,需要注意的是,不但要删除该节点的空间,更要将指向这块空间的指针置空
	{
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);//将指针tail->next指向的空间释放(归还给内存)
		tail->next = NULL;//将指向这块空间的指针置空(避免野指针的出现)
	}
}

头删链表中的元素:

void SListPopFront(SLTNode** pphead)
{
	assert(*pphead != NULL);
	SLTNode* next = (* pphead)->next;//由于要释放头节点的空间,指向头节点的指针地址会丢失,因此要预先定义指针以保存头节点的相关位置(这里是第二个节点的位置)
	free(*pphead);
	*pphead = next;//此函数中虽然也有空间的释放,但是指向被释放空间的指针在这一步被赋予了新的地址,避免了野指针的出现
}

在链表中找值为x的节点:从第一个节点一个一个向后找,(注意要保留头节点的位置,因此要定义一个头节点指针的拷贝),当cur->data==x时,说明找到了,如果一直到cur->next==NULL都没有符合条件的节点,说明没找到。

SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* cur= phead;
	while (cur)
	{
		if (cur->data == x)
			return cur;
		else
			cur = cur->next;
	}
	return NULL;
}

在单链表的pos位置插入一个值为x的节点:

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	SLTNode* newnode = BuyListNode(x);//根据之前定义的函数,直接申请空间并创建一个新节点即可
	SLTNode* posPrev = *pphead;
	if (*pphead == pos)//如果头节点就是pos,那直接头插即可,相对简单。
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else//pos位于链表的内部,想要在pos位置插入一个元素,需要找到pos前一个位置的元素,并在该元素的后面插入节点,操作完毕后,该节点就在pos的位置了
	{
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
}

在单链表的特定位置删去一个节点:和在单链表的特定位置插入一个节点思路类似。

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead);
	assert(pos);
	if (*pphead == pos)
	{
		*phead = pos->next;
		free(pos);
	}
	else
	{
		SLTNode* tmp = *pphead;
		while (tmp->next != pos)
		{
			tmp = tmp->next;
		}
		tmp->next = pos->next;
		free(pos);
	}
}

销毁单链表:不但要依次释放每一个节点的空间,还要注意避免释放空间后野指针的出现。(此函数涉及双指针的问题,在指针交替移动的时候,next始终指向具体的空间,而cur每当指向的空间被释放时,又立即被赋予next的地址,因此在删除过程中,除头节点外,始终没有野指针的出现。

void SListDestory(SListNode** pphead)
{
	assert(*pphead);
	SLTNode* cur = *pphead;//尽量不要丢失头指针,所以定义它的一个拷贝
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;//在最后一个循环中,next恰好为NULL,故不需要专门为cur置空
	}
	*pphead = NULL;//将头指针置空,避免野指针的出现(整个过程中头指针都没有发生变化,但它指向的空间已经被释放)
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值