[声明]:ACWing Y总课程 总结
2009年408真题——408元年的数据结构算法题!!!
输入 / 输出样例
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
------------------------
输入:head = [1], n = 1
输出:[]
/**
* 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) {
auto dummy = new ListNode(-1);
dummy->next = head; // 创建虚拟头结点, 并指向head
// 虚拟头结点目的是为了防止头结点被删掉
auto first = dummy, second = dummy;\
while(n --) first = first->next;
while(first->next){ // first 为 nullptr 退出
first = first->next;
second = second->next;
}
second->next = second->next->next; // delete
return dummy->next; // 虚拟头结点的下一个结点一定是头结点, 且预防了头结点被删除
}
};
输入 / 输出样例
输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
思路:伪装成下一个节点,把下一个结点删除即可
代码如下:
class Solution {
public:
void deleteNode(ListNode* node) { // node是给定结点
node->val = node->next->val;
node->next = node->next->next;
}
};
class Solution {
public:
void deleteNode(ListNode* node) { // node是给定结点
*(node) = *(node->next); // 将结构体整段赋值
}
};
输入 / 输出样例
输入:head = [1,1,2,3,3]
输出:[1,2,3]
代码如下:
1.方法一:
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
auto dummy = new ListNode(-1);
dummy->next = head;
auto p = dummy;
while(p->next){
if(p->next->val == p->val){
p->next = p->next->next;
continue;
}
else{
p = p->next;
}
}
return head;
}
};
2.y总代码
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
auto p = head;
while(p){
if(p->next && p->next->val == p->val)
p->next = p->next->next;
else p = p->next;
}
return head;
}
};
输入 / 输出样例
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
代码如下:
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head) return nullptr;
int n = 0;
for(auto p = head; p; p = p->next) n++;
k %= n;
auto first = head, second = head;
while(k--) first = first->next;
while(first->next){
first = first->next;
second = second->next;
}
first->next = head;
head = second->next;
second->next = nullptr;
return head;
}
};
输入 / 输出样例
输入:head = [1,2,3,4]
输出:[2,1,4,3]
代码如下:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(!head) return nullptr;
auto dummy = new ListNode(-1);
dummy->next = head;
auto p = dummy;
while(p->next && p->next->next){
auto a = p->next;
auto b = a->next;
// 交换相邻结点
p->next = b;
a->next = b->next;
b->next = a;
p = a;
}
return dummy->next;
}
};
输入 / 输出样例
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
代码如下:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head) return nullptr;
auto a = head;
auto b = a->next;
while(b){
auto c = b->next;
b->next = a;
a = b;
b = c;
}
head->next = nullptr; // 头结点变为尾结点
return a; // a为新的头结点
}
};
输入 / 输出样例
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
代码如下
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(m == n) return head;
auto dummy = new ListNode(-1);
dummy->next = head;
auto a = dummy, d = dummy;
for(int i = 0; i < m-1; i++) a = a->next; // a为m-1位置的结点
for(int i = 0; i < n; i++) d = d->next; // d为n位置的结点
auto b = a->next, c = d->next;
// [模板]反转链表
auto p = b, q = b->next; // p q r为模板的a b c
while(q != c){ // 反转区间[b,d]
auto r = q->next;
q->next = p;
p = q;
q = r;
}
b->next = c;
a->next = d;
return dummy->next;
}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
auto p = headA, q = headB;
while(p != q){ // p q相遇时退出
if(p) p = p->next;
else p = headB; // 若p空, 则继续遍历B
if(q) q = q->next;
else q = headA; // 若q空, 则继续遍历A
}
return p;
}
};
∵ fast和slow一定会相遇,假设在c点相遇,b~c = y,C为环周长,n为圈数
fast距离: x + nC + y
slow距离: x+y
∵ x + nC + y = 2x + 2y
∴ x = nC -y
∴ x+y=nC
不妨设n = 1
∴ x+y = C
∴ c~b = x
即 c~b = a~b = x
∴ 将一个指针移到head,两个指针同时移动x距离后,一定在环的入口相遇。
得证!
代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
auto fast = head, slow = head;
while(slow){
fast = fast->next;
slow = slow->next;
if(slow) slow = slow->next;
else break;
if(fast == slow){
fast = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}
};
略难
代码如下
class Solution {
public:
ListNode* sortList(ListNode* head) {
int n = 0;
for(auto p = head; p; p = p->next) n++;
auto dummy = new ListNode(-1);
dummy->next = head;
for(int i = 1; i < n; i *= 2){ // 归并长度
auto cur = dummy;
for(int j = 0; j + i < n; j += i * 2){ // 每次扫描长度为 2*i 的归并段
// left为左归并段端点, right为右归并段端点
auto left = cur->next, right = cur->next;
for(int k = 0; k < i; k++) right = right->next; // right为第二个归并段的左端点
int l = 0, r = 0; // l r记录当前左/右归并段是否遍历完
// ★归并两段长度为 i 的区间
while(l < i && r < i && left && right){
if(left->val <= right->val){ // 归并排序 l_a <= r_b
cur->next = left;
cur = left;
left = left->next;
l ++;
}else{ // 归并排序 l_a > r_b
cur->next = right;
cur = right;
right = right->next;
r ++;
}
}
// 归并最后剩余的部分
while(l < i && left){
cur->next = left;
cur = left;
left = left->next;
l ++;
}
while(r < i && right){
cur->next = right;
cur = right;
right = right->next;
r ++;
}
cur->next = right;
} // for j
} // for i
return dummy->next;
}
};