C++:从0开始学习链表(练习)
上一次我们学习了链表的基本概念和操作,今天我们来写几道链表的练习题,来巩固知识
1.力扣206反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
思路:
可以将链表的指针反转,使后一个指针指向前一个指针,但是要注意存储后一个指针的下一个指针的地址,否则会造成后面数据的丢失
class Solution {
public:ListNode* reverseList(ListNode* head) {//slow指针指向空,即头指针的前一个指针ListNode* slow = nullptr;//fast指针指向头指针ListNode* fast = head;while(fast){ListNode* fnxt = fast->next;fast->next = slow;slow = fast;fast = fnxt;}return slow;//此时slow指针指向反转完链表的头节点}
};
2.力扣24两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4] 输出:[2,1,4,3]
思路:
创建一个虚拟头节点,然后用上一题相似的方法,依次将每个节点的指针反转
class Solution {
public:ListNode* swapPairs(ListNode* head) {//创建虚拟头节点,并插入链表ListNode* vnode = new ListNode(0);vnode->next = head;head = vnode;//创建一个节点p存储虚拟头节点0ListNode* p = vnode;while(p->next&&p->next->next){ListNode* slow = p->next;//slow指向第一个元素1ListNode* fast = p->next->next;//fast指向第二个元素2ListNode* fnxt = fast->next;//fnxt指向第三个元素3//反转链表p->next = fast;//将0->2fast->next = slow;//2->1slow->next = fnxt;//1->3//此时链表为0->2->1->3->4->5->6p = slow;//更新p到需要处理的元素前一位,即3的前一位,也就是slow指向的位置}//将虚拟头结点删去head = vnode->next;//返回链表头节点return head;}
};
3.力扣19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
思路:
创建一个虚拟头节点,使用slow和fast两个指针指向虚拟头节点,将fast指针先移动n个元素,再将两个元素一起移动,直到fast指针到尾部时,slow指针刚好指向要删除元素的前一个元素
class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* vnode = new ListNode();//虚拟头节点vnode->next = head;//两个指针指向虚拟头节点ListNode* fast = vnode;ListNode* slow = vnode;//将快指针fast先移动n个元素for(int i = 0;i<=n;i++){if(fast == nullptr) return head;//如果移动过程中已经到尾节点,说明链表没有倒数第n个元素fast = fast->next;}//将两个指针一起移动while(fast){fast = fast->next;slow = slow->next;}//最后slow指针指向要删除元素的前一个元素slow->next = slow->next->next;return vnode->next;}
};
4.力扣876链表的中间节点
给你单链表的头结点 head
,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:head = [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点,值为 3 。
示例 2:
输入:head = [1,2,3,4,5,6] 输出:[4,5,6] 解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。
思路:
由于要找中间节点,所以快指针的遍历速度是慢指针的两倍,这样当快指针遍历结束时,慢指针刚好指向中间节点
class Solution {
public:ListNode* middleNode(ListNode* head) {ListNode* fast = head;//快指针ListNode* slow = head;//慢指针//循环条件为://奇数:快指针指向的下一个元素为nullptr则慢指针到达中间节点//偶数:快指针指向的元素为nullptr,则慢指针指向第二个中间节点 while(fast&&fast->next){fast = fast->next->next;//快指针两倍速度遍历slow = slow->next;}return slow;}
};
5.力扣160相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
思路:
创建两个指针分别指向两个链表的头节点,两个指针同时分别遍历两个链表,当一个指针遍历完其指向的链表时,将其重新指向另一个链表的头节点继续遍历.
这样如果两个链表有相交,则两个指针会指向同一个元素,循环停止.如果没有相交,当两个指针都遍历完两个链表时,会同时指向两个尾节点nullptr,循环也会停止
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode* pa = headA;//pa遍历headAListNode* pb = headB;//pb遍历headBwhile(pa!=pb){//循环条件为两个指针指向不相同if(pa==nullptr){//当pa遍历完headA时,继续遍历headBpa = headB;}else{//遍历链表pa = pa->next;}if(pb == nullptr){//当pb遍历完headB时,继续遍历headApb = headA;}else{//遍历链表pb = pb->next;}}//如果有相交,则pa会指向相交的节点//如果不相交,pa最终和pb一起指向nullptrreturn pa;}
};
这是五题力扣上的关于链表操作的相对简单的算法题,大家可以通过这几题,再理解理解链表的指针域和值域的关系,以及每个链表节点之间的关系