[数据结构]链表OJ

news2025/7/28 8:39:06

目录

数据结构之链表OJ::

                                     1.移除链表元素

                                     2.反转链表

                                     3.链表的中间结点

                                     4.链表中倒数第k个结点

                                     5.合并两个有序链表

                                     6.链表分割

                                     7.链表的回文结构

                                     8.相交链表

                                     9.环形链表

                                    10.环形链表II

                                    11.复制带随机指针的链表


数据结构之链表OJ::

1.移除链表元素

删除链表中等于给定值val的所有结点
struct ListNode
{
	int val;
	struct ListNode* next;
};
方法一
struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* cur = head, * prev = NULL;
	while (cur != NULL)
	{
		//1.头删
		//2.非头删
		if (cur->val == val)
		{
			if (cur == head)
			{
				head = head->next;
				free(cur);
				cur = head;
			}
			else
			{
				prev->next = cur->next;
				free(cur);
				cur = prev->next;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
	return head;
}
不用二级指针的方法:返回头指针
main函数调用方式:plist = removeElements(plist,6);
方法二
struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* cur = head;
	struct ListNode* newhead = NULL, * tail = NULL;
	while (cur != NULL)
	{
		if (cur->val != val)
		{
			if (tail == NULL)
			{
				newhead = tail = cur;
			}
			else
			{
				tail->next = cur;
				tail = tail->next;
			}
		}
		else
		{
			struct ListNode* del = cur;
			cur = cur->next;
			free(del);
		}
	}
	if (tail != NULL)
	{
		tail->next = NULL;
	}
	return newhead;
}
方法三:
struct ListNode* removeElements(struct ListNode* head, int val)
{
	struct ListNode* cur = head;
	struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
	struct ListNode* tail = guard;
	while (cur != NULL)
	{
		if (cur->val != val)
		{
			tail->next = cur;
			tail = tail->next;
			cur = cur->next;
		}
		else
		{
			struct ListNode* del = cur;
			cur = cur->next;
			free(del);
		}
	}
	最后结点是val 就会出现此问题
	if (tail != NULL)
	{
		tail->next = NULL;
	}
	head = guard->next;
	free(guard);
	return head;
}
替代我们之前实现的二级指针
1.返回新的链表头 2.设计为带哨兵位

2.反转链表

反转链表
方法一:取结点头插到新链表
struct ListNode* reverseList(struct ListNode* head)
{
	struct ListNode* cur = head;
	struct ListNode* newhead = NULL;
	while (cur)
	{
		struct ListNode* next = cur->next;
		cur->next = newhead;
		newhead = cur;
		cur = next;
	}
	return newhead;
}
方法二:翻转链表方向
struct ListNode* reverseList(struct ListNode* head)
{
	struct ListNode* n1, *n2, *n3;
	n1 = NULL;
	n2 = head;
	n3 = NULL;
	while (n2)
	{
		n3 = n2->next;
		n2->next = n1;
		迭代
		n1 = n2;
		n2 = n3;
	}
	return n1;
}

3.链表的中间结点

链表的中间结点——快慢指针
struct ListNode* middleNode(struct ListNode* head)
{
	struct ListNode* slow, * fast;
	slow = fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

4.链表中倒数第k个结点

链表中倒数第k个结点——快慢指针
方法一:
struct ListNode* FindKthToTail(struct ListNode* pListHead, unsigned int k)
{
	struct ListNode* fast, * slow;
	fast = slow = pListHead;
	while (k--) 走k步
	{
		k大于链表长度
		if (fast == NULL)
			return NULL;
		fast = fast->next;
	}
	while (fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}
方法二:
struct ListNode* FindKthToTail(struct ListNode* pListHead, unsigned int k)
{
	struct ListNode* fast, * slow;
	fast = slow = pListHead;
	while (fast && --k)走k-1步
	{
		k大于链表长度	
		fast = fast->next;
	}
	if (fast == NULL)
		return NULL;
	while (fast->next)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

5.合并两个有序链表 

合并两个有序链表
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
	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 = cur1;
			cur1 = cur1->next;
		}
		else
		{
			tail->next = cur2;
			cur2 = cur2->next;
		}
		tail = tail->next;
	}
	if (cur1)
		tail->next = cur1;
	if (cur2)
		tail->next = cur2;
	struct ListNode* head = guard->next;
	free(guard);
	return head;
}

6.链表分割 

链表分割
struct ListNode* partition(struct ListNode* pHead, int x)
{
	struct ListNode* lessGuard, * lessTail, * greaterGuard, * greaterTail;
	lessGuard = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	greaterGuard = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	lessGuard->next = NULL;
	greaterGuard->next = NULL;
	struct ListNode* cur = pHead;
	while (cur)
	{
		if (cur->val < x)
		{
			lessTail->next = cur;
			lessTail = lessTail->next;
		}
		else
		{
			greaterTail->next = cur;
			greaterTail = greaterTail->next;
		}
		cur = cur->next;
	}
	lessTail->next = greaterGuard->next;
	greaterTail->next = NULL;
	pHead = lessGuard->next;
	free(greaterGuard);
	free(lessGuard);
	return pHead;
}

7.链表的回文结构 

链表的回文结构
方法一:找中间结点翻转链表
struct ListNode* middleNode(struct ListNode* head)
{
	struct ListNode* slow, * fast;
	slow = fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}
struct ListNode* reverseList(struct ListNode* head)
{
	struct ListNode* n1, * n2, * n3;
	n1 = NULL;
	n2 = head;
	n3 = NULL;
	while (n2)
	{
		n3 = n2->next;
		n2->next = n1;
		n1 = n2;
		n2 = n3;
	}
	return n1;
}
bool chkPalindrome(struct ListNode* head)
{
	struct ListNode* mid = middleNode(head);
	struct ListNode* rmid = reverseList(mid);
	while (head && rmid)
	{
		if (head->val != rmid->val)
			return false;
		head = head->next;
		rmid = rmid->next;
	}
	return true;
}
方法二:整个链表逆置看和链表是否相同 但要注意需要重新拷贝原链表

8.相交链表 

相交链表
判断是否相交判断尾结点的地址是否相同
找交点:求出长度lenA lenB 长的链表先走差距步 第一个相等的就是交点
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)
{
	if (headA == NULL || headB == NULL)
	{
		return NULL;
	}
	struct ListNode* curA = headA, * curB = headB;
	int lenA = 1;
	找尾结点
	while (curA->next)
	{
		curA = curA->next;
		++lenA;
	}
	int lenB = 1;
	while (curB->next)
	{
		curB = curB->next;
		++lenB;
	}
	if (curA != curB)
	{
		return NULL;
	}
	struct ListNode* longList = headA, * shortList = headB;
	if (lenA < lenB)
	{
		longList = headB;
		shortList = headA;
	}
	长的链表先走差距步
	int gap = abs(lenA - lenB);
	while (gap--)
	{
		longList = longList->next;
	}
	同时走找交点
	while (longList != shortList)
	{
		longList = longList->next;
		shortList = shortList->next;
	}
	return longList;
}

9.环形链表 

环形链表
快慢指针法:slow进环以后 fast开始追赶slow 
bool hasCycle(struct ListNode* head)
{
	struct ListNode* fast, * slow;
	fast = slow = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
			return true;
	}
	return false;
}
请证明一下,slow走一步 fast一次走两步?
请证明一下,slow走一步 fast一次走三步?是否可以? 答:不一定
请证明一下,slow走一步 fast一次走X步?是否可以?每次缩小X-1
请证明一下,slow走X步  fast一次走Y步?是否可以?X<Y 每次缩小X-Y
fast走两步时:假设slow进环以后 fast slow之间的差距为N 即追赶距离为N
slow和fast每移动一步 距离缩小1 距离缩小为N N-1 N-2...1 0 距离为0即相遇
fast走三步时:假设slow进环以后 fast slow之间的差距为N 即追赶距离为N
slow每追赶一次 它们之间距离缩小两步 距离变化为N N-2 N-4 N-6...
如果N为偶数则能追上 如果为奇数距离由1变为-1 意味着它们之间的距离变为了C-1(C是环的长度)
如果环减一是偶数再追一圈就能追上 如果环减一为奇数 则永远追不上

10.环形链表II

环形链表II
1.公式证明推导
2.转换成相交问题
fast走的距离 = 2*slow走的距离
假设进环前的长度是L
假设环的长度是C
假设入口点到相遇点距离是X
slow走的距离是L+X
fast走的距离是L+C=X
假设slow进环前 fast在环里面转了N圈 N>=1
2(L+X) = L+X+N*C
(L+X) = N*C
L = N*C-X
L = (N-1)*C+C-X
结论:一个指针A从头开始走 一个指针B从相遇点开始走 它们会在入口点相遇
1.公式证明推导
struct ListNode* detectCycle(struct ListNode* head)
{
	struct ListNode* slow = head, * fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
		{
			struct ListNode* meet = slow;
			while (meet != head)
			{
				meet = meet->next;
				head = head->next;
			}
			return meet;
		}
	}
	return NULL;
}
2.转换成相交问题
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)
{
	if (headA == NULL || headB == NULL)
	{
		return NULL;
	}
	struct ListNode* curA = headA, * curB = headB;
	int lenA = 1;
	找尾结点
	while (curA->next)
	{
		curA = curA->next;
		++lenA;
	}
	int lenB = 1;
	while (curB->next)
	{
		curB = curB->next;
		++lenB;
	}
	if (curA != curB)
	{
		return NULL;
	}
	struct ListNode* longList = headA, * shortList = headB;
	if (lenA < lenB)
	{
		longList = headB;
		shortList = headA;
	}
	长的链表先走差距步
	int gap = abs(lenA - lenB);
	while (gap--)
	{
		longList = longList->next;
	}
	同时走找交点
	while (longList != shortList)
	{
		longList = longList->next;
		shortList = shortList->next;
	}
	return longList;
}
struct ListNode* detectCycle(struct ListNode* head)
{
	struct ListNode* slow = head, * fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
		{
			转换成相交
			struct ListNode* meet = slow;
			struct ListNode* next = meet->next;
			meet->next = NULL;
			struct ListNode* entryNode = getIntersectionNode(head, next);
			恢复环
			meet->next = next;
			return entryNode;
		}
	}
	return NULL;
}

11.复制带随机指针的链表

 

复制带随机指针的链表
方法一:1.遍历原链表 复制结点 尾插
       2.更新random 找random原链表中第i个 新链表中对应第i个
方法二:1.拷贝原结点 链接到所有原结点的后面
      ps:原结点和拷贝结点建立一个链接关系 找到原结点就可以找到拷贝结点
       2.更新每个拷贝结点的random 
       3.将拷贝结点解下来 链接成新链表
struct Node
{
	int val;
	struct Node* next;
	struct Node* random;
};
struct Node* copyRandomList(struct Node* head)
{
	1.插入copy结点
	struct Node* cur = head;
	struct Node* copy = NULL;
	struct Node* next = NULL;
	while (cur)
	{
		next = cur->next;
		copy = (struct Node*)malloc(sizeof(struct Node));
		copy->val = cur->val;
		cur->next = copy;
		copy->next = next;
		cur = next;
	}
	2.更新copy->random
	cur = head;
	while (cur)
	{
		copy = cur->next;
		if (cur->random == NULL)
			copy->random = NULL;
		else
			copy->random = cur->random->next;
		cur = cur->next->next;
	}
	3.copy结点要解下来链接在一起 恢复原链表
	struct Node* copyHead = NULL, * copyTail = NULL;
	cur = head;
	while (cur)
	{
		copy = cur->next;
		next = copy->next;
		取结点尾插
		if (copyTail == NULL)
		{
			copyHead = copyTail = copy;
		}
		else
		{
			copyTail->next = copy;
			copyTail = copyTail->next;
		}
		恢复原链表链接
		cur->next = next;
		cur = next;
	}
	return copyHead;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/368018.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot自定义starter时使用@AutoConfigureBefore、@AutoConfigureAfter的细节问题

正常利用springboot的自动装配 ConfB Configuration(proxyBeanMethodsfalse) public class ConfB {public ConfB(){System.out.println("ConfB构造方式执行...");} }不加spring.factories 项目包结构 此时resources中没有spring.factories 执行结果 2023-02-24…

运动蓝牙耳机什么牌子好,运动蓝牙耳机品牌推荐

现在市面上运动耳机的品牌越来越多&#xff0c;还不知道选择哪一些运动耳机品牌&#xff0c;可以看看下面的一些耳机分享&#xff0c;运动耳机需要注意耳机的参数配置以及佩戴舒适度&#xff0c;根据自己最根本的使用需求来选择运动耳机。 1、南卡Runner Pro4骨传导蓝牙运动耳…

剑指 Offer 46. 把数字翻译成字符串

剑指 Offer 46. 把数字翻译成字符串 难度&#xff1a;middle\color{orange}{middle}middle 其实就是有条件的 青蛙跳格子 问题。 题目描述 给定一个数字&#xff0c;我们按照如下规则把它翻译为字符串&#xff1a;0 翻译成 “a” &#xff0c;1 翻译成 “b”&#xff0c;………

GitHub狂飙30K+star面试现场,专为程序员面试打造,现已开源可下载

《程序员面试现场》上线2个月已经在GitHub上已经狂飙到30Kstar&#xff08;能在在GitHub上拿到30K的star&#xff0c;有没有干货&#xff0c;我就不多说了&#xff09;总结的很全面&#xff0c;主要是针对面试之前的准备工作&#xff0c;分为知彼、知己、问答、贯通、综合五部分…

06- OpenCV查找图像轮廓 (OpenCV系列) (机器视觉)

知识重点 灰度图转换: gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)二值化: 返回两个东西&#xff0c;一个阈值&#xff0c; 一个是二值化的图: thresh, binary cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)查找轮廓: 返回两个结果&#xff0c;分别是轮廓和层级: c…

白帽黑客入行应该怎么学?零基础小白也能轻松上手!

这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 1为什么网络安全行业是IT行业最后的红利&#xff1f; 根据腾讯安全发布的《互联网安全报告》&#xff0c;…

Python每日一练(20230224)

目录 1. 列表奇偶拆分 ★ 2. 二叉树的后序遍历 ★★ 3. 接雨水 ★★★ 附录 二叉树 特点 性质 特殊二叉树 满二叉树 完全二叉树 完全二叉树性质 二叉树的遍历 1. 列表奇偶拆分 【问题描述】 输入一个列表&#xff0c;包含若干个整数&#xff08;允许为空&#xff…

Spring Batch 高级篇-并行步骤

目录 引言 概念 案例 转视频版 引言 接着上篇&#xff1a;Spring Batch 高级篇-多线程步骤&#xff0c;了解Spring Batch多线程步骤后&#xff0c;接下来一起学习一下Spring Batch 高级功能-并行步骤 概念 并行步骤&#xff0c;指的是某2个或者多个步骤同时执行。比如下…

Ask林曦|来回答,30个你关心的日常问题(二)

在林曦老师的线上书法直播课上&#xff0c;上课前后的聊天时间里&#xff0c;时常有同学向林曦老师提问&#xff0c;这些问题涵盖了日常生活的诸多方面&#xff0c;从身体的保养&#xff0c;到快乐的法门&#xff0c;皆是大家感兴趣的&#xff0c;也都共同关切的。   暄桐教室…

python+Vue学生作业系统 django课程在线学习网站系统

系统分为学生&#xff0c;教师&#xff0c;管理员三个角色&#xff1a; 学生功能&#xff1a; 1.学生注册登录系统 2.学生查看个人信息&#xff0c;修改个人信息 3.学生查看主页综合评价&#xff0c;查看今日值班信息 4.学生在线申请请假信息&#xff0c;查看请假的审核结果和请…

180、【动态规划】leetcode ——583. 两个字符串的删除操作:两种动态规划思路(C++版本)

题目描述 原题链接&#xff1a;583. 两个字符串的删除操作 解题思路 &#xff08;1&#xff09;基于求最长公共子序列思路 本题与 1143. 最长公共子序列 的区别在于&#xff0c;1143中求的是两个序列中的最长公共子序列&#xff0c;而本题是要找到最少删除多少个元素后可以得…

PHP程序员适合创业吗?

创业是一件自然而然的事&#xff0c;不需要人为选择。 只要你是一个努力能干主动的人&#xff0c;当你在一个行业深耕5年之后&#xff0c;就会发现人生发展的下一步就是创业。当然如果行业合适的话。 什么叫行业合适呢&#xff1f; 就是创业的成本并不那么高&#xff0c;不需…

怎么在LinkedIn领英安全添加到3万个好友?

根据领英最新公布的数据&#xff1a;领英全球用户数已经达到8.3亿&#xff0c;超5800万个公司主页&#xff0c;可以说是世界上最-大的business database。 这就不难理解为什么越来越多的外贸人&#xff0c;开始认真尝试和重视在领英开发客户&#xff0c;因为领英确实是外贸人&a…

鸿蒙3.0 APP混合开发闪退问题笔记

APP采用cordova混合开发&#xff0c; 鸿蒙2.0以及安卓操作系统正常使用&#xff0c;但是在鸿蒙3.0中出现APP闪退&#xff0c;对APP进行真机调试发现&#xff0c;鸿蒙3.0系统对crosswork插件存在兼容问题&#xff0c;这些问题会导致APP页面加载失败&#xff0c;进而导致App闪退测…

扬帆优配|“涨停敢死队”慌了?监管“盯紧”异常交易

日前&#xff0c;沪深买卖所发布《主板股票反常买卖实时监控细则》&#xff0c;对反常买卖行为的类型和标准作出规则。其间&#xff0c;针对“打板”“封板”等反常行为的监控遭到商场重视&#xff0c;有商场传闻称&#xff0c;新规或导致高频买卖毁灭&#xff0c;“量价型股票…

什么蓝牙耳机打游戏好?打游戏好用的无线蓝牙耳机

午休或是周末约上好友玩两局游戏&#xff0c;是忙里偷闲的快乐时刻&#xff0c;对于普通游戏玩家&#xff0c;其实耳机够用就行&#xff0c;下面就分享几款打游戏好用的蓝牙耳机。 一、南卡小音舱蓝牙耳机 蓝牙版本&#xff1a;5.3 推荐系数&#xff1a;五颗星 南卡小音舱li…

【代码随想录二刷】Day24-回溯-C++

代码随想录二刷Day24 今日任务 理论基础 77.组合 语言&#xff1a;C 理论基础 解决的问题 ① 组合问题&#xff1a;不考虑顺序 ② 切割问题 ③ 子集问题 ④ 排列问题&#xff1a;考虑顺序 ⑤ 棋盘问题&#xff1a;N皇后&#xff0c;解数独回溯法三部曲 ① 回溯函数模板返回…

ChatGPT来了,软件测试工程师距离失业还远吗?

小伙伴们前一段是不是都看到过ChatGPT的相关视频&#xff0c;那它到底是什么&#xff1f;对软件测试行业会有什么影响&#xff1f; 今天汇智妹就用一篇文章来给大家讲清楚。 一、ChatGPT是什么&#xff1f; 简单来说&#xff0c;ChatGPT是一款人工智能聊天机器人&#xff0c;…

【Spring中@Autowired和@Resource注解的区别?】

一.背景 Spring中Autowired和Resource注解的区别&#xff1f; Spring框架想必大家都知道吧&#xff0c;那么Spring中Autowired和Resource注解的区别你知道吗&#xff1f;如果不知道也不要紧&#xff0c;我们就一起来学习一起吧。 二.Autowired和Resource注解的区别&#xff1f…

【人工智能 AI 】您可以使用机器人流程自动化 (RPA) 实现自动化的 10 个业务流程:Robotic Process Automation (RPA)

摘:人类劳动正在被机器(例如在工业中)或计算机程序(适用于所有行业)所取代。 目录 10 processes you can robotise in your company您可以在公司中实现自动化的 10 个流程 Human employees or robotic workers?人类员工还是机器人工人? Robots take over headhunting…