单链表C语言实现附加力扣题

Posted IT莫扎特

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单链表C语言实现附加力扣题相关的知识,希望对你有一定的参考价值。


前言

今天我们的主题是实现单向不带头不循环的单链表

链表的实现

接口的声明

typedef int SLTDataTypde;

typedef struct SLTNode 
{
	SLTDataTypde data;
	struct SLTNode *next;
}SLTNode;

//链表销毁
void SLTNodeDestor(SLTNode *ps);
//创建结点
SLTNode *BuySLTNode(int x);
//打印
void SLTprint(SLTNode *ps);
//尾插
void SLTPushBack(SLTNode **ps, int x);
//头插
void SLTPushFront(SLTNode **ps,int x);
//头删
void SLTPopFront(SLTNode **ps);
//尾删
void SLTPopBack(SLTNode **ps);
//查找
SLTNode* SLTFind(SLTNode *ps, int x);
//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x);
//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x);
//移除指定位置之后
void SLTEraseAfter(SLTNode *pos);
//移除指定位置之前
void SLTEraseBefor(SLTNode **ps ,SLTNode *pos);

接口的实现

#include"SLTNode.h"

//创建结点
SLTNode *BuySLTNode(int x) 
{
	SLTNode *tmp = (SLTNode *)malloc(sizeof(SLTNode));
	if (!tmp) 
	{
		perror("BuySLTNode::malloc");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;

	return tmp;
}
//打印
void SLTprint(SLTNode *ps) 
{
	
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL\\n");
}	

//链表销毁
void SLTNodeDestor(SLTNode *ps) 
{
	assert(ps);
	while (ps) 
	{
		SLTNode *del = ps;
		ps = ps->next;
		free(ps);
	}
}
//尾插
void SLTPushBack(SLTNode **ps, int x)
{
	SLTNode *newNode = BuySLTNode(x);
	if (*ps == NULL) 
	{
		*ps = newNode;
		
	}
	else 
	{
		SLTNode *tail = *ps;
		//找尾
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
	

}

//头插
void SLTPushFront(SLTNode **ps,int x) 
{
	
	SLTNode *newNode = BuySLTNode(x);
	newNode->next = *ps;
	*ps = newNode;
}

//头删
void SLTPopFront(SLTNode **ps) 
{
	if (*ps == NULL)
		return;
	SLTNode *delNode = *ps;
	*ps = delNode->next;
	free(delNode);
	delNode = NULL;

}
//尾删
void SLTPopBack(SLTNode **ps) 
{

	SLTNode *phead = *ps;
	//只有一个结点
	if (phead->next == NULL)
	{
		free(phead);
		*ps = NULL;
		phead = NULL;
	}
	else if (*ps == NULL) 
	{
		return ;
	}
	else
	{	
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;
		//剩多个结点
		while (cur->next != NULL)
		{
			prev = cur;
			cur = cur->next;
		}
		free(cur);
		cur = NULL;
		prev->next = NULL;
	}
	
}

//查找
SLTNode* SLTFind(SLTNode *ps, int x) 
{
	if (ps == NULL)
		return NULL;
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		if (cur->data == x) 
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x) 
{
	assert(pos);
	SLTNode *next = pos->next;
	SLTNode *newNode = BuySLTNode(x);
	pos->next = newNode;
	newNode->next = next;
}

//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x)
{
	assert(pos);
	SLTNode *newNode = BuySLTNode(x);
	if (pos == *ps) 
	{
		newNode->next = *ps;
		*ps = newNode;
	}
	else 
	{
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;

		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = newNode;
		newNode->next = cur;
	}
}

//移除指定位置之后
void SLTEraseAfter(SLTNode *pos) 
{
	assert(pos);
	SLTNode *next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

//移除指定位置
void SLTEraseBefor(SLTNode ** ps, SLTNode *pos)
{
	assert(pos);
	SLTNode *next = pos->next;
	if (*ps == pos) 
	{
		free(*ps);
		*ps = NULL;
		*ps = next;
	}
	else
	{
		
		SLTNode *prev = NULL;
		SLTNode *cur = *ps;
		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}
}

销毁

//链表销毁
void SLTNodeDestor(SLTNode *ps) 
{
	assert(ps);
	while (ps) 
	{
		SLTNode *del = ps;
		ps = ps->next;
		free(ps);
	}
}

打印

遍历链表的每个结点,打印这个结点的data

//打印
void SLTprint(SLTNode *ps) 
{	
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL\\n");
}

创建结点

//创建结点
SLTNode *BuySLTNode(int x) 
{
	SLTNode *tmp = (SLTNode *)malloc(sizeof(SLTNode));
	if (!tmp) 
	{
		perror("BuySLTNode::malloc");
		exit(1);
	}
	tmp->data = x;
	tmp->next = NULL;

	return tmp;
}

尾插

创建一个新的结点newNode,找到最后一个结点的位置tail,用tail链接newNode,这里要注意的是我们的链表的实现是以不带头为目的的,所以会是一个空表,如果是空表的情况下,需要将ps初始化成为第一个结点,往后尾插就只需要找尾了,这里要传二级指针,为了改变外部的指针

//尾插
void SLTPushBack(SLTNode **ps,int x) 
{
	SLTNode *newNode = BuySLTNode(x);
	//如果是空表
	if (*ps == NULL) 
	{
		*ps = newNode;
	}
	else 
	{
		//尾插找尾
		SLTNode *tail = *ps;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

传递一级指针无法改变main函数的plist的指针,因为是在不同的栈帧中开辟的,只是一份值拷贝,互不影响,所以传递二级指针

头插

头插只需要创建一个新的结点newNode,让新的结点链接ps,再让ps去做新的头,由于需要改变外面的指针,还是要传递二级指针,头插即使是面对链表为空还是不会受影响

//头插
void SLTPushFront(SLTNode **ps,int x) 
{
	SLTNode *newNode = BuySLTNode(x);
	newNode->next = ps;
	//ps去做新的头
	*ps = newNode;
}

头删

头删同样需要改变外面的指针,所以要传二级,移除第一个结点,让第二个结点做新的头

//头删
void SLTPopFront(SLTNode **ps) 
{
	if (*ps == NULL)
		return;
	SLTNode *delNode = *ps;
	*ps = delNode->next;
	free(delNode);
	delNode = NULL;
}

尾删

尾删需要考虑三种情况
1、表为空,不需要删除,直接返回
2、只剩一个结点,删除这个结点,再置空,也需要改变外面指针的指向,必须传二级指针
3、有多个结点,定义前驱指针prev,和目标指针cur,当cur指向最后一个结点的时候释放cur,cur置空,前驱指针prev指向空

//尾删
void SLTPopBack(SLTNode **ps) 
{

	SLTNode *phead = *ps;
	//只有一个结点
	if (phead->next == NULL)
	{
		free(phead);
		*ps = NULL;
		phead = NULL;
	}
	//表为空直接返回
	else if (*ps == NULL) 
	{
		return ;
	}
	//剩多个结点
	else 
	{	
		//定义前驱指针和目标指针
		SLTNode *tail= *ps;
		SLTNode *prev = NULL;
		
		while (tail->next != NULL)
		{
			prev = tail;
			tail= tail->next;
		}
		//移除最后一个结点,让前一个指针指向空
		free(tail);
		cur = NULL;
		prev->next = NULL;
	}
	
}

查找

查找54

找到该结点就返回,没找到就返回空,空表直接返回

//查找
SLTNode* SLTFind(SLTNode *ps, int x) 
{
	if (ps == NULL)
		return NULL;
	SLTNode *cur = ps;
	while (cur != NULL) 
	{
		if (cur->data == x) 
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

指定位置后插入

在pos后面插入一个新的结点,需要备份pos的下一个结点,先让newNode指向第二个结点再让pos指向newNode

//指定位置后插入
void SLTInsertAfter(SLTNode *pos, int x) 
{
	assert(pos);
	SLTNode *next = pos->next;
	SLTNode *newNode = BuySLTNode(x);
	pos->next = newNode;
	newNode->next = next;
}

指定位置前插入

1、在指定pos的前面插入,如果pos和ps在同一个位置就是头插了
2、如果pos出现在链表的中间某个位置,要想在pos前插入一个新的结点就必须要找到pos前的一个结点prev,让这个结点prev指向新的结点newNode,最后让newNode指向当前结点

//指定位置前插入
void SLTInsertBefor(SLTNode **ps, SLTNode *pos, int x)
{
	assert(pos);
	SLTNode *newNode = BuySLTNode(x);
	if (pos == *ps) 
	{
		newNode->next = *ps;
		*ps = newNode;
	}
	else 
	{
		SLTNode *cur = *ps;
		SLTNode *prev = NULL;

		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = newNode;
		newNode->next = cur;
	}
}

指定位置后删除

next用来保存pos位置的下一个结点,pos指向next的下一个结点,释放next位置的结点

//移除指定位置之后
void SLTEraseAfter(SLTNode *pos) 
{
	//表为空不做处理
	if(pos == NULL)
	{
		return ;
	}
	assert(pos);
	SLTNode *next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

移除指定位置

prev和cur一起一后,当cur与pos相遇了,就将prev链接cur的下一个结点,释放pos位置的结点,将指针置空

如果plist和pos指向一起,就是头删操作了

//移除指定位置
void SLTEraseBefor(SLTNode ** ps, SLTNode *pos)
{
	assert(pos);
	SLTNode *next = pos->next;
	//pos等于ps,头删
	if (*ps == pos) 
	{
		free(*ps);
		*ps = NULL;
		*ps = next;
	}
	else
	{
		SLTNode *prev = NULL;
		SLTNode *cur = *ps;
		while (cur != pos) 
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}
}

力扣题

移除链表元素:

点我.

考虑两种场景

  • 1、如果表的第一个结点的值就是val,那么就是头删了,释放第一个结点,需要将head的指向改变,指向下一个结点
  • 2、如果表的所有结点全是val的情况,head的指向就可以一直改变,直到指向NULL,最后返回去的就是NULL

链表移除结点的变形题,把特殊场景处理就能搞定

struct ListNode* removeElements(struct ListNode* head, int val){
  
    struct ListNode *prev = NULL;
    struct ListNode *cur = head;
    while(cur != NULL)
    {
    	//值为val
        struct ListNode *del = NULL;
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        //值不为val
        else
        {
            //如果cur是头的情况
            struct ListNode *next = cur->next;
            if(prev == NULL)
            {
                free(cur);
                head = next;
                cur = next;
            }
            //cur不是头
            else
            {
                prev->next = next;
                free(cur);
                cur = next;
            }
        }
    }
    return head;
}

第二种写法:带哨兵位的头节点,可以让prev指向哨兵位结点,这样就不需要考虑prev是否

以上是关于单链表C语言实现附加力扣题的主要内容,如果未能解决你的问题,请参考以下文章

反转链表--力扣题

数据库力扣题组合两个表

循环链表(循环单链表循环双链表)的相关操作的代码实现(C语言)

C语言中数据结构中的单向链表的问题;

如何用c语言实现单链表的逆置?

数据库力扣题上升的温度