由于单链表本身还存在在缺陷,所以很多OJ题会深挖这些缺陷让问题很多的小明进行解决。本篇文章从力扣和牛客网上选取了以下题目进行题目解答分享:移除链表元素,链表的中间结点,合并两个有序链表,反转链表。
如果对单链表有问题的小明,希望数据结构之单链表能够解答你的困惑。
目录
移除链表元素
思路:在题目分析后,我们知道题目要求我们删除链表中所有的指定元素。
在一开始看到这道题目,第一步我想的是使用一个指针进行对链表元素的遍历,找到指定的元素后直接free。
但是考虑到链表的特性,如果直接删除的话,那么链表原来的连接结构会断连。所以本题需要定义两个指针cur和prev,cur用于对数据的遍历删除,prev则要对cur进行保存操作防止链表的断连。
题解代码:
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode*cur = head;
struct ListNode*prev = NULL;
while(cur)
{
if(cur->val==val)
{
if(cur==head)
{
cur = head->next;
free(head);
head = cur;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else{
prev = cur;
cur = cur->next;
}
}
return head;
}
在代码中我们注意到了对cur在头结点单独进行处理,若不单独处理在cur = prev->next时,prev已经是一个空指针。
链表的中间节点
思路:由题目可知我们要得到链表的中间结点且分两种情况,一种是一个中间结点,一种是有两个中间节点,此时要返回二者中的后一个结点,这两种情况由结点个数的奇偶决定,要返回中间节点,则改变头结点的位置,需要移动的次数为n/2.
代码如下
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode*cur = head;
struct ListNode*prev = head;
int count=0;
while(cur)
{
count++;
cur = cur->next;
}
int n = count/2;
while(n--)
{
head= head->next;
}
return head;
}
合并两个有序链表
思路:我首先想到的是以一个链表为基准,但是发现这样操作起来链表得重新连接很麻烦。于是我决定重新定义头和尾,创建一个新的链表进行填入尾插操作。 很多问题的小明问,指向一个list,list后续的结点不用管了吗?小明啊,该去复习复习链表的结构咯。指向链表的一个结点,由于链表的连接性,后面的结点也是一起连接过来滴。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
struct ListNode*head = NULL,*tail=NULL;
if(list1==NULL&&list2 ==NULL)//先对两个链表进行判空操作
return NULL;
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
while(list1&&list2)
{
if(list1->val<list2->val)
{ if(tail==NULL)//尾结点为空无法尾插
{
head =tail =list1;
}
else
{
tail->next =list1;//尾插,将原来的尾与新尾连接
tail = tail->next;//插入的值定为新尾
}
list1 = list1->next;
}
else{
if(tail==NULL)
{
head =tail =list2;
}
else{
tail->next =list2;
tail =tail->next;
}
list2 = list2->next;
}
}
if(list1)
{
tail->next =list1;
}
if(list2)
{
tail->next = list2;
}
return head;
}
反转链表
思路:由题目介绍可以知道本题要求我们改变链表中各结点的指向,即将结点指向翻转过来。如下图。
为实现上述改变,我们首先考虑到用两个指针,用n1代表空,头结点为n2,使n2指向n1,即使原来的头指向NULL变成了尾结点。但是这个时候会造成一个问题,当我们想通过n2往后走找到2结点时,1和2已经失去了连接无法找到结点2如下图。所以我们考虑用三个指针,两个指针n1与n2用于反转,n3则用于保存结点的位置。
如下图,使用三个指针进行链表的反转。用n3保存n2->next的结点位置 。
当三个指针处于如下图状态时,停止移动。
实现代码如下。
struct ListNode* reverseList(struct ListNode* head) {
if(head ==NULL)
{
return NULL;
}
struct ListNode*n1 = NULL;
struct ListNode*n2 = head;
struct ListNode*n3 = n2->next;
while(n2)//n2为空时停止移动
{
n2->next = n1;//反转链表指向
n1 = n2;
n2 = n3;
if(n3)//n3会先走到空,当n3=NULL时无法进行移动。
n3 = n3->next;
}
return n1;
}
总结
在做有关链表相关题目时,一定要进行图像的分析!!!其次所有有关链表的问题,要对链表是否为空、链表是否只有一个头结点进行分析。有关链表的合并通常直接开辟新的链表更加快捷方便。在后续的文章中,我还为问题的很多的小明同学分享有关单链表的题目,关注我,细听下回分解。