移除链表元素
https://leetcode.cn/problems/remove-linked-list-elements/
题意:删除链表中等于给定值 val 的所有节点。
链表基础题目,没啥好说的,注意生成虚拟头结点,然后进行删除。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyhead = new ListNode(0);
dummyhead -> next = 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;
}
}
return dummyhead -> next;
}
};
设计链表
https://leetcode.cn/problems/design-linked-list/
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
![](https://img-blog.csdnimg.cn/img_convert/d6d267232ae78a06a6ac6a49d2fd9f14.png)
这道题主要考虑了链表制造的方方面面,注意虚拟头结点不算第0个节点,其他都比较常规(注意链表size的变化!)
![](https://img-blog.csdnimg.cn/img_convert/4c09832eaa8b91808e286b94b23a6315.webp?x-oss-process=image/format,png)
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
size = 0;
dummyhead = new LinkedNode(0);
}
int get(int index) {
if(index < 0 || index > (size - 1)) 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 != NULL){
cur = cur -> next;
}
cur -> next = NewNode;
size++;
}
void addAtIndex(int index, int val) {
LinkedNode* NewNode = new LinkedNode(val);
if(index < 0 ) index = 0;
LinkedNode* cur = dummyhead;
if(index > size) return;
while(index-- ){
cur = cur -> next;
}
NewNode->next = cur->next;
cur->next = NewNode;
size++;
}
void deleteAtIndex(int index) {
if(index < 0 || index > size - 1) return;
LinkedNode* cur = dummyhead;
while(index-- ){
cur = cur -> next;
}
LinkedNode* tmp = cur->next;
cur -> next = cur -> next -> next;
delete tmp;
size--;
}
private:
LinkedNode* dummyhead;
int size;
};
反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
https://leetcode.cn/problems/reverse-linked-list/
用双指针法,让一个指针指向头结点,一个指向null,通过指针让头结点的指针指向null;这样完成了第一个节点指向null的动作,然后两个指针统一右移,也就是null的节点到头结点,头结点到下一个节点,继续循环。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = NULL;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
};
两两交换链表中的节点
https://leetcode.cn/problems/swap-nodes-in-pairs/
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
![](https://img-blog.csdnimg.cn/img_convert/2940321158f1eba5aebfc2b3d2aea228.png)
通过画图解决。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
return dummyHead->next;
}
};
删除链表的倒数第N个节点
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
思路:这道题考察了双指针的经典用法,也就是使用双指针的快慢指针间距进行删除特定元素。
举个例子说,链表删除倒数第四个节点,也就是链表[12345678]里面的5,这个时候我们使用一个快慢指针,分别指向1和指向6,这样当指向6的指针指向空的时候,指向1的指针指向4,就可以删除第4-> next个数值,也就是删掉5。而1和6之间间隔5,即n+1.
代码如下:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0);
dummyhead ->next = head;
ListNode* FastIndex = dummyhead;
ListNode* SlowIndex = dummyhead;
while( n--){
FastIndex = FastIndex -> next;
}
FastIndex = FastIndex -> next;
while(FastIndex != NULL ){
SlowIndex = SlowIndex -> next;
FastIndex = FastIndex -> next;
}
ListNode* tmp = SlowIndex -> next;
SlowIndex -> next = SlowIndex -> next -> next;
delete tmp;
return dummyhead -> next;
}
};
链表相交
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思路很简单,目前知道两个链表a和b,先用while求出链表各自长度,然后长的那个肯定要从第index个(也就是index后的长度和短的那个相等的地方)开始遍历。里面有个小难点就是我们默认curA为最长链表的头,lenA为其长度,如果B更长,那么使用swap把a和b的长度和指针换一下。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
环形链表II
https://leetcode.cn/problems/linked-list-cycle-ii/
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
这道题背过,公式推导后结果是:
定义一个快慢指针,快的走两步,慢的走一步,快的和慢的一定会在环形区域相遇(前提是有环形区域),然后再相间的地方定义快的指针,从头结点定义慢的指针,两个一起遍历,相遇的地方就是我们要找的入环的第一个节点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};