题目:
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
思路:
快慢指针:慢指针为头结点,快慢指针相差n,然后快指针到最后一个节点的时候,
慢指针刚好到倒数第n+1个节点,然后删除倒数n节点
注意:增加头结点,防止要删除首节点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummy = new ListNode(-1);
dummy->next = head;
ListNode *slow = dummy;
ListNode *fast = dummy;
while(n--) {
fast = fast->next;
}
while(fast != nullptr && fast->next != nullptr) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummy->next;
}
};
题目:
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,
则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
思路:
方法一:哈希表
可以通过哈希表记录,如果遇到哈希表里面有的就相当于第一个入环口
方法二:快慢指针
a为环外长度,b为入环口到相遇的长度,c为相遇到入环口长度
a + n * (b + c) + b = 2 * (a + b)第一次相遇的路程
得 a = (n - 1) * (b + c) + b;
所以 fast 一次一步再走 a 必然会遇到 slow
//快慢指针
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head;
ListNode *slow = head;
while(1) {//第一次相遇
//遇到空指针相当于不是循环,return
if(fast == nullptr || fast->next == nullptr)
return nullptr;
slow = slow->next;
fast = fast->next->next;
if(slow == fast) break;
}
fast = head;
while(fast != slow) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
题目:
给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
思路:
方法一:哈希表
把a加入哈希表,遍历b判断是否有节点在哈希表中,有则返回,无则不相交
方法二:双指针
a = a + b, b = b + a
如果a和b相交的话,在遍历对方那部分的时候就可以找到相交的节点
//双指针
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA == nullptr || headB == nullptr) return nullptr;
ListNode *curA = headA;
ListNode *curB = headB;
while(curA != curB) {
curA = curA == nullptr? headB : curA->next;
curB = curB == nullptr? headA : curB->next;
}
return curA;
}
};
题目:
给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
思路:
创建两个双指针,从头结点和首节点开始一直反转
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *cur = head;
while(cur != nullptr) {
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
题目:
给定两个 非空链表 l1和 l2 来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相> 加会返回一个新的链表。
可以假设除了数字 0 之外,这两个数字都不会以零开头。
输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
思路:
方法一:用两个栈(麻烦)
先用两个栈进行反转,相加,但最后还要反转链表
方法二:翻转链表
翻转链表后相加,再翻转
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
l1 = reverseList(l1);
l2 = reverseList(l2);
return reverseList(addList(l1, l2));
}
//链表相加
ListNode* addList(ListNode* l1, ListNode* l2) {
int ans = 0;
ListNode *node = new ListNode(-1);
ListNode *dummy = node;
while(l1 || l2) {
int x = 0, y = 0;
if(l1) {
x = l1->val;
l1 = l1->next;
}
if(l2) {
y = l2->val;
l2 = l2->next;
}
int num = (x + y + ans) % 10;
ans = (x + y + ans) / 10;
node->next = new ListNode(num);
node = node->next;
}
if(ans) {//进位的话再加上
node->next = new ListNode(ans);
node = node->next;
}
return dummy->next;
}
//翻转链表
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *cur = head;
while(cur) {
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
题目:
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln-1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入: head = [1,2,3,4]
输出: [1,4,2,3]
思路:
方法一:线性表
用线性表存储,然后用下表访问线性表,创建新链表
方法二:寻找链表中点 + 链表逆序 + 合并链表
//方法二
class Solution {
public:
void reorderList(ListNode* head) {
//快慢指针找中点
ListNode *fast = head;
ListNode *slow = head;//中点
while(fast != nullptr && fast->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
}
// 翻转后半部分
fast = nullptr;
while(slow != nullptr) {
ListNode *next = slow->next;
slow->next = fast;
fast = slow;
slow = next;
}
slow = head;
//slow为前半部分链表,fast为后半部分链表
while(slow || fast) {
if(slow) {
ListNode *next1 = slow->next;
slow->next = fast;
slow = next1;
}
if(fast) {
ListNode *next2 = fast->next;
fast->next = slow;
fast = next2;
}
}
}
};
题目:
给定一个链表的 头节点 head ,请判断其是否为回文链表。
如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。
输入: head = [1,2,3,3,2,1]
输出: true
思路:
class Solution {
public:
bool isPalindrome(ListNode* head) {
//寻找中点
ListNode *fast = head;
ListNode *slow = head;
while(fast != nullptr && fast->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
}
//翻转后半部分
fast = reverseList(slow);
slow = head;//前半部分
//判断是否回文
bool ok = true;
//fast <= slow, 所以while要用fast,下面证明:
//例如:[1,2,1] 此时 slow = 1 -> 2, fast = 1 -> 2,因为2翻转的时候前面的1还指向2
//[1,2,2,1] 此时slow = 1 -> 2 -> 2, fast = 1 -> 2
while(ok && fast != nullptr) {
if(slow->val != fast->val) {
ok = false;
break;
}
fast = fast->next;
slow = slow->next;
}
return ok;
}
//翻转链表
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *cur = head;
while(cur) {
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
题目:
多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表>也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。
给定位于列表第一级的头节点,请扁平化列表,即将这样的多级双向链表展平成普通的双向链表,使所有结点出现在单级双链表中。
示例:
输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
输出:[1,2,3,7,8,11,12,9,10,4,5,6]
解释:
扁平化后的链表如下图:
思路:
DFS深搜:
将图片顺时针旋转90°,看成一个二叉树,前序遍历
创建一个新节点,用于遍历时连接前后节点
class Solution {
private:
//头结点
Node *prevNode = new Node(-1);
public:
Node* flatten(Node* head) {
dfs(head);
if(head != nullptr)
head->prev = nullptr;
return head;
}
//*preNode跟着dfs遍历,遍历一个连接一个
void dfs(Node* root) {
if(root == nullptr) return ;
//cout << root->val;
Node *left = root->child;
Node *right = root->next;
prevNode->next = root;
root->prev = prevNode;
prevNode = root;
dfs(left);
root->child = nullptr;
dfs(right);
}
};
题目:
给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。
给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。
如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。
如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。
输入:head = [3,4,1], insertVal = 2
输出:[3,4,1,2]
解释:在上图中,有一个包含三个元素的循环有序列表,你获得值为 3 的节点的指针,我们需要向表中插入元素 2 。新插入的节点应该在 1 和 3 之间,插入之后,整个列表如上图所示,最后返回节点 3 。
思路:
三种情况可以插入:
特例:
class Solution {
public:
Node* insert(Node* head, int insertVal) {
if(head == nullptr) {
head = new Node(insertVal);
head->next = head;
return head;
}
Node *cur = head;
while(cur->next != head) {//重点,如果只有一个数的时候正好跳过
if((cur->val <= insertVal && insertVal <= cur->next->val) ||
(cur->val > cur->next->val && insertVal >= cur->val) ||
(cur->val > cur->next->val && insertVal <= cur->next->val)) {
break;
}
cur = cur->next;
}
Node *next = cur->next;
cur->next = new Node(insertVal);
cur->next->next = next;
return head;
}
};