一.移除链表元素
题意:删除链表中等于给定值 val 的所有节点。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
解题思路:
解法一:不使用虚拟头节点
此时需要考虑两种情况:第一种情况是删除的元素就是头节点,第二种情况是删除的元素不是头节点。
删除某个元素,先要找到他的前一个元素,然后将前一个元素的next指向要删除元素的下一个节点。
解法二:使用虚拟头节点
创建一个虚拟头节点,使得虚拟头节点的next指向原先的head,删除元素的方法与上面相似。删除完之后,让head指向虚拟头节点的next,释放虚拟头节点即可。
解法一代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) { // 注意这里不是if
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next!= NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
解法二代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0);//创建一个虚拟头节点
dummyHead->next = head;// 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL)
{
if (cur->next->val == val)
{
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
}else
{
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
二.反转链表
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
解题思路:
方法一:双指针法
使用两个指针,定义第一个指针为cur,指向链表的头节点,由于我们是反转操作。头节点最后会变成尾节点,头节点最后会指向NULL,因此定义一个pre节点为NULL。根据下图,我们可以看出cur和pre节点会不断的向右移动,cur->next会变成pre完成链表反转的操作。然后将cur和pre一起向右移动。当pre移动到最后一个节点的时候,cur节点指向为空。因此while循环的判断条件为cur指向为空时,结束循环。反转完毕以后,pre为头节点,将其返回即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* temp = NULL;
while (cur)//cur=NULL时,pre指向尾元素,此时结束循环
{
temp = cur->next; //保存的是未反转链表的下一个节点
cur->next = pre;//反转操作,将当前节点的next指向前一个结点
pre = cur;//将pre和cur右移
cur = temp;
}
return pre;
}
};
解法二:递归法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverse(ListNode* cur, ListNode* pre)
{
if (cur == NULL) return pre;
ListNode* temp = cur->next;//下一个cur的位置
cur->next = pre;//改变指向
return reverse(temp,cur);//将temp赋值给原来的cur,将cur赋值给原来的pre;cur变成了下一个cur,pre变成原来的cur
}
ListNode* reverseList(ListNode* head) {
return reverse(head, NULL);
}
};
三.两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
解题思路:
按题目的意思,是将相邻的两个元素进行交换,从第一和第二两个元素交换开始,我们发现,需要第三个节点的参与才可以完成交换,因此考虑引入一个虚拟头节点。引入一个虚拟头节点后,按照下图所示总计分为三个步骤:第一步是将虚拟头节点的next指向第二个节点,第二步是将第二个节点的next指向第一个节点(此处第一与第二个节点发生了位置交换),第三步是将原来第一个节点的next指向第三个节点,至此,前两个节点的顺序发生转换。
操作后的链表顺序如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *dummyHead = new ListNode(0);//设置一个虚拟头节点
ListNode* cur = dummyHead;
dummyHead->next = head;
while (cur->next != NULL && cur->next->next !=NULL)/*第一个条件限制偶数个元素,第二个条件限制奇数个元素的链表。
两个判断条件的顺序不能改变,把第二个判断条件放到前面可能会造成空指针操作*/
{
ListNode*temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = cur->next->next;//步骤一
cur->next->next = temp1;//步骤二
cur->next->next->next = temp2;//步骤三
cur = cur->next->next;// cur移动两位,准备下一轮交换// cur移动两位,准备下一轮交换
}
return dummyHead->next;
}
};
四.删除链表倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解题思路:
我们要删的是倒数第n个节点,所以我们**要先找到倒数第n+1个节点,才能删除第n个节点。**采用双指针的方式进行操作,先让快指针走n+1步,然后快慢指针同时移动,直到快指针指向了末尾NULL,然后删除慢指针所指向的下一个节点,完成。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* buddyHead = new ListNode(0);
ListNode* fastIndex = buddyHead;
ListNode* slowIndex = buddyHead;
buddyHead->next = head;
n += 1;//防止n超过链表长度,导致对空指针操作
while (n-- && fastIndex!=NULL)
{
fastIndex = fastIndex->next;
}
while(fastIndex != NULL)
{
fastIndex = fastIndex->next;
slowIndex = slowIndex->next;
}
slowIndex->next = slowIndex->next->next;
return buddyHead->next;
}
};
五.环形链表
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
解题思路:
首先第一点:需要判断链表是否有环。采用双指针法,定义快慢指针slow与fast。fast比slow走的快,如果有环,那么fast与slow有可能相遇。定义快指针一次走两步,慢指针一次走一步。
第二点:是找到这个环的入口。假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
当slow与fast相遇的时候,slow一共走了x+y,fast一共走了x+y+n*(z+y);fast可能已经在环里面走了好几圈才遇上slow,n为fast在环内所走的圈数。
因为fast一次走两步,slow一次走一步,因此fast的路程一定是slow路程的两倍,因此可以得到这样的等式:2*(x+y)=x+y+n*(z+y),进一步化简x+y=n*(z+y);fast让一圈之后x=(n-1)(y+z)+y+z-y;
最后得到x=(n-1)(y+z)+z;当n=1时,x=z;当n大于1时,也是一样的结果,只是fats在环里面多转了好几圈罢了,x与z的值始终相等。
这个时候分别定义两个指针:一个指向head,一个指向fast与slow的相遇点,两个指针同时移动,每次移动一步,当两个指针相遇的时候,此时的下标就是环的入口。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
{
ListNode* indexFast = fast;
ListNode* indexSlow = head;
while(indexFast != indexSlow)
{
indexFast = indexFast->next;
indexSlow = indexSlow->next;
}
return indexFast;
}
}
return NULL;
}
};