OJ :反转链表、移除单链表的元素、合并两个有序链表、链表分割 |【哨兵位】| C

必备知识:单链表的增删查改

【数据结构初阶】-3-链表 | 链表是什么?| 【单链表的增删查改|思路+源码】

概览及题目链接

在这里插入图片描述

在这里插入图片描述

题一:反转链表(头插)

在这里插入图片描述

思路一:依次反转

在这里插入图片描述

源码

注意:不能对空指针解引用!
易错:

struct ListNode* n3 = head->next;  //如果head不能NULL(所以要提前处理head为NULL的情况)

if (n3)
{
	n3 = n3->next; //最后一次循环中n3已经为NULL不能对其解引用
	//(所以对n3是否为空要进行判断!)
}
struct ListNode* reverseList2(struct ListNode* head)
{
	if (head == NULL)
	{
		return NULL;
	}
	struct ListNode* n2 = head, * n1 = NULL, * n3 = head->next;
	while (n2)
	{
		n2->next = n1;
		n1 = n2;
		n2 = n3;
		if (n3)
		{
			n3 = n3->next;
		}

	}
	return n1;
}

思路二:依次头插

在这里插入图片描述

源码

struct ListNode* reverseList1(struct ListNode* head)
{
	if (head == NULL)
	{
		return NULL;
	}
	struct ListNode* cur = head;
	struct ListNode* next = head->next;
	struct ListNode* newhead = NULL;
	while (cur)
	{
		cur->next = newhead;
		newhead = cur;
		cur = next;
		if (next)
			next = next->next;
	}
	return newhead;
}

(本质上以上两个代码的实现没有很大的区别,思路一更像是思路二的具体化)

题二:移除单链表的元素(尾插)

在这里插入图片描述

思路:非val的尾插

在这里插入图片描述

  • 首先,如上。移除链表元素后的链表的头节点可能被更改,因此我们要先解决头节点的问题
    我们根据head寻找链表中第一个有效的头节点,它是第一个尾插的对象(虽然它实际上它并没有插入谁),此时,我们需要考虑,移除链表元素后的newhead是否为空?
  • 移除链表元素后的head是否为空?
    • 链表本身为空链表(如上,示例2)
    • 因为非val的将被尾插,然而从第一个结点到最后一个结点我们未找到第一个(此时也是唯一一个)非val的结点(如上,示例3)

在这里插入图片描述
综上,尾插的时候有一次必要的判断(详情见完整源码)

if (!newhead)
{
	newhead = cur;
}

注意:但凡对指针进行了解引用的都有必要判断这个指针是否为空,不能对空指针解引用!

if (next)
	next = next->next;
if (tail)
	tail->next = NULL;

源码(无哨兵位头节点)

//无哨兵位
struct ListNode* removeElements1(struct ListNode* head, int val)
{
	if (!head)
		return NULL;
	struct ListNode* cur = head, * next = head->next;
	struct ListNode* newhead = NULL, * tail = NULL;
	//尾插
	while (cur)
	{
		if (cur->val == val)
		{
			free(cur);
			cur = next;
			if (next)
				next = next->next;
		}
		else
		{
			if (!newhead)
			{
				newhead = cur;
			}
			else
			{
				tail->next = cur;
			}
			tail = cur;
			cur = next;
			if (next)
				next = next->next;
		}
	}
	if (tail)
		tail->next = NULL;
	if (newhead)
		return newhead;
	return NULL;
}

【哨兵位头结点】

哨兵位头节点:不存储有效数据的头节点(malloc申请一块空间作为形式上的头节点)👉方便尾插,尾插完后记录下真正的头节点(哨兵位头结点的下一个),再 free 掉申请的空间

源码(含哨兵位头节点)

struct ListNode* removeElements2(struct ListNode* head, int val)
{
	struct ListNode* cur = head;
	struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));//哨兵位头节点
	if (!guard)
	{
		perror("removeElements()");
		return NULL;
	}
	struct ListNode* tail = guard;
	//尾插
	while (cur)
	{
		if (cur->val != val)
		{
			tail->next = cur;
			tail = tail->next;
			cur = cur->next;
		}
		else
		{
			struct ListNode* next = cur->next;
			free(cur);
			cur = next;
		}
	}
	tail->next = NULL;//tail只在新链表为空的情况下才可能为空
	//新链表有哨兵位头节点之后不可能为空,所以这里不需要判空
	struct ListNode* newhead = guard->next;
	free(guard);
	return newhead;
}

题三:合并两个有序链表(尾插)

在这里插入图片描述

思路:双指针,取小者进行尾插

在这里插入图片描述

  1. 首先如果其中一个为空,则返回另一个(如果两个都为空,返回其中一个也还是为空)
  2. 申请一块空间作为头结点
  3. 双指针比较,取小者进行尾插,当有一个为空时停止比较(所以循环的条件是两个指针都不为空)
  4. 其中有一个链表走到空指针停下,但是还有另一个链表剩下的结点需要继续尾插 ,剩下的直接接上
  5. 记录哨兵位头结点的下一个
  6. 释放哨兵位头结点
  7. 返回记录的地址

源码

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if(!list1)
        return list2;
    if(!list2)
        return list1;
    struct ListNode* guard =(struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next=NULL;
    struct ListNode* tail=guard;
    struct ListNode* cur1=list1,*cur2=list2;
    while(cur1&&cur2)
    {
        if(cur1->val>cur2->val)
        {
            tail->next=cur2;
            tail=tail->next;
            cur2=cur2->next;
        }
        else
        {
            tail->next=cur1;
            tail=tail->next;
            cur1=cur1->next;
        }
    }
    if(cur1)
        tail->next=cur1;
    if(cur2)
        tail->next=cur2;
    struct ListNode* head=guard->next;
    free(guard);
    return head; 
}

题四:链表分割(尾插)

题目描述:现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

思路:<x的尾插到第一个链表;≥x的尾插到第二个链表;两个链表连接起来

在这里插入图片描述

struct ListNode* partition(struct ListNode* pHead, int x)
{
	//创建两个头结点
	struct ListNode* lesshead = (struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode* greaterhead = (struct ListNode*)malloc(sizeof(struct ListNode));
	if ((!greaterhead) || (!lesshead))
	{
		perror("partition");
		return NULL;
	}
	lesshead->next = NULL;
	greaterhead->next = NULL;
	
	struct ListNode* lesstail = lesshead, * greatertail = greaterhead;
	struct ListNode* cur = pHead;
	//尾插
	while (cur)
	{
		if (cur->val < x)
		{
			lesstail->next = cur;
			lesstail = cur;
			cur = cur->next;
		}
		else {
			greatertail->next = cur;
			greatertail = cur;
			cur = cur->next;
		}
	}
	lesstail->next = greaterhead->next;
	greatertail->next = NULL;
	struct ListNode* head = lesshead->next;

	//释放
	free(lesshead);
	free(greaterhead);
	
	return head;
}

—————————————————————————————————————@fantasy_13_7——————END——————

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畋坪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值