一、链表定义
c++链表节点定义方式:
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
自己定义构造函数初始化节点:
ListNode *head=new ListNode(5)
使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;
链表存储方式:地址随机分布
链表种类:
- 单链表
- 双链表
- 循环链表
链表操作:
- 删除节点:head->next=head->next->next
- 添加节点:
链表和数组性能对比分析:
一、Leetcode题目
1.移除链表元素
题目链接:203.移除链表元素
移除链表元素需要考虑删除元素位置有头结点和中间节点,其中处理头结点是比较麻烦的事,一般采取设置一个哑结点去处理头结点。
ListNode *dummy=new ListNode(0);
dummy->next=head;
ListNode *cur=dummy;
1.先初始化一个哑结点。
2.让哑结点指向head。
3.最后用cur指针等于哑结点进行while语句的循环。
整体代码:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head==nullptr)
return nullptr;
ListNode *dummy=new ListNode(0);
dummy->next=head;
ListNode *cur=dummy;
while(cur->next)
{
if(cur->next->val==val)
{
cur->next=cur->next->next;
}
else{
cur=cur->next;
}
}
return dummy->next;
}
};
while(cur->next)判断如果遇到空节点即为NULL,退出循环。
重要的事情说三遍!
最后返回的是dummy->next!
最后返回的是dummy->next!
最后返回的是dummy->next!
为什么不返回cur?
cur一直在移动,最后cur的位置为链表最后一个节点(不是尾节点),此时返回cur只会返回一个节点。
为什么不返回head呢?
我们用哑结点的原因就是防止头结点是我们需要删除的节点,因此如果head是我们需要删掉的节点,那么不可以返回。
2.反转链表
双指针法
用pre指针和cur指针完成操作,并声明一个tmp临时指针。
在原链表上进行翻转,注意以下几点:
1.尾节点指向的NULL需要重新初始化一个pre指针使其等于NULL。
2.更新pre和cur指针。
3.用tmp指针储存cur->next。先储存再翻转。
4.最后返回的是pre。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr)
{
return nullptr;
}
ListNode *pre=nullptr;
ListNode *cur=head;
while(cur){
ListNode *tmp=cur->next;
cur->next=pre;
pre=cur;
cur=tmp;
}
return pre;
}
};
3.两两交换链表中的节点
1使用哑结点,返回dummy->next.
2.while循环条件为cur->next和cur->next->next不为空。cur初始为哑结点
3.cur->next=?
cur->next->next=?
cur->next->next->next=?
最后再更新cur。
注意事项:不要想着一直去更新cur的位置,合理使用指针域next,在完成一次交换后再更新cur。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==nullptr||head->next==nullptr)
{
return head;
}
ListNode *dummy=new ListNode(0);
dummy->next=head;
ListNode *cur=dummy;
while(cur->next&&cur->next->next)
{
ListNode *temp_1=cur->next;
ListNode *temp_2=cur->next->next->next;
cur->next=cur->next->next;
cur->next->next=temp_1;
cur->next->next->next=temp_2;
cur=temp_1;
}
return dummy->next;
}
};
4.删除链表的倒数第 N 个结点
快慢指针法
- 先让fast走n次,求倒数节点位置,就先用fast指针走正数n次
- fast再走一次,方便slow指针进行删除
- slow和fast同时走直到fast走到null位置。这时slow位置即为total-n,即为倒数n个节点。
- 防止将head删掉,使用dummyhead,返回dummyhead->next;
- while循环条件(n–)当括号内为0即退出循环。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummyhead=new ListNode(0);
ListNode *slow=dummyhead;
ListNode *fast=dummyhead;
dummyhead->next=head;
while(n--&&fast){
fast=fast->next;
}
fast=fast->next;
while(fast){
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return dummyhead->next;
}
};