目录

链表结构

leetcode  206 单链表反转——(考察指针)

leetcode 234  回文链表

leetcode 92  链表中间段逆序

leetcode21 合并两个链表

LeetCode19  删除链表的倒数第 n 个节点

leetcode 24 两两交换链表中的节点

leetcode 160 相交链表

leetcode 142 环形链表

leetcode 86 分隔链表

leetcode 138 复制随机指针的链表


链表结构

struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};

构建链表——尾插法

ListNode * CreateNode(int n)  {ListNode *head = NULL, *pnew = NULL, *ptail = NULL; int num, i = 1;while (i <= n){pnew = new ListNode(0);cout << "输入第" << i << "个结点:" << endl;cin >> num;pnew->val = num;pnew->next = NULL;if (head == NULL)head = pnew;else{ptail->next = pnew;}ptail = pnew;i++;}pnew = NULL;delete pnew;return head;
}

leetcode  206 单链表反转——(考察指针)

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思路:

迭代

设置两个指针(new_head和next),new_head始终使其指向新链表的头结点,next始终使其指向待反转链表的头结点。

步骤图示如下(渣绘)

代码如下:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {if((head==nullptr)||(head->next==nullptr))return head;ListNode * new_head=nullptr,*next=nullptr;while(head){//next指向待排序链表的头结点next=head->next;//令被拆下来的节点的next指针指向新链表的头结点head->next=new_head;//移动new_head,使其保持指向新链表的头结点new_head=head;//使head指向待排序链表的头结点head=next;}return new_head;}
};

递归

递归着将链表层层拆解,等遇到尾节点时,将其设置为链表的头结点,之后层层从递归中返回,将之前记录的节点设置为当前链表的尾节点。类似下图(渣绘,求原谅):

代码如下:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* helper(ListNode*  head){ListNode*  current_head=nullptr;ListNode*  head_next=nullptr;if ((head == NULL)||(head->next == NULL)){return head;}else{//记下当前的头结点a0current_head = head;//记下当前头结点后面的结点a1head_next = head->next;//返回(a1...an)逆转后的头结点head = helper(head_next);//用上面保存的地址(逆转后的尾结点)指向原来的头结点a0head_next->next = current_head;//将a0的next域置零current_head->next = NULL;}//返回a0return head;
}ListNode* reverseList(ListNode* head) {if((head==nullptr)||(head->next==nullptr))return head;ListNode * tail=nullptr;tail=helper(head);return tail;}
};

leetcode 234  回文链表

请判断一个链表是否为回文链表。你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

思路:

空间复杂度为O(1),因此只能哟经指针了。一个快指针每次走两步,慢指针每次走一步,以此来找到链表的中间节点。

之后对后半段单链表进行逆序。将逆序之后的链表与元链表的前半段进行比较。

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};
*/
class PalindromeList {
public:bool chkPalindrome(ListNode* A) {ListNode *fast=A, *slow=A;//找到中间节点while(fast){slow=slow->next;fast=fast->next?fast->next->next:fast->next;}//对后半段进行逆序ListNode *next=nullptr, *newhead=nullptr;while(slow){next=slow->next;slow->next=newhead;newhead=slow;slow=next;}//比较while (A && newhead){if (A->val != newhead->val){return false;}A = A->next;newhead = newhead->next;}return true;}
};

leetcode 92  链表中间段逆序

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:

1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

思路:

关键点在于这四个节点

找到四个节点之后,对需要逆置的段进行单链表逆置操作即可。

特殊条件:m=1的情况。m=1,说明逆置段开始节点的前驱是一个空指针,此时应该返回以逆置段开始节点为作为头结点的链表。

实现代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* reverseBetween(ListNode* head, int m, int n) {if(head==nullptr) return nullptr;if(n==1) return head;//只有一个节点,则必返回头结点ListNode* result=head; //返回的结果ListNode* pre_head=NULL;//逆置段开始节点的前驱ListNode* new_head=NULL;ListNode* tail=head; //逆置段的尾节点int change_len=n-m+1; //要逆置的节点个数while(head&&--m){pre_head=head; //pre_head指向逆置段头结点的前驱head=head->next;}tail=head;//逆置段的尾节点ListNode* next=nullptr;//一波单链表逆序操作while(head && change_len--){next=head->next;head->next=new_head;new_head=head;head=next;}tail->next=head; //逆置段尾节点连接不需要逆置的那段节点if(pre_head){ //m!=1的情况pre_head->next=new_head;}else{result=new_head;}return result;}
};

leetcode21 合并两个链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路:

  1. 先检查两个链表是否都非空,如果存在空链接,则返回另一个链表的头结点
  2. 然后设置一个空指针,使其指向两个链表中头结点值较小的节点,将该指针作为结果链表的头结点
  3. 之后进入循环体,不断比较,直到有一方的链表到达了尾节点为止
  4. 最后连接剩余的未参与比较的链表段

实现代码:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {//1if((!l2)||(!l1)){ //如果存在空链表,则返回另一个链表的头结点if(l1){return l1;}else{return l2;}}//2ListNode *result=(l1->val>l2->val)?l2:l1;//结果的头结点if(result==l1){l1=l1->next;}else if(result==l2){l2=l2->next;}//3ListNode *helper=result;while(l1&&l2){if(l1->val>l2->val){helper->next=l2;l2=l2->next;}else{helper->next=l1;l1=l1->next;}helper=helper->next;}//4//连接l1或l2的剩余节点if(l1){helper->next=l1;}else if(l2){helper->next=l2;}return result;}
};

LeetCode19  删除链表的倒数第 n 个节点

题目:

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.当删除了倒数第二个节点后,链表变为 1->2->3->5.

使用两根指针,指针间距离为n-1.当一根指针指向链表尾节点时,证明另一根指针所指节点应该被删除。

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* pNode = head;ListNode* fast = head;ListNode* slow = head;//使快指针移动n步,使其与慢指针之间保持一定距离for (int i = 0; i < n; ++i) fast = fast->next;if (fast == nullptr){pNode = head->next;delete head; head = nullptr;return pNode;}//使快慢指针同步运动,直到快指针指向链表尾while (fast->next != nullptr){fast = fast->next;slow = slow->next;}//使pNext指向该被删除的节点ListNode* pNext = slow->next;slow->next = pNext->next;//释放空间delete pNext; pNext = nullptr;return head;}
};

leetcode 24 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

说明:

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

递归

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {if(head == NULL || head->next == NULL) return head;ListNode* temp = head -> next;head -> next = swapPairs(temp -> next);temp -> next = head;return temp;}
}; 

循环

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *p = dummyHead;while (p->next && p->next->next) {ListNode *node1 = p->next;ListNode *node2 = node1->next;ListNode *next = node2->next;node2->next = node1;node1->next = next;p->next = node2;p = node1;}ListNode *ret = dummyHead->next;delete dummyHead;return ret;}
};

leetcode 160 相交链表

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

  • 如果两个链表没有交点,返回 null.
  • 在返回结果后,两个链表仍须保持原有的结构。
  • 可假定整个链表结构中没有循环。
  • 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

原题:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {}
};

思路:

1、使用set——太low

将一个链表中的各个节点的地址存入set中,然后遍历另一个节点。发现地址相同的,就是相交的节点了(存地址,因为不同节点的值可以相同,但是地址不会重复)

class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {set<ListNode *>con;//将链表A中的地址存入set中while(headA){con.insert(headA);headA=headA->next;}//遍历链表B的地址,找到地址相同的节点while(headB){if(con.find(headB)!=con.end()){return headB;}headB=headB->next;}return nullptr;}
};

2、方法2

static const auto _____ = []() {ios::sync_with_stdio(false);cin.tie(nullptr);return nullptr;
}();class Solution {
public:vector<int> getDistence(ListNode *headA, ListNode *headB){vector<int>ans;int res=0;ListNode *temp;//疑问,这里改变的指针指向,会不会影响原函数while(headA && headB){headA=headA->next;headB=headB->next;}if(headA!=nullptr){ans.push_back(1);temp=headA;}else if(headB!=nullptr){ans.push_back(2);temp=headB;}else{ans.push_back(0);temp=headA;}while(temp){res++;temp=temp->next;}ans.push_back(res);return ans;}ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if((!headA)||(!headB)) return nullptr;//获取哪一个链表长,并获取两个链表长度的差距vector<int>res=getDistence(headA, headB);if(res[0]==1){while(res[1]--){headA=headA->next;}}else if(res[0]==2){while(res[1]--){headB=headB->next;}}while(headA!=headB){headA=headA->next;headB=headB->next;}return headA;        }
};

加了这句之后,代码就快了,。。。

static const auto _____ = []() {ios::sync_with_stdio(false);cin.tie(nullptr);return nullptr;
}();

方法三:

双指针遍历两个链表,当链表B遍历到尾部时,使其next指向链表A的表头;同理,当链表A遍历到尾部时,使其next指向链表B的表头。如此遍历,有重合就证明有交点。最多遍历 链表A的长度+链表B的长度 即可判断出是否有相交的节点。

class Solution {public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *pA = headA;ListNode *pB = headB;// 有空链表肯定无交点if(pA == nullptr || pB == nullptr){return nullptr;}//ifwhile(pA && pB){// 交点if(pA == pB){return pA;}if(pA->next && pB->next){pA = pA->next;pB = pB->next;}// 到达pA末尾,pB未到达else if(!pA->next && pB->next){pA = headB;pB = pB->next;}// 到达pB末尾,pA未到达else if(pA->next && !pB->next){pA = pA->next;pB = headA;}// 同时到达pA,pB末尾else{return nullptr;}}return nullptr;}
};

leetcode 142 环形链表

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

进阶:
你是否可以不用额外空间解决此题? 能!!!

方法一:用set,太low

class Solution {
public:ListNode *detectCycle(ListNode *head) {if(!head) return nullptr;       set<ListNode*>res;  while(head){if(res.find(head)!=res.end()){return head;}res.insert(head);head=head->next;}return nullptr;}
};

方法二:快慢指针

快指针和慢指针,如果链表有环,则两个指针终会相遇。但是像雨点未必是环的入口,如图:

那么环该如何求?由于快指针的速度是慢指针的两倍,且链表有环,则两个指针走过的地方可以分为a, b, c 三块:

static const auto _____ = []() {ios::sync_with_stdio(false);cin.tie(nullptr);return nullptr;
}();class Solution {
public:ListNode *detectCycle(ListNode *head) {if(!head) return nullptr;ListNode *fast=head;ListNode *slow=head;ListNode *meet=nullptr;while(fast){slow=slow->next;fast=fast->next;if(!fast) return nullptr;//链表无环fast=fast->next;if(fast==slow){meet=slow;break;}}while(head && meet){if(head==meet){return head;}head=head->next;meet=meet->next;}return nullptr;                }
};

leetcode 86 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

思路:

设置两个临时头结点,分别存储小于x的节点和大于等于x的节点。最后把两段链表连接起来即可。

class Solution {
public:ListNode *partition(ListNode *head, int x) {  //创建两个临时头结点ListNode *leftHead = (ListNode*)malloc(sizeof(ListNode));  ListNode *rightHead = (ListNode*)malloc(sizeof(ListNode)); //小的都存lpre后面,其余的都存rpre后面。之后把这俩链接就可以了ListNode *lpre = leftHead,*rpre = rightHead;  while(head != NULL){  if(head->val < x){  lpre->next = head;  //使得lpre指向head,新的小点都查到lpre后面lpre = head;  }  else{  rpre->next = head;  rpre = head;  }  head = head->next;  }  rpre->next = NULL;  lpre->next = rightHead->next;  return leftHead->next;  }
};

leetcode 138 复制随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深度拷贝。

思路:——通过map映射来实现。

第一步:

拷贝一下原链表

然后将原链表的地址与原链表的编号作为键值对存入一个map中,用一个vector存储另一个链表的各个节点的地址。

第二步:

遍历原链表,查看链表的random指针指向的是哪一个节点的地址,通过地址获取random指针指向的节点编号。然后在另一个vector中去查找对应的地址并构建新链表的random指针。

实现代码:

class Solution {
public:RandomListNode *copyRandomList(RandomListNode *head) {if(!head) return nullptr;map<RandomListNode *, int>node_map;vector<RandomListNode *>node_vec;RandomListNode *ptr=head;int i=0; //节点编号while(ptr){//创建新链表的节点,并将地址存入vector中node_vec.push_back(new RandomListNode(ptr->label));//创建map映射,map有重载 [] ,如果map中没有 [] 中的元素,则会创建键值对并存入map中//具体实现可以参考源码node_map[ptr]=i++;ptr=ptr->next;}//结尾是一个空指针,push_back了一个0之后,node_vec.push_back(0);ptr=head;i=0;int id=0;while(ptr){node_vec[i]->next=node_vec[i+1];if(ptr->random){id=node_map[ptr->random];node_vec[i]->random=node_vec[id];                }//因为有  node_vec.push_back(0);,否则最后结尾是指针并不指向空,//导致输出的结果不正确ptr=ptr->next;i++;}        return node_vec[0];        }
};

分门别类刷leetcode——链表(C++实现)相关推荐

  1. 分门别类刷leetcode——高级数据结构(字典树,前缀树,trie树,并查集,线段树)

    目录 Trie树(字典树.前缀树)的基础知识 字典树的节点表示 字典树构造的例子 字典树的前序遍历 获取字典树中全部单词 字典树的整体功能 字典树的插入操作 字典树的搜索操作 字典树的前缀查询 字典树 ...

  2. 分门别类刷题总结列表 C++ 实现

     目录 输入输出 leetcode 牛客网 算法训练营 SQL shell编程 零七八碎 买的课 真题 输入输出 1 牛客刷题输入输出总结 2 记录各个七七八八的输入 持续更新中 leetcode 1 ...

  3. 学长冷月带你怒刷LeetCode之反转链表

    本文目录 前言 题目描述 冷月题解 总结一下 前言 链表的操作是数据结构中最基础的算法之一,反转列表也是一道经典的笔试题.很多公司的面试题库中都有这道题,有的公司明确题目要求不能使用额外的节点存储空间 ...

  4. leetcode刷题链表

    leetcode刷题链表 反转链表Ⅱ 题目描述将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n)O(n),空间复杂度 O(1)O(1).例如: 给出的链表为9 ...

  5. 【前端来刷LeetCode】两数之和与两数相加

    大部分玩前端的小伙伴,在算法上都相对要薄弱些,毕竟调样式.调兼容就够掉头发的了,哪还有多余的头发再去折腾. 确实在前端中需要使用到算法的地方是比较少,但若要往高级方向发展,算法的基本功就非常重要啦.对 ...

  6. 为了OFFER,花了几个小时,刷下Leetcode链表算法题

    @Author:Runsen @Date:2020/9/13 链表是我这辈子的痛啊,每次学到链表,就往往不会,就没有继续学下去.现在大四不能继续让这个链表阻碍我了.现在基本是重刷数据结构和算法,毕竟笔 ...

  7. vscode 上刷leetcode算法题(含配置)

    为什么选择在vscode 上刷leetcode 减少leetcode网页提交次数 提高"提交通过率" 轻量级的,易于debug的IDE 语法高亮美观 方便跑多个测试用例 调试链表, ...

  8. 拿什么拯救你,我的面试之——从零打卡刷Leetcode(No.003)

    写在前边: 小詹一直觉得自己编程能力不强,想在网上刷题,又怕不能坚持.不知道有木有和小伙伴和小詹一样想找个人一起刷题呢?欢迎和小詹一起定期刷leetcode,每周一周五更新一题,每一题都吃透,欢迎一题 ...

  9. java开发有必要刷leetcode吗_刷 leetcode 需要哪些基础?

    首先要知道 基础的数据结构:数组.字符串.树.堆.栈.队列.哈希表 基础的算法: 枚举遍历, 二分查找,递归,回溯 明白基础的数据结构之后,我们可以发现 leetcode上已经做好了分类, 首先我们要 ...

  10. 面试官系列 - LeetCode链表知识点题型总结

    文章目录 前言 知识点 什么是链表 类别 单向链表 循环链表 双向链表 双向循环链表 与数组的性能对比 优缺点 常用技巧 题型总结 基本操作 删除类 翻转类题型 合并链表 环形链表 拆分链表 排序链表 ...

最新文章

  1. 使用 fcntl 函数 获取,设置文件的状态标志
  2. C++中的friend详细解析
  3. json 数据 生成 图表_CAPP工艺图表 / 知识重用 快速编制
  4. Android开发之百度地图经纬度转换地址(以及获取详细地址的方法自测成功)
  5. 前端学习(3187):ant-design的button介绍按钮属性
  6. Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
  7. spring cloud全家桶_吃透这份Github点赞120k的Spring全家桶笔记Offer拿到手软
  8. linux tomcat 发布servlet,SpringBoot项目使用war包部署至云服务器(Linux+Tomcat)
  9. 微型计算机基础知识,微型计算机的基础知识
  10. 最小生成树(Prim算法+Kruskal算法)
  11. 16.1 Class类与Java反射
  12. 用java语言写网上购物的语句_用java代码写一个简单的网上购物车程序
  13. 二阶有源带通滤波器滤波原理
  14. 常用贴片器件正负极区分
  15. kalibr源码解析之fov模型重投影误差计算
  16. Cadence软件输出PDF原理图时页码重复或乱序问题
  17. 强化学习(一):概述
  18. 桃飘火焰焰,梨堕雪漠漠。(排序 贪心
  19. Android加载本地大Bitmap文件,解析出来的Bitmap是整张图片是黑色的
  20. 使用mongo shake实现从一个单实例mongo往多个mongo单实例进行数据同步

热门文章

  1. 22478计算机代码,数字2247代表啥意思 2247数字意思
  2. 华为笔试题--直角三角形周长
  3. flask form表单
  4. 八卦一下 ,拉点流量
  5. python爬取bilibili数据_用 Python 抓取 bilibili 弹幕并分析!
  6. OSChina 周二乱弹 —— 代码中的坑是怎么出现的?
  7. 2017已经接近尾声,然而我却什么都没干成
  8. Linux在线词典问题,linux中goldendict发声词典播放报错问题解决方案
  9. 分享几款C/C++在线编译器
  10. python设置窗口位置_python中tkinter窗口位置