目录
前面介绍了单链表(无头单向非循环链表)结构和操作,那么我们该如何好好利用所学的知识解决问题呢,光说不练假把式,接下来让我们破解一些练习题吧!
前言:
对于咱们对编程有兴趣或者成为陌生手段来说,刷题一般用牛客网和力扣
一、移除链表元素
1.1思路
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
创建一个新链表(newTail,newHead)置为空,创建一个指针pcur指向头节点head。
开始遍历pcur不为空,判断如果pcur的值不等于val:
如果newHead为空,新链表等于pcur;
不为空则newTail下一个节点等于pcur,newTail下移一个节点。
pcur下移。
如果newTail不为空,则newTail下一个节点为空。
返回newHead。
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
//创建新链表
ListNode* newHead,*newTail;
newHead = newTail = NULL;
//遍历原链表
ListNode* pcur = head;
while(pcur)
{
//找值不为val的节点,往新链表中进行尾插
if(pcur->val != val)
{
//链表为空
if(newHead == NULL)
{
newHead = newTail = pcur;
}
//链表不为空
else
{
newTail->next = pcur;
newTail = newTail->next;
}
}
pcur=pcur->next;
}
if(newTail)
newTail->next=NULL;
return newHead;
}
二、反转链表
1.1思路
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
判断head如果为空,则返回head。
创建三个指针n1=NULL,n2=head,n3=n2->next。
遍历n2不为空:n2->next=n1,n1=n2,n2=n3。
判断n3不为空,n3=n3->next。
最后返回n1。
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
//处理空链表
if(head == NULL)
{
return head;
}
//创建三个指针
ListNode* n1,*n2,*n3;
n1=NULL,n2=head,n3=n2->next;
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
n3=n3->next;
}
//此时n1就是链表反转后的新的头结点
return n1;
}
三、链表的中间结点
1.1思路
给你单链表的头结点 head
,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
使用快慢指针,创建2个指针slow , fast。
slow走一格,fast走两格。
当fast==NULL&&fast->==NULL时slow正好在中间。
返回slow。
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
//创建慢指针slow,慢指针fast
ListNode*slow=head,*fast=head;
//慢指针走一步,快指针走两步
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
//此时slow正好在中间
return slow;
}
四、合并两个有序链表
1.1思路
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
当list1或list2有一个为空时,直接返回另一个
创立一个新链表,创立两个头指针l1=list1,l2=list2,
创建两个空指针newHead和newTail指向为空。
l1和l2比大小,小的进newTali,然后newTail向下移动一格节点
最后比完大小如果l1没了l2剩一个,那么newTali的下一个节点就是l2,反之
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
//处理链表为空情况
if(list1 == NULL)
{
return list2;
}
if(list2 == NULL)
{
return list1;
}
//创建一个新链表newHead,newTail
ListNode*newHead=NULL,*newTail=NULL;
//创立两个指针l1,l2分别指向两个链表的头节点
ListNode*l1=list1,*l2=list2;
while(l1 && l2)
{
if(l1->val < l2->val)
{
//l1插入新链表
if(newHead == NULL)
{
newHead=newTail=l1;
}
else
{
newTail->next=l1;
newTail=newTail->next;
}
l1=l1->next;
}
else
{
//l2插入新指针
if(newHead == NULL)
{
newHead=newTail=l2;
}
else
{
newTail->next=l2;
newTail=newTail->next;
}
l2=l2->next;
}
}
//跳出循环只有两种情况:要么l1为空(l2不为空),要么l2为空(l1不为空)
if(l1)
{
newTail->next=l1;
}
if(l2)
{
newTail->next=l2;
}
return newHead;
}
五、链表分割
1.1思路
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
创建一个头指针pcur存储pHead;创建俩个非空链表,小节点less,大节点Great.
<X存入less反之;大节点尾GreatTail->NULL.
合并两个链表,释放两个链表的头节点,创建一个指针ret储存结果链表的头lessHead.
返回ret.
1.2代码实现
#include <cstddef>
#include <cstdlib>
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
//创建俩个非空链表
ListNode* lessHead, *lessTail;
lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));
ListNode* GreatHead, *GreatTail;
GreatHead = GreatTail = (ListNode*)malloc(sizeof(ListNode));
//创建一个头指针
ListNode* pcur = pHead;
while (pcur) {
if (pcur->val < x) {
lessTail->next = pcur;
lessTail = lessTail->next;
} else {
GreatTail->next = pcur;
GreatTail = GreatTail->next;
}
pcur = pcur->next;
}
GreatTail->next = NULL;
//合并链表
lessTail->next = GreatHead->next;
//创建一个新链表
ListNode* ret = lessHead->next;
free(lessHead);
free(GreatHead);
lessHead = GreatHead = NULL;
return ret;
}
};
六、链表的回文结构
1.1思路
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900
1.2代码实现
class PalindromeList {
public:
ListNode*findMidNode(ListNode*phead)
{
ListNode*slow=phead;
ListNode*fast=phead;
while (fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
ListNode*reverseList(ListNode*phead)
{
ListNode*n1,*n2,*n3;
n1=NULL,n2=phead,n3=n2->next;
while (n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
{
n3=n3->next;
}
}
return n1;
}
bool chkPalindrome(ListNode* A)
{
// write code here
ListNode*mid=findMidNode(A);
ListNode*right=reverseList(mid);
ListNode*left=A;
while (right)
{
if (left->val != right->val)
{
return false;
}
left=left->next;
right=right->next;
}
return true;
}
};
七、相交链表
1.1思路
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
ListNode* l1 = headA;
ListNode* l2 = headB;
int sizeA=0,sizeB=0;
while(l1)
{
sizeA++;
l1=l1->next;
}
while(l2)
{
sizeB++;
l2=l2->next;
}
//求绝对值
int gap =abs(sizeA-sizeB);
//让长链表先走gap步
ListNode*longList = headA;
ListNode*shortList = headB;
if(sizeA < sizeB)
{
longList = headB;
shortList = headA;
}
while(gap--)
{
longList = longList->next;
}
//此时longList和shortList指针在同一起跑线
while(longList && shortList)
{
if(longList == shortList)
{
//链表相交
return longList;
}
//继续往后走
longList = longList->next;
shortList = shortList->next;
}
//链表不相交
return NULL;
}
八、环形链表I
1.1思路
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
1.2代码实现
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head)
{
//快慢指针
ListNode*slow = head;
ListNode*fast = head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow == fast)
{
return true;
}
}
//两个指针没有相遇
return false;
}
九、环形链表II
1.1思路
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
创建快慢节点slow,fast
当两节点相遇时,创建一个头指针pcur,
如果相遇点slow没有和pcur相等,则都下走一个节点
相遇返回pcur
1.2代码实现
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head)
{
ListNode*slow=head;
ListNode*fast=head;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(slow == fast)
{
ListNode*pcur=head;
while(pcur != slow)
{
pcur=pcur->next;
slow=slow->next;
}
return pcur;
}
}
return NULL;
}