前言
📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL。
📚本文收录与初阶数据结构系列,本专栏主要是针对时间、空间复杂度,顺序表和链表、栈和队列、二叉树以及各类排序算法,持续更新!
📚相关专栏C++及Linux正在发展,敬请期待!
1.链表OJ题
1.1 第一题
第一题链接:链表分割
题目描述:
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
题目分析:
这道题呢解法很多,大部分同学想到的呢就是把小于x结点的放前面,大于X的放后面,但是注意, 题目有一个要求是不能改变原数据的顺序,所以呢,需要考虑其他方法。
我在这个地方给大家提供一个很快速的方法:
为什么用带哨兵位(带头的链表),主要是因为可以不用判断链表是不是为空,在后面链接greater链表的时候也更加方便。
代码实现:
ListNode* partition(ListNode* pHead, int x)
{
struct ListNode* lesshead , *lesstail,*greaterhead,*greatertail;
lesshead = lesstail = (struct ListNode *)malloc(sizeof(struct ListNode));
greaterhead = greatertail = (struct ListNode *)malloc(sizeof(struct ListNode));
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 = greaterhead->next;
greatertail->next = NULL;
pHead = lesshead->next;
free(lesshead);
free(greaterhead);
return pHead;
}
大家记住,就是哨兵位知识一个站位的,不存储有效数据。是否使用带头的大家也不要死记硬背,而是根据实际情况来决定。
1.2 第二题
第二题链接:链表的回文结构
题目描述:
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
回文是什么意思呢?就是是否对称,中间结点前面的链表的数值和后面的是否相同,相同就返回Ture,否则就返回False。这题应该怎么做?有些同学想到将数值取出来放在数列中,然后左右两边同时走最后,走完了全部相同就判断为真,这样可不可以,其实是可以的,但是这道题有要求空间复杂度为O(1),那怎么办?
题目分析:
其实核心就是,要想到逆置字符串,一起来看下代码
代码实现:
bool chkPalindrome(ListNode* head) {
//找到中间点然后逆置
//找中间点
struct ListNode * rmid = head;
struct ListNode * fast = head;
struct ListNode * slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
rmid = slow;
struct ListNode * n1 = NULL;
struct ListNode * n2 = rmid;
struct ListNode * n3 = rmid->next;
while(n3)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3)
n3=n3->next;
}
rmid = n2;
while(rmid)
{
if(head->val != rmid->val)
return false;
else
{
head = head->next;
rmid = rmid->next;
}
}
return true;
}
1.3 第三题
第三题链接:相交链表
题目描述:
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
题目分析:
这道题比较复杂,要仔细听。
代码实现:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
struct ListNode * tailA = headA;
struct ListNode * tailB = headB;
int lenA = 1;
int lenB = 1;
while(tailA->next)
{
tailA = tailA->next;
lenA++;
}
while(tailB->next)
{
tailB = tailB->next;
lenB++;
}
if(tailA != tailB)
{
return NULL;
}
struct ListNode* longlist = headA;
struct ListNode* shortlist = headB;
if(lenA < lenB)
{
longlist = headB;
shortlist = headA;
}
int dif = abs(lenA-lenB);
while(dif--)
{
longlist = longlist->next;
}
while(longlist)
{
if(longlist != shortlist)
{
longlist = longlist->next;
shortlist = shortlist->next;
}
else
{
break;
}
}
return longlist;
}
1.4 第四题
第四题链接:环形链表
题目描述:
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
题目分析:
很多同学认为,带环链表循环无法结束,不带环链表可以结束,那用循环判断就可以了呀,不需要其他方法,但是带环链表的循环是死循环,程序无法结束。那么我们一起来分析一下,
上图是什么意思呢,是这样,首先定义快慢指针,快指针一次走两步,慢指针一次走一步,等到慢进链表时候快指针就开始追赶慢指针,直到最后追上慢指针结束。只要是能追上就是带环,不能追上就不带环。
代码实现:
bool hasCycle(struct ListNode *head)
{
struct ListNode * slow = head;
struct ListNode * fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
return true;
}
return false;
}
1.5 第五题
第五题链接:环形链表Ⅱ
题目描述:
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表
题目分析:
实现代码:
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode * slow = head;
struct ListNode * fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
{
struct ListNode* meet = fast;
while(head != meet)
{
head = head->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}
1.6 为什么第四题和第五题可以这么玩,深度剖析
对于第四题来说,我想问问两个问题
1、fast和slow一定会相遇吗? 答案:一定能相遇
2、fast必须比slow,多走n步可以吗?(n=2,3,4,5,...) 答案:不一定可以
解答:
1、如果fast比slow多走一步,就一定会相遇,为什么?看图
2.多走n步可以吗?
还有一个问题呢是为什么第五题可以这么玩?相遇点凭什么一定能遇到的头结点?凭什么能返回相遇点的地址,他和链表入口处是一样的吗?
总结
1、其实OJ题并不难,难的就是一些思路以及算法这方面可能见得少了没想到,大家一定要多联系多思考
2、需要把链表学的比较好一些,因为这个是后面的基础,下一讲给大家更新带头双向循环链表。
如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言