24两两交换链表中的节点
19删除链表中倒数第N个节点
对于24题从这三张图就可以完美写出来了,一种迭代的思想,链表中迭代思想,很大程度需要建立一个虚拟的头节点。
迭代的重复的以上的三个步骤,就可以完成这个题目
/**
* 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) {
//我先设置一个判断就是如果他是空链表或者链表中只有一个元素,我就不需要交换了
if(head == NULL || head->next == NULL)
{
return head;
}
ListNode *dummyHead = new ListNode(0); //设置虚拟的头节点
dummyHead->next = head;
ListNode *cur = dummyHead;
ListNode *tmp;
//我刚开始写的while语句的时候判断语句只写了cur->next != NULL 但是leetcode的实例中链表中的节点个数会出现奇数个,例如出现3个前面两个交换后面那个不动,所以要判断他是否有右相邻的节点。
while(cur->next != NULL && cur->next->next != NULL)
{
ListNode *tmp2 = cur->next->next->next;
tmp = cur->next;
cur->next = tmp->next; // 步骤一
cur->next->next = tmp;//步骤二
cur->next->next->next = tmp2;//步骤三
cur = cur->next->next;
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
对于19题我的思想就是先遍历一遍链表长度,然后再遍历链表找到我们要删除节点的前一个位置,删除节点,使用虚拟头节点,这样方便对于头节点的删除,让删除操作都统一不用单独再分类。
/**
* 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 *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *cur = dummyHead;
int size = 0;//记录链表中有几个节点
while(cur != nullptr)
{
size++;
cur = cur->next;
}
if(size == 1)
{
return nullptr;
}
cur = dummyHead;
size = size-n-1;
while(size--)
{
cur = cur->next;
}
ListNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
return dummyHead->next;
}
};
题目中说可以尝试一下用一趟扫描实现,实现这样需要用到是双指针的思想一个快指针一个是慢指针。
1.初始化两个指针,都指向虚拟头节点位置
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *fast = dummyHead;
ListNode *slow = dummyHead;
2.fast首先走n+1步,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便操作)
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
3.fast和slow同时移动,直到fast指向末尾
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
我的代码思路和上面2和3有点出处,但是大概思想也是一样,我是让fast指针指到最后一个节点而不是指向NULL,然后我就不需要让fast指针提前移动n+1下,只需要移动n下就行,然后在slow和fast指针同时移动的时候,多加一个判断就是fast->next != NULL,这个判断出来fast指针是否到达最后一个节点了。
while(n-- && fast != nullptr)
{
fast = fast->next;
}
while(fast!=nullptr && fast->next != nullptr)
{
fast = fast->next;
slow =slow->next;
}
4.删除slow指向的下一个节点
ListNode *tmp = slow->next;
slow->next = tmp->next;
delete tmp;
head = dummyHead->next;
delete dummyHead;
return head;
这点需要注意的是,leetcode中不删除节点也能通过编译,但是在实际开发中,C++不像python,java有自动回收机制,C++需要手动释放,所以我们当开辟了一个新的区域的时候,需要手动删除,例如我们创建了一个虚拟节点,是在堆区开辟的内存,所以我们需要delete删除。