C++链表题
在C++中,链表是一种常见的数据结构,用于存储一系列的元素,其中每个元素都指向下一个元素。这种结构允许动态地增加或删除元素,而不需要像数组那样预先分配固定大小的内存。
// 定义链表节点结构
typedef struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
} ListNode;
反转链表
给定一个单链表的头结点pHead(该头节点是有值的,如它的val是1),长度为n,反转该链表后,返回新链表的表头。
// 反转整个链表
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* curr = head;
while(curr != nullptr) {
ListNode* next = curr->next;
curr->next = pre;
pre = curr;
curr = next;
}
return pre;
}
链表内指定区间反转
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n),空间复杂度 O(1)。
// 反转链表中m到n的部分
ListNode* reverseBetween(ListNode* head, int m, int n) {
// 创建虚拟头节点,简化边界情况
ListNode* dummy = new ListNode(0);
dummy->next = head;
// 找到第m-1个节点(反转的前一个节点)
ListNode* pre = dummy;
for(int i=1; i<m; i++) {
pre = pre->next;
}
// 反转从m到n的节点
ListNode* curr = pre->next;
ListNode* next;
for(int i=m; i<n; i++) {
next = curr->next;
curr->next = next->next;
next->next = pre->next;
pre->next = next;
}
ListNode* result = dummy->next;
delete dummy;
return result;
}
链表中的节点每k个一组翻转
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样你不能更改节点中的值,只能更改节点本身。
// 反转链表中每k个节点一组的部分(O(1)空间复杂度)
ListNode* reverseKGroup(ListNode* head, int k) {
// 创建虚拟头节点,简化边界情况
ListNode* dummy = new ListNode(0);
dummy->next = head;
// pre指向每组反转的前一个节点
ListNode* pre = dummy;
// end指向每组反转的最后一个节点
ListNode* end = dummy;
while(end->next != nullptr) {
// 移动end到第k个节点
for(int i=0; i<k && end != nullptr; i++) {
end = end->next;
}
// 如果剩余节点不足k个,结束循环
if(end == nullptr) break;
// start指向每组反转的第一个节点
ListNode* start = pre->next;
// nextGroup指向每组反转的下一组的第一个节点
ListNode* nextGroup = end->next;
// 将当前组与下一组断开连接
end->next = nullptr;
// 反转当前组
pre->next = reverseList(start);
// 将反转后的组与下一组重新连接
start->next = nextGroup;
// 更新pre和end为下一组反转的前一个节点
pre = start;
end = pre;
}
ListNode* result = dummy->next;
delete dummy;
return result;
}
合并两个排序的链表
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
// 合并两个有序链表
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 创建虚拟头节点,简化边界处理
ListNode* dummy = new ListNode(0);
ListNode* curr = dummy;
// 同时遍历两个链表,将较小的节点添加到结果链表
while(l1 != nullptr && l2 != nullptr) {
if(l1->val <= l2->val) {
curr->next = l1;
l1 = l1->next;
} else {
curr->next = l2;
l2 = l2->next;
}
curr = curr->next;
}
// 连接剩余节点
if (l1 != nullptr) {
curr->next = l1;
} else {
curr->next = l2;
}
ListNode* result = dummy->next;
delete dummy;
return result;
}
合并k个已排序的链表
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
// 定义优先队列的比较函数(小根堆)
struct ListNodeCompare {
bool operator()(ListNode* a, ListNode* b) {
return a->val > b->val; // 小根堆:值小的优先级高
}
};
// 小根堆(优先队列)实现合并k个链表
ListNode* mergeKLists(vector<ListNode*>& lists) {
// 创建小根堆,存储每个链表的当前头节点
priority_queue<ListNode*, vector<ListNode*>, ListNodeCompare> minHeap;
// 将所有非空链表的头节点加入堆
for (ListNode* list : lists) {
if (list != nullptr) {
minHeap.push(list);
}
}
// 虚拟头节点,简化边界处理
ListNode* dummy = new ListNode(0);
ListNode* curr = dummy;
// 不断从堆中取出最小节点,添加到结果链表
while (!minHeap.empty()) {
// 取出堆顶(最小)节点
ListNode* minNode = minHeap.top();
minHeap.pop();
// 将最小节点添加到结果链表
curr->next = minNode;
curr = curr->next;
// 如果该节点所在链表还有后续节点,将其加入堆
if (minNode->next != nullptr) {
minHeap.push(minNode->next);
}
}
// 获取结果链表的头节点并删除虚拟节点
ListNode* result = dummy->next;
delete dummy;
return result;
}
判断链表中是否有环
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
// 检测链表中是否有环(Floyd's Tortoise and Hare算法)
bool hasCycle(ListNode *head) {
if(head == nullptr) return false;
ListNode* slow = head; // 慢指针(乌龟),每次移动1步
ListNode* fast = head; // 快指针(兔子),每次移动2步
while(fast != nullptr && fast->next != nullptr) {
slow = slow->next; // 慢指针移动1步
fast = fast->next->next; // 快指针移动2步
if(slow == fast) { // 如果相遇,说明有环
return true;
}
}
return false; // 快指针到达链表末尾,说明没有环
}
链表中环的入口结点
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
// 找到链表中环的入口节点(Floyd's Tortoise and Hare算法)
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if(pHead == nullptr) return nullptr;
ListNode* slow = pHead; // 慢指针,每次移动1步
ListNode* fast = pHead; // 快指针,每次移动2步
// 第一步:检测环的存在
while(fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast) { // 快慢指针相遇,说明有环
// 第二步:找到环的入口
slow = pHead; // 将慢指针重置到链表头部
while(slow != fast) { // 两个指针都以相同速度移动,再次相遇点即为环入口
slow = slow->next;
fast = fast->next;
}
return slow; // 返回环的入口节点
}
}
return nullptr; // 快指针到达链表末尾,说明没有环
}
链表中倒数最后k个结点
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
// 找到链表中倒数第k个节点(双指针法)
ListNode* FindKthToTail(ListNode* pHead, int k) {
// 处理边界情况:链表为空或k<=0
if(pHead == nullptr || k <= 0) return nullptr;
ListNode* slow = pHead; // 慢指针,初始指向链表头部
ListNode* fast = pHead; // 快指针,初始指向链表头部
// 第一步:让快指针先走k步
for(int i=0; i<k; i++) {
if(fast == nullptr) { // 如果链表长度小于k,返回nullptr
return nullptr;
}
fast = fast->next;
}
// 第二步:快慢指针同时移动,直到快指针到达链表末尾
while(fast != nullptr) {
slow = slow->next;
fast = fast->next;
}
return slow; // 慢指针此时指向倒数第k个节点
}
删除链表的倒数第n个节点
给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
// 删除链表中倒数第n个节点(双指针法)
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 创建虚拟头节点,简化删除头节点的边界情况
ListNode* dummy = new ListNode(0);
dummy->next = head;
// 慢指针指向虚拟头节点
ListNode* slow = dummy;
// 快指针指向虚拟头节点
ListNode* fast = dummy;
// 快指针先走n+1步,这样快慢指针之间的距离为n+1
for(int i=0; i<n+1; i++){
// 题目保证n有效,此检查可以省略,但保留以增强鲁棒性
if(fast == nullptr){
return head;
}
fast = fast->next;
}
// 快慢指针同时移动,直到快指针到达链表末尾
while (fast != nullptr) {
slow = slow->next;
fast = fast->next;
}
// 此时慢指针指向倒数第n+1个节点,删除倒数第n个节点
ListNode* toDelete = slow->next;
slow->next = slow->next->next;
delete toDelete; // 释放被删除节点的内存
// 保存新的头节点
ListNode* newHead = dummy->next;
// 释放虚拟头节点
delete dummy;
return newHead;
}
两个链表的第一个公共结点
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。
ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
// 双指针交叉遍历法:时间复杂度O(n), 空间复杂度O(1)
if (pHead1 == nullptr || pHead2 == nullptr) return nullptr;
ListNode* p1 = pHead1;
ListNode* p2 = pHead2;
// 两个指针同时遍历,当走到末尾时切换到另一个链表的头部
// 如果有公共节点,它们会在公共节点相遇
// 如果没有公共节点,最终都会走到nullptr
while (p1 != p2) {
p1 = (p1 == nullptr) ? pHead2 : p1->next;
p2 = (p2 == nullptr) ? pHead1 : p2->next;
}
return p1; // 返回公共节点或nullptr
}
链表相加
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
ListNode* addInList(ListNode* head1, ListNode* head2) {
// 边界处理
if (head1 == nullptr) return head2;
if (head2 == nullptr) return head1;
// 1. 反转两个链表,使链表头指向原链表的最低位
ListNode* reversed1 = reverseList(head1);
ListNode* reversed2 = reverseList(head2);
// 2. 创建结果链表的虚拟头节点
ListNode* dummy = new ListNode(0);
ListNode* curr = dummy;
int carry = 0; // 进位
// 3. 逐位相加,处理进位
while (reversed1 != nullptr || reversed2 != nullptr || carry != 0) {
int val1 = (reversed1 != nullptr) ? reversed1->val : 0;
int val2 = (reversed2 != nullptr) ? reversed2->val : 0;
// 计算当前位的和与进位
int sum = val1 + val2 + carry;
int digit = sum % 10;
carry = sum / 10;
// 将结果位添加到新链表
curr->next = new ListNode(digit);
curr = curr->next;
// 移动指针
if (reversed1 != nullptr) reversed1 = reversed1->next;
if (reversed2 != nullptr) reversed2 = reversed2->next;
}
// 4. 反转结果链表,得到正确的顺序
ListNode* result = reverseList(dummy->next);
// 恢复原始链表的结构(可选,根据需求决定)
// reverseList(reversed1); // 恢复head1
// reverseList(reversed2); // 恢复head2
// 释放虚拟头节点
delete dummy;
return result;
}
单链表的排序
给定一个节点数为n的无序单链表,对其按升序排序。
// 找到链表的中点(快慢指针法)
ListNode* findMiddle(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode* slow = head; // 慢指针,每次移动1步
ListNode* fast = head->next; // 快指针,每次移动2步
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow; // 返回中点的前一个节点
}
// 基于归并排序的链表排序(O(n log n)时间,O(n)空间)
ListNode* sortInList(ListNode* head) {
// 边界处理:空链表或只有一个节点的链表已经有序
if (head == nullptr || head->next == nullptr) {
return head;
}
// 1. 找到链表的中点,将链表分成两个子链表
ListNode* mid = findMiddle(head);
ListNode* secondHalf = mid->next;
mid->next = nullptr; // 将链表断开
// 2. 递归地对两个子链表进行排序
ListNode* left = sortInList(head);
ListNode* right = sortInList(secondHalf);
// 3. 合并两个已排序的子链表
return mergeTwoLists(left, right);
}
判断一个链表是否为回文结构
给定一个链表,请判断该链表是否为回文结构。回文是指该字符串正序逆序完全一致。
bool isPail(ListNode* head) {
// write code here
// 边界处理:空链表或只有一个节点的链表都是回文
if (head == nullptr || head->next == nullptr) {
return true;
}
// 1. 使用快慢指针找到链表中点
ListNode* slow = head;
ListNode* fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
// 2. 反转后半部分链表
ListNode* secondHalfHead = reverseList(slow);
ListNode* secondHalfTemp = secondHalfHead; // 保存后半部分反转后的头节点,用于恢复
// 3. 比较前半部分和反转后的后半部分
bool isPalindrome = true;
ListNode* firstHalf = head;
while (secondHalfHead != nullptr) {
if (firstHalf->val != secondHalfHead->val) {
isPalindrome = false;
break;
}
firstHalf = firstHalf->next;
secondHalfHead = secondHalfHead->next;
}
// 4. 恢复原链表结构(可选,但良好的编程实践)
reverseList(secondHalfTemp);
// 5. 返回结果
return isPalindrome;
}
链表的奇偶重排
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
ListNode* oddEvenList(ListNode* head) {
// write code here
// 边界处理:空链表、单节点或双节点链表直接返回
if (!head || !head->next) {
return head;
}
// 创建奇偶节点的指针
ListNode* odd = head; // 奇数位置节点的头指针
ListNode* even = head->next; // 偶数位置节点的头指针
ListNode* evenHead = even; // 保存偶数链表的头节点
// 遍历链表,分离奇偶节点
while (even != nullptr && even->next != nullptr) {
odd->next = even->next; // 奇数节点指向后一个奇数节点
odd = odd->next; // 移动奇数节点指针
even->next = odd->next; // 偶数节点指向后一个偶数节点
even = even->next; // 移动偶数节点指针
}
// 将偶数链表连接到奇数链表的末尾
odd->next = evenHead;
return head;
}
删除有序链表中重复的元素
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次例如:给出的链表为1→1→2,返回1→2.
给出的链表为1→1→2→3→3,返回1→2→3.
ListNode* deleteDuplicates(ListNode* head) {
// write code here
if (!head) {
return head;
}
ListNode* current = head;
while (current->next != nullptr) {
if (current->val == current->next->val) {
// 删除重复节点
ListNode* temp = current->next;
current->next = current->next->next;
delete temp; // 释放内存,避免内存泄漏
} else {
// 移动到下一个节点
current = current->next;
}
}
return head;
}
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:给出的链表为1→2→3→3→4→4→5, 返回1→2→5.
给出的链表为1→1→1→2→3, 返回2→3.
ListNode* deleteDuplicatesSec(ListNode* head) {
// 边界处理:空链表直接返回
if (!head) {
return head;
}
// 创建虚拟头节点,简化边界情况处理
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* prev = dummy;
ListNode* current = head;
while (current != nullptr) {
// 检查当前节点是否与下一个节点重复
bool isDuplicate = false;
while (current->next != nullptr && current->val == current->next->val) {
isDuplicate = true;
// 删除重复节点并释放内存
ListNode* temp = current->next;
current->next = current->next->next;
delete temp;
}
if (isDuplicate) {
// 当前节点也是重复的,需要删除
ListNode* temp = current;
prev->next = current->next;
current = current->next;
delete temp;
} else {
// 当前节点不重复,移动到下一个节点
prev = prev->next;
current = current->next;
}
}
// 获取新的头节点并释放虚拟头节点
head = dummy->next;
delete dummy;
return head;
}

浙公网安备 33010602011771号