目录
(一)移除链表元素
(二)设计链表
(三)翻转链表
(四)两两交换链表中的节点
(五)删除链表倒数第n个节点
(六)链表相交
(七)环形链表Ⅱ
(一)移除链表元素
第一种是头结点需要单独处理的情况:
//处理头结点
while(head!=0&&head->val==val){
ListNode *p=head;
head=head->next;
delete p;
//处理其他节点
ListNode *q=head;
while(q!=0&&q->next!=0){
if(q->next->val==val){
ListNode *r=q->next;
q->next=q->next->next;
delete r;
}
else
q=q->next;
第二种是用虚拟头结点。相当于在原有链表前再添加一个模拟节点:
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
(二)设计链表
内容很多但不难,注意private用法*
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
// 初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp=nullptr;
_size--;
}
// 打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
(三)翻转链表
思路一:双指针
pre指向cur的前一位,目的是为了翻转后cur向前指。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *p=NULL;
ListNode*cur=head;
ListNode*mid;
while(cur!=NULL){//不是cur->next!=NULL因为这样会漏掉最后一个值
mid=cur->next;
cur->next=p;
p=cur;
cur=mid;
}
return p;//cur只会返回一个值,p有完整的链表
}
};
思路二:递归的想法(本质还是双指针)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
return reverse(NULL,head);
}
ListNode* reverse(ListNode *p,ListNode*cur){
ListNode*mid;
if(cur==NULL) {return p;}
mid=cur->next;
cur->next=p;
return reverse(cur,mid);
}
};
思路三:头插法(需要建立虚拟头结点)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *dummyHead=new ListNode(-1);
ListNode *cur=head;
while(cur!=NULL){
ListNode *mid=cur->next;
cur->next=dummyHead->next;
dummyHead->next=cur;
cur=mid;
}
return dummyHead->next;
}
};
思路四:用栈的特性(这里老报错,不知道为啥)
Stack<ListNode> stack = new Stack<>();
(四)两两交换链表中的节点(*)
这边画个图就能理解了,说实话第一次看代码给绕晕了。。。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode*dummyHead=new ListNode(0);
dummyHead->next=head;
ListNode *cur=dummyHead;
while(cur->next!=NULL&&cur->next->next!=NULL){
ListNode *temp=cur->next;
ListNode *temp1=cur->next->next->next;
cur->next=cur->next->next;
cur->next->next=temp;
cur->next->next->next=temp1;
cur=cur->next->next;
}
return dummyHead->next;
}
};
(五)删除链表倒数第n个节点
注意slow需要指向被删节点前一个节点,所以要n++。若在后面写fast=fast->next可能会出问题(当n超过区间范围时)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummyHead=new ListNode(0);
dummyHead->next=head;
ListNode *fast=dummyHead,*slow=dummyHead;
n++;
while(n--&&fast!=NULL){
fast=fast->next;
}
while(fast!=NULL){
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return dummyHead->next;
}
};
(六)链表相交
这里主要注意相同节点而不是相同数值
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p=headA;
ListNode *q=headB;
int lenA=0,lenB=0;
while(p!=NULL){
lenA++;
p=p->next;
}
while(q!=NULL){
lenB++;
q=q->next;
}
//这里需要再将两节点再指回头结点遍历,长的先移
p=headA;
q=headB;
if(lenA>lenB){//统一默认让b最长
swap(lenA,lenB);
swap(p,q);
}
int gap=lenB-lenA;
while(gap--){
q=q->next;
}
while(q!=NULL){
if(p==q)
{ return q;}
p=p->next;
q=q->next;
}
return NULL;
}
};
(七)环形链表Ⅱ
设置快慢指针,快指针速度为2,满指针速度为1,这样当快与慢相遇时一定有个环。这里有个追速度的问题,可以视慢指针不动,快指针以速度1的速度不断追击慢指针,所以一定会追到。如果快指针速度为3可能就碰不到了。
slow=x+y//x是起点到回路开端的距离,y是回路开端到碰撞节点的距离;
fast=x+y+n(z+y)//z=碰撞节点到回路开端的另外半圆的距离;
可以根据速度,写出等式:
2*slow=fast
2(x+y)=x+y+n(z+y)
当n=1时,可以推出x=z;
得出关键的信息,碰撞节点就是回路的开端节点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast=head;
ListNode *slow=head;
int pos=0;
while(fast!=NULL&&fast->next!=NULL){
slow=slow->next;
fast=fast->next->next;
if(slow==fast){
ListNode *index1=fast;
ListNode *index2=head;
while(index2!=index1){
index1=index1->next;
index2=index2->next;
}
return index2;
}
}
return NULL;
}
};
总结
记得回顾,有的容易忘