Leetcode C++ 链表
Leetecode 206
206. Reverse Linked List
Reverse a singly linked list.
Example:
Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
- 思路:
首先设定两个指针,pre和curr,翻转指针就是将curr的next指向pre,但是由于链表是单向的,因此反转后就无法获得下一个指针,所以还需要设定第三个指针next存储curr翻转前的下一个指针。
ListNode* reverseList(ListNode* head) {
if (!head) // head = nullptr
return head;
ListNode* pre = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = pre;
// move
pre = curr;
curr = next;
}
return pre;
}
Leetecode 92
92. Reverse Linked List II
Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ≤ m ≤ n ≤ length of list.
Example:
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
- 思路:
使用链表轮转的方法解决链表翻转的问题。
ListNode* reverseBetween(ListNode* head, int m, int n) {
// for m = 1
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* prev = dummy;
ListNode* curr = prev->next;
ListNode* next = curr->next;
// start node id = m (start from 1)
for (int id = 1; id < m; ++id) {
prev = prev->next;
curr = curr->next;
next = next->next;
}
// rotate
for (int id = 0; id < n - m; ++id) {
curr->next = next->next;
next->next = prev->next;
prev->next = next;
next = curr->next;
}
return dummy->next;
}
Leetecode 86
86. Partition List
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
Example:
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
- 思路:
使用轮换方法,将小于x的节点轮换到左边,保持顺序不变。由于使用轮换所以需要3个指针。
start:指向最后一个大于等于x的node;
prev:指向最后一个小于x的node;
then:指向新发现的小于x的node;
- 条件1:当start值小于x时,3个指针同时前移
- 条件2:当start值大于等于x时,此时判断then的值,如果then的值同样大于等于x时同时后移;
- 条件3:如果then值小于x,那么进行轮换操作;
- 后处理:考虑轮换后start是否为最后节点,如果不是最后节点,那么还要考虑start下一节点的值来判断start是否需要后移;
ListNode* partition(ListNode* head, int x) {
// 如果当前List是空或只有一个node,直接返回
if (!head || !head->next)
return head;
// 构造一个虚拟节点
ListNode* dummy = new ListNode();
dummy->next = head;
// 因为使用轮转方法,所以需要prev, start和the指针
ListNode* prev = dummy;
ListNode* start = prev->next;
ListNode* then = start->next;
while (then) {
// 条件1(只有初始时才可能满足):start值小于x,那么所有指针向后移动
if (start->val < x) {
prev = prev->next;
start = start->next;
then = then->next;
} else if (start->val >= x && then->val >= x) { // 条件2:此时prev不移动,start和then后移
start = start->next;
then = then->next;
} else if (start->val >= x && then->val < x) { // 条件3:此时进行轮换操作
// 轮换操作
start->next = then->next;
then->next = prev->next;
prev->next = then;
// 操作后处理,要考虑轮换后start是否为最后节点
prev = prev->next;
if (start->next) { // 轮换后如果start的next节点存在
if (start->next->val >= x) // 如果start的next存在,根据值大小判断start是否后移
start = start->next;
then = start->next;
} else // start是最后一个节点
then = nullptr;
}
}
ListNode* res = dummy->next;
delete dummy;
return res;
}
Leetecode 328
328. Odd Even Linked List
Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.
You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.
Example 1:
Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL
- 思路:
使用两个指针交替指向对方的next然后再前进一步的方法,因为连接后even链表再尾部,因此终止条件设置为even本身或其next为nullptr时终止。由于要连接两个链表,所以要首先保存被连接的链表的第一个元素的指针。
ListNode* oddEvenList(ListNode* head) {
if (!head || !head->next)
return head;
ListNode* odd = head;
ListNode* even = head->next;
ListNode* temp = even; // for link odd and even finally
while (even && even->next) {
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = temp;
return head;
}
Leetecode 2
2. Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example 1:
Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.
- 思路:
- 使用dummy head指向存储结果的新List的头指针,可以统一循环中指针向后移动的语句;
- 在while开始处new ListNode,保证新List长度和l1或l2相同,便于后期处理;
- 注意循环结束后如果flag位是1,那么需要添加新的节点,存储flag位;
- 补上nullptr;
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* res = new ListNode();
ListNode* start = res;
int flag = 0;
int new_val = 0;
while (l1 || l2) {
start->next = new ListNode();
start = start->next;
int l1_val = l1 ? l1->val : 0;
int l2_val = l2 ? l2->val : 0;
int sum = l1_val + l2_val + flag;
new_val = sum % 10;
flag = sum / 10;
start->val = new_val;
if (l1)
l1 = l1->next;
if (l2)
l2 = l2->next;
}
if (flag) {
start->next = new ListNode();
start = start->next;
start->val = flag;
}
start->next = nullptr;
return res->next;
}
Leetecode 445
445. Add Two Numbers II
You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Follow up:
What if you cannot modify the input lists? In other words, reversing the lists is not allowed.
Example:
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
- 思路:
- 将list压入栈中,实现元素反转;
- 弹出栈然后相加,注意增加flag存储溢出(类似Leetcode 2);
- 如果存储结果到List中,那么需要反转结果List;如果存储结果到stack中,那么通过结果stack再直接构造List即可。
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// push into stack
stack<int> s1, s2, res;
while (l1) {
s1.push(l1->val);
l1 = l1->next;
}
while (l2) {
s2.push(l2->val);
l2 = l2->next;
}
/*
ListNode* dummy = new ListNode();
ListNode* curr = dummy;
*/
// add
int flag = 0;
int sum = 0;
while (!s1.empty() || !s2.empty()) {
//curr->next = new ListNode();
//curr = curr->next;
int s1_val = s1.empty() ? 0 : s1.top();
int s2_val = s2.empty() ? 0 : s2.top();
int temp = s1_val + s2_val + flag;
sum = temp % 10;
flag = temp / 10;
//curr->val = sum;
res.push(sum);
if (!s1.empty())
s1.pop();
if (!s2.empty())
s2.pop();
}
if (flag) {
/*
curr->next = new ListNode();
curr = curr->next;
curr->val = flag;
*/
res.push(flag);
}
//curr->next = nullptr;
/*
// reverse result
curr = dummy->next;
ListNode* next = curr->next;
while (next) {
curr->next = next->next;
next->next = dummy->next;
dummy->next = next;
next = curr->next;
}
return dummy->next;
*/
// create result list by res stack
ListNode* res_dummy = new ListNode();
ListNode* res_curr = res_dummy;
while (!res.empty()) {
res_curr->next = new ListNode();
res_curr = res_curr->next;
res_curr->val = res.top();
res.pop();
}
res_curr->next = nullptr;
return res_dummy->next;
}
Leetecode 82
82. Remove Duplicates from Sorted List II
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
Return the linked list sorted as well.
Example 1:
Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:
Input: 1->1->1->2->3
Output: 2->3
- 思路1:
因为设计节点的删除,所以首先构造虚拟头指针,并分别设置prev、curr和next指针。
分所有情况讨论,条件分别为:curr与next是否相邻、next是否为空、curr值与next值是否相等。
因为值相等需要next不能为空,因此将curr与next是否相邻作为最外层条件。
ListNode* deleteDuplicates(ListNode* head) {
if (!head || !head->next)
return head;
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* prev = dummy;
ListNode* curr = prev->next;
ListNode* next = curr->next;
/*
1. curr and next are adjcent && next is null;
2. curr and next are adjcent && next is not null && curr->val = next->val;
3. curr and next are adjcent && next is not null && curr->val != next->val;
4. curr and next are NOT adjcent && next is null;
5. curr and next are NOT adjcent && next is not null && curr->val = next->val;
6. curr and next are NOT adjcent && next is not null && curr->val != next->val;
*/
while (curr) {
if (curr->next == next) {// curr and next are adjcent
if (!next) { // next is nullptr (1)
curr = curr->next; // curr is nullptr now
} else { // next is NOT nullptr
if (curr->val == next->val) { // curr->val = next->val (2)
next = next->next;
} else { // curr->val != next->val (3)
prev = prev->next;
curr = curr->next;
next = next->next;
}
}
} else { // curr and next are NOT adjcent
if (!next) { // next is nullptr (4)
prev->next = next;
ListNode* del = curr;
while (del != next) {
curr = curr->next;
delete del;
del = curr;
}
} else { // next is NOT nullptr
if (curr->val == next->val) { // curr->val = next->val (5)
next = next->next;
} else { // curr->val != next->val (6)
prev->next = next;
ListNode* del = curr;
while (del != next) {
curr = curr->next;
delete del;
del = curr;
}
next = next->next;
}
}
}
}
return dummy->next;
}
*思路2:
这个思路比较简单,首先建立一个ListNode虚拟节点,然后通过判断head与head->next是否相同,相同则head向后移动,不同则使用虚拟节点将head串起来。
实现可参考连接.
Leetecode 24
24. Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head.
You may not modify the values in the list’s nodes. Only nodes itself may be changed.
Example1:
Input: head = [1,2,3,4]
Output: [2,1,4,3]
Example 2:
Input: head = []
Output: []
Example 3:
Input: head = [1]
Output: [1]
- 思路:
- 交换List中两节点(curr和next);
- 交换后prev和curr节点指针向后移动两位;
- 判断终止条件,就是要保证带交换的两个节点(curr和next)都不能为null;
ListNode* swapPairs(ListNode* head) {
if (!head || !head->next)
return head;
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* prev = dummy;
ListNode* curr = dummy->next;
ListNode* next = curr->next;
while (true) {
// exchange
prev->next = next;
curr->next = next->next;
next->next = curr;
prev = curr;
curr = prev->next;
// terminal condition
if (!curr)
break;
if (!curr->next)
break;
next = curr->next;
}
return dummy->next;
}
Leetecode 25
25. Reverse Nodes in k-Group
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes, in the end, should remain as it is.
Follow up:
Could you solve the problem in O(1) extra memory space?
You may not alter the values in the list’s nodes, only nodes itself may be changed.
Example 1:
Input: head = [1,2,3,4,5], k = 2
Output: [2,1,4,3,5]
Example 2:
Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]
Example 3:
Input: head = [1,2,3,4,5], k = 1
Output: [1,2,3,4,5]
Example 4:
Input: head = [1], k = 1
Output: [1]
- 思路:
总体还是通过链表的轮转方法,注意进行反转的前提条件。
- 首先检测是否满足反转的前提条件,待反转的节点不为空(也是反转结束的条件),通过设置一个check指针来判断;
- 使用连边的轮转方法,轮转k-1次,也就实现了k个节点的反转;
- 将prev指针向前移动至start,然后循环进行判断和k-1次轮转;
ListNode* reverseKGroup(ListNode* head, int k) {
if (!head || !head->next || k==1)
return head;
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* prev = dummy;
ListNode* start = nullptr;
ListNode* next = nullptr;
ListNode* check = nullptr;
while (true) {
// check for reverse
check = prev;
for (int i = 0; i < k; ++i) {
if (!check->next)
return dummy->next;
check = check->next;
}
start = prev->next;
next = start->next;
// reverse
for (int i = 0; i < (k - 1); ++i) {
start->next = next->next;
next->next = prev->next;
prev->next = next;
// next rotate
next = start->next;
}
// next group
prev = start;
}
return dummy->next;
}
Leetecode 147
147. Insertion Sort List
Sort a linked list using insertion sort.
Algorithm of Insertion Sort:
(1) Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list.
(2) At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there.
(3) It repeats until no input elements remain.
- 思路:
- 定义一个新列表,并使用一个虚拟节点dummy指向该列表,初始为nullptr;
- 使用insert指针指向head列表中待插入的节点,并将该节点从head列表中取出成独立节点;
- 使用start指针从dummy开始向后遍历结果列表,比较start->next和insert,当发现比insert节点值大时,将insert节点插入到start和start->next节点之间;
- 当遍历结束时,如果插入条件始终不满足,那么将insert节点作为结果列表最后一个节点进行插入;
ListNode* insertionSortList(ListNode* head) {
if (!head || !head->next)
return head;
ListNode* dummy = new ListNode(INT_MIN);
dummy->next = nullptr;
ListNode* start = dummy;
ListNode* insert = head;
while (insert) {
// 从dummy重新遍历结果列表
start = dummy;
// 将insert节点从head列表中取出,作为一个单独节点
head = head->next;
insert->next = nullptr;
while (start->next) {
if (start->next->val > insert->val) { // 插入条件3
insert->next = start->next;
start->next = insert;
break;
} else {
start = start->next;
}
}
if (!start->next) { // 插入条件4
insert->next = start->next;
start->next = insert;
}
res;
}
return dummy->next;
}
Leetecode 148
148. Sort List
Given the head of a linked list, return the list after sorting it in ascending order.
Follow up: Can you sort the linked list in O(n logn) time and O(1) memory (i.e. constant space)?
- 思路:
使用链表的归并排序算法,额外使用一个节点。
- 将一个列表分割为两个,使用快慢指针方法;
- 实现将两个列表从小到大排序,merge为一个列表;
- 递归使用
// 排序归并
ListNode* mergeList(ListNode* l1, ListNode* l2) {
ListNode* l = new ListNode();
ListNode* start = l;
while (l1 && l2) {
if (l1->val < l2->val) {
start->next = l1;
l1 = l1->next;
} else {
start->next = l2;
l2 = l2->next;
}
start = start->next;
}
if (l1)
start->next = l1;
if (l2)
start->next = l2;
ListNode* res = l->next;
delete l;
return res;
}
// 分割列表为两部分
ListNode* sortList(ListNode* head) {
if (!head || !head->next)
return head;
// 使用快慢指针来获取列表中间位置节点
ListNode* fast = head;
ListNode* slow = head;
// find mid list
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
ListNode* right = slow->next;
slow->next = nullptr;// 将列表分为两个列表
// 递归
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(right);
return mergeList(l1, l2);
}
Leetecode 237
237. Delete Node in a Linked List
Write a function to delete a node in a singly-linked list. You will not be given access to the head of the list, instead you will be given access to the node to be deleted directly.
It is guaranteed that the node to be deleted is not a tail node in the list.
Example 1:
Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.
- 思路:
直接删除指定的链表中的节点,方法时将下一个节点复制到待删除的节点,然后删除下一个节点。
注意:该删除方法无法删除链表的最后一个节点!!
void deleteNode(ListNode* node) {
if (!node)
return;
if (!node->next) {
delete node;
node = nullptr;
}
ListNode* del = node->next;
node->val = del->val;
node->next = del->next;
delete del;
}
Leetecode 19
19. Remove Nth Node From End of List
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Follow up: Could you do this in one pass?
Example 1:
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
- 思路:
- 首先构造出两个节点start和end,二者之间的间距n个节点;
- 同时移动两个节点,直到end指向列表末尾,此时start指向倒数第n个节点;
- 删除start节点即可。
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* prev = dummy;
ListNode* start = head;
ListNode* end = start;
// 拉开start和end之间的距离满足n个节点
for (int i = 0; i < n - 1; ++i) {
if (!end->next)
throw runtime_error("length of list less than n!\n");
end = end->next;
}
// start和end同时向后移动,直到end达到列表末尾
while (end->next) {
prev = prev->next;
start = start->next;
end = end->next;
}
// 输出start节点(倒数第n个节点)
prev->next = start->next;
delete start;
return dummy->next;;
}
Leetecode 61
61. Rotate List
Given the head of a linked list, rotate the list to the right by k places.
Example 1:
Input: head = [1,2,3,4,5], k = 2
Output: [4,5,1,2,3]
Example 2:
Input: head = [0,1,2], k = 4
Output: [2,0,1]
- 思路:
- 获得链表的长度count,并获取一个指向最后一个节点的指针start
- 计算实际转动的次数: k = count - k % count
- 将链表转换为一个环(start->next = head)
- 通过start指针向前移动k次,得到prev指针
- 返回prev->next作为头指针
ListNode* rotateRight(ListNode* head, int k) {
if (!head || !head->next)
return head;
int count = 1;
ListNode* start = head;
while(start->next) {
start = start->next;
count++;
}
// make a circle
start->next = head;
// move prev pointer
k = count - (k % count);
ListNode* prev = start;
for (int i = 0; i < k; ++i) {
prev = prev->next;
}
ListNode* ret = prev->next;
prev->next = nullptr;
return ret;
}
Leetecode 143
143. Reorder List
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
You may not modify the values in the list’s nodes, only nodes itself may be changed.
Example 1:
Given 1->2->3->4, reorder it to 1->4->2->3.
Example 2:
Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
- 思路
- 寻找中间节点(快慢指针),将列表分割为两个列表
- 反转第二个列表
- 将两个列表进行merge操作
void reorderList(ListNode* head) {
if (!head || !head->next)
return;
// find mid node and split into two lists
ListNode* prev = new ListNode();
prev->next = head;
ListNode* mid = head;
ListNode* end = head;
while (true) {
// odd number
if (!end->next) {
break;
}
// even number
if (!end->next->next) {
prev = prev->next;
mid = mid->next;
break;
}
// move to next
prev = prev->next;
mid = mid->next;
end = end->next->next;
}
prev->next = nullptr; // split into two lists
// reverse mid list
ListNode* dummy = new ListNode();
dummy->next = mid;
ListNode* next = mid->next;
while (next) {
mid->next = next->next;
next->next = dummy->next;
dummy->next = next;
next = mid->next;
}
mid = dummy->next;
// merge
ListNode* start = head;
while (start && mid) {
dummy->next = start;
start = start->next;
dummy->next->next = mid;
mid = mid->next;
dummy = dummy->next->next;
}
if (start)
dummy->next = start;
if (mid)
dummy->next = mid;
}
Leetecode 234
234. Palindrome Linked List
Given a singly linked list, determine if it is a palindrome.
Example 1:
Input: 1->2
Output: false
Example 2:
Input: 1->2->2->1
Output: true
- 思路:
- 找到中间节点(快慢指针)
- 反转第二个List
- 逐一对比两个List的元素是否相同
bool isPalindrome(ListNode* head) {
if (!head || !head->next)
return true;
// find mid node
ListNode* fast = head;
ListNode* low = head;
while (true) {
if (!fast->next)
break;
if (!fast->next->next)
break;
low = low->next;
fast = fast->next->next;
}
ListNode* mid = low->next;
// reverse
ListNode* dummy = new ListNode();
dummy->next = mid;
ListNode* next = mid->next;
while (next) {
mid->next = next->next;
next->next = dummy->next;
dummy->next = next;
next = mid->next;
}
// compare
mid = dummy->next;
while (mid) {
if (mid->val != head->val)
return false;
mid = mid->next;
head = head->next;
}
return true;
}