移除一个节点之后,要养成手动清理内存的习惯。
设置一个虚拟头结点在进行移除节点操作
ListNode* dummy=new ListNode(0);//设置一个虚拟头结点
dummy->next=head;//将虚拟头结点指向head
ListNode* cur=dummy;//cur指针指向虚拟头结点
释放多余的节点
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy=new ListNode(0);//设置一个虚拟头结点
dummy->next=head;//将虚拟头结点指向head
ListNode* cur=dummy;//cur指针指向虚拟头结点
while(cur->next!=NULL){
if(cur->next->val==val){
ListNode* temp=cur->next;//将待删除的节点暂时保留下来
cur->next=temp->next;
delete temp;
}
else cur=cur->next;
}
return dummy->next;
delete dummy;
}
};
这道题目设计链表的五个接口:
- 获取链表第index个节点的数值
- 在链表的最前面插入一个节点
- 在链表的最后面插入一个节点
- 在链表第index个节点前面插入一个节点
- 删除链表的第index个节点
注意事项:
//链表节点结构体定义
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val),next(NULL){}
};
//初始化链表(疑惑:这里初始化为什么不用定义数据类型。在后面的private)
MyLinkedList() {
dummy=new ListNode(0);//虚拟头结点
size=0;
}
和后面的搭配使用
private:
int size;
ListNode* dummy;
在第index位置添加或者删除节点时 (因为index是从0开始的)
ListNode* cur=dummy;//直接在cur定义在dummy处,避免了从0出发的不方便
class MyLinkedList {
public:
//链表节点结构体定义
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val),next(NULL){}
};
//初始化链表(疑惑:这里初始化为什么不用定义数据类型。在后面的private)
MyLinkedList() {
dummy=new ListNode(0);//虚拟头结点
size=0;
}
//获取第index节点的值,index从0开始
int get(int index) {
if(index>(size-1) || index<0) return -1;
ListNode* cur=dummy;//定义一个指针指向虚拟头结点,避免一开始head就是空的
while(index){
cur=cur->next;
index--;
}
return cur->val;
}
//在链表头部添加节点
void addAtHead(int val) {
ListNode* temp=new ListNode(val);//先创建一个新节点
//以下两行就是插入节点temp
temp->next=dummy->next;
dummy->next=temp;
//插入节点之后,size增多
size++;
}
//在链表尾部添加节点
void addAtTail(int val) {
ListNode* temp=new ListNode(val);//先创建一个新节点
ListNode* cur=dummy;//直接在cur定义在dummy处,避免空指针的引用
while(cur->next!=NULL){
cur=cur->next;
}
cur->next=temp;
size++;
}
//在第index个节点之前添加节点
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
void addAtIndex(int index, int val) {
if(index > size) return;//index大于链表长度
ListNode* temp=new ListNode(val);//先创建一个新节点
ListNode* cur=dummy;//直接在cur定义在dummy处,避免了从0出发的不方便
while(index){
cur=cur->next;
index--;
}
temp->next=cur->next;
cur->next=temp;
size++;
}
//删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if(index>(size-1) || index<0) return;
ListNode* cur=dummy;//直接在cur定义在dummy处,避免了从0出发的不方便
while(index){
cur=cur->next;
index--;
}
ListNode* temp=cur->next;
cur->next=temp->next;
delete temp;
size--;
}
private:
int size;
ListNode* dummy;
};
如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。
其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表
双指针法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre=NULL;//定义一个pre,初始化为NULL
ListNode* cur=head;//定义一个指针,初始化指向头结点
while(cur){
ListNode* temp=cur->next;//用一个临时节点来保存cur->next,因为后面的操作会改变cur
cur->next=pre;//改变指针指向
pre=cur;//改变pre指向
cur=temp;//改变cur指向
}
return pre;
}
};
递归法
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
//递归终止条件
if(cur==NULL) return pre;
ListNode* temp=cur->next;//暂时保存cur的下一节点,因为后面cur会发生变化
cur->next=pre;//翻转操作
return reverse(cur,temp);//下一递归的pre=cur,下一递归的cur=temp
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL,head);
}
};
快慢指针指向头结点。
让快指针先走n步
判空(此时fast指针可能指向NULL),故得先判空,然后返回head->next
fast和slow同时移动,直到fast走到链表末尾
删除节点
返回head
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL) return NULL;
//定义快慢指针指向头结点
ListNode* slow=head;
ListNode* fast=head;
while(n){
fast=fast->next;
n--;
}
if(fast==NULL) return head->next;//提前判空。例如只有一个元素,且n=1
while(fast->next){
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return head;
}
};
模拟法
初始时,cur指向虚拟头结点 。接着进行三个步骤,自己画图模拟一下。否则指针容易搞错。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
//if(head==NULL) return NULL;
ListNode* dummy=new ListNode(0);//设置虚拟头结点
dummy->next=head;//虚拟头结点和链表接起来
ListNode* cur=dummy;//cur指针指向虚拟头结点
while(cur->next && cur->next->next){
ListNode* tempA=cur->next;
ListNode* tempB=cur->next->next->next;//保存临时节点
cur->next=tempA->next;
cur->next->next=tempA;
tempA->next=tempB;
cur=tempA;
}
return dummy->next;
}
};
求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。
双指针模拟,curA一开始指向headA,curB一开始指向headB,大循环while(CurA!=CurB) 。curA走一圈链表A,走到头就转向链表B(也就是CurA=headB);curB同理。直到curA=CurB
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA=headA;
ListNode* curB=headB;
while(curA!=curB){
if(curA) curA=curA->next;
else{
curA=headB;
}
if(curB) curB=curB->next;
else{
curB=headA;
}
}
return curA;
}
};
双指针模拟法
1.定义slow,fast指针,都指向head节点
2.制造第一次相遇。slow一次走一步,fast一次走两步,如果fast或者fast->next为空,表明不成环,返回NULL(提前判断)
3.制造第二次相遇。fast指向头,slow在第二步的位置。两者都走一步,直到两者相遇
4.返回
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow=head;
ListNode* fast=head;
while(true){
if(fast==NULL || fast->next==NULL) return NULL;//没有环
slow=slow->next;
fast=fast->next->next;
if(slow==fast) break;//第一次相遇
}
fast=head;//第一次相遇之后,让fast指向head
while(slow!=fast){//等第二次相遇
slow=slow->next;
fast=fast->next;
}
return slow;//返回相遇处的节点
}
};