链表OJ题练习1
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表OJ题练习1相关的知识,希望对你有一定的参考价值。
链表的中间结点(快慢指针)
链接:链表的中间结点
问题描述:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
分析:
- 定义快慢两个指针快指针走两部的同时,慢指针走一步
- 当快指针走到尾的时候慢指针位置指向的地址就是中间节点
注意(奇数和偶数个节点
)
代码如下
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
struct ListNode*slow=head;
struct ListNode*fast=head;
if(head==NULL)
{
return NULL;
}
while(fast!=NULL)
{
if(fast->next==NULL)
return slow;
slow=slow->next;
fast=(fast->next)->next;
}
return slow;
}
变形:输入一个链表,输出该链表中倒数第k个结点
链接:链表中倒数第k个结点
分析:
- 同样定义快慢两个指针,但是这里的快指针是先向后走k步,慢指针不变
- 再让快慢指针同时以相同速度走,快指针到尾,慢指针即为倒数第k个节点
代码如下
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pListHead ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast, *slow;
slow = fast = pListHead;
if(pListHead==NULL)
return NULL;
while(k--)
{
if(fast==NULL)
return NULL;
fast = fast->next;
}
while(fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
链表的分割
链接:链表分割
问题描述
现有一链表的头指针 ListNode pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针*。
分析:
- 创建两个链表,分别存放小于x的节点和大于等于x的节点,分别进行尾插
- 注意大链表的尾最后还会指向链表中的某一部分,形成环,所以注意断开链表
- 注意空指针,空链表的情况
代码如下
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
if(pHead==NULL)
return NULL;
struct ListNode*less=NULL;
struct ListNode*lesstail=NULL;
struct ListNode*bigger=NULL;
struct ListNode*biggertail=NULL;
struct ListNode*cur=pHead;
//放入大小两个链表再链接
while(cur!=NULL)
{
if(cur->val < x){
if(less==NULL)
{
less=cur;
lesstail=cur;
// lesstail=lesstail->next;
}
else{
lesstail->next=cur;
lesstail=lesstail->next;
}
}
else{
if(bigger==NULL)
{
bigger=cur;
biggertail=cur;
//biggertail=lesstail->next;
}
else{
biggertail->next=cur;
biggertail=biggertail->next;
}
}
cur=cur->next;
}
if(bigger!=NULL)
biggertail->next=NULL;
if(less==NULL)
{
return bigger;
}
if(bigger==NULL)
{
lesstail->next=NULL;
return less;
}
lesstail->next=bigger;
return less;
}
};
链表的回文结构
链接:链表的回文结构
问题描述:
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
测试样例:
1->2->2->1
返回:true
分析:
先用快慢指针找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
注意:必到被逆置的链表指向空为止即可
代码如下
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
struct ListNode* reverse(ListNode *phead){
struct ListNode *prev=NULL;
struct ListNode *cur=phead;
struct ListNode *latter;
while(cur)
{
latter=cur->next;
cur->next=prev;
prev=cur;
cur=latter;
}
return prev;
}
bool chkPalindrome(ListNode* A) {
struct ListNode *cur=A;
struct ListNode *fast=A;
struct ListNode *slow=A;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
struct ListNode *newhead=reverse(slow);
while(newhead)
{
if((cur->val)!=(newhead->val))
return false;
else{
cur=cur->next;
newhead=newhead->next;
}
}
return true;
}
};
相交链表
链接:相交链表
问题描述:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
进阶:
你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?
分析
:
- 先计算出两个链表的长度
- 让长的链表先走相差的长度
- 然后两个链表同时走,直到遇到相同的节点,即为第一个公共节点
代码如下:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int lenA = 0, lenB = 0;
struct ListNode* curA = headA, *curB = headB;
//计算链表长度
while(curA) {
++lenA;
curA = curA->next;
}
while(curB) {
++lenB;
curB = curB->next;
}
int gap = abs(lenA-lenB);
struct ListNode* longList = headA, *shortList = headB;
if(lenA < lenB) {
longList = headB;
shortList = headA;
}
//让长链表先走几步
while(gap--){
longList = longList->next;
}
//两个链表同时走,直到遇到相同的节点
while(longList && shortList)
{
if(longList == shortList) {
return longList;
}
else {
longList = longList->next;
shortList = shortList->next;
}
}
return NULL;
}
环形链表 II
链接:环形链表Ⅱ
问题描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
分析1(数学结论):
- 定义两个指针快指针每次走两步,慢指针走一步(最后两指针必相遇),
记录此时相遇位置
- 重新定义两个指针,一个从链表头开始走,一个从相遇位置开始走,
最后必相交在入口点
(链表开始入环的第一个节点)
以上是数学证明出的结论
详见:详解环形链表
分析2(普通方法):
- 定义两个指针快指针每次走两步,慢指针走一步(最后两指针必相遇),
记录此时相遇位置
- 重新定义两个指针,一个从链表头开始走,一个从相遇位置开始走,
最后必相交在入口点
(链表开始入环的第一个节点)
代码如下(数学结论)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast=head,*slow=head;
struct ListNode *judge;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
judge=fast;
break;
}
}
struct ListNode *first=head;
while(first&&first->next&&judge&&judge->next)
{
if(first==judge)
{
return first;
}
else
{
first=first->next;
judge=judge->next;
}
}
return NULL;
}
复制带随机指针的链表
链接:复制带随机指针的链表
问题描述:
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
分析:
- 拷贝链表的每一个节点,拷贝的节点先链接到被拷贝节点的后面
- 复制随机指针的链接:拷贝节点的随机指针指向被拷贝节点随机指针的下一个位置
- 拆解链表,把拷贝的链表从原链表中拆解出来,同时还原 原节点
代码如下
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
if(head==NULL)
return NULL;
struct Node*cur=head;
while(cur)
{
struct Node*next=cur->next;
struct Node*newcur=(struct Node*)malloc(sizeof(struct Node));
cur->next=newcur;
newcur->next=next;
newcur->val=cur->val;
cur=next;
}
cur=head;
while(cur)
{
struct Node*next=cur->next;
if(cur->random!=NULL)
next->random=cur->random->next;
else
next->random=NULL;
cur=next->next;
}
cur=head;
struct Node*newhead=cur->next;
while(cur->next->next)
{
struct Node*copy=cur->next;
struct Node*newcur=copy->next;
copy->next=newcur->next;
cur=newcur;
}
return newhead;
}
以上是关于链表OJ题练习1的主要内容,如果未能解决你的问题,请参考以下文章
数据结构之超硬核热门复杂度数组链表OJ题2W+文字+图片详解