链表常见的题型和解题思路

1.链表中环的入口节点

首先判断头指针是不是空的
然后需要判断这个链表中包不包含环:两个指针,一个一步一个两部,如果相遇,说明存在
然后判断环节点的个数:从相遇的位置开始,往前走并计数,直到和自己再次相遇,得到个数
然后找出入口节点:从头开始,俩指针一个先走n步,另一个再走,两个相遇的位置就是入口节点位置

2.翻转链表

需要判断链表是不是空的或者链表是不是只有一个节点,如果是的话,直接就输出原来的链表了;

如果不是的话,翻转利用循环来做,因为需要将当前节点指向前一个节点,所以后一个节点需要先保存位置,也就是需要三个指针,一个pPre,一个pCur,一个pNext:先保存后一个节点,然后把当前节点指向前一个节点,然后把当前节点变成上一个节点,下一个节点变成当前节点;注意翻转之后头结点是原来的最后一个节点。

3.从尾到头打印链表

思路(1):可以先翻转链表,再逐个输出

思路(2):这种具有“后进先出”特性的,用栈比较容易,创建一个栈存放链表的节点,存放完之后从栈顶依次取出即可

4.两个链表的第一个公共节点

举例子:
1 2 5 9 6 3 0
   7 8 9 6 3 0

首先需要知道的是,两个链表从公共节点开始,之后的节点肯定都是一模一样的;

可以先遍历得到两个链表各自的长度,然后让长的那个先走比另一个长出的步数,再同时走,判断哪里相等哪里就是第一个公共节点了

5.链表中倒数第k个节点

单向链表肯定是不能从后往前数的,这个跟上面有的类似,既然知道是倒数第k个,那就两个指针,让一个先走k-1步,然后两个同时走,判断先走的那个到尾结点了,那后走的那个就是倒数第k个节点。

6.删除链表中重复的节点

例如:1 2 3 3 4 4 5结果是1 2 5

首先需要判断头指针(第一个节点)和第二个节点是不是空的,如果是,返回头指针就行了;

正常情况的时候,因为有可能存在删除第一个节点的情况,所以需要先重新创建一个头指针ListNode* newHead = new ListNode(-1),然后把这个头指针赋初值指向原本的头指针newHead->next = pHead;

然后需要三个指针来解决这个问题,分别是pPre pCur pNext三个,pPre 赋初值newHead,pCur赋初值pHead, 利用当前节点的循环来进行:得判断当前节点和当前节点的下一个节点不为空才进入循环来查找和删除,因为里头要对节点进行删除,所以要先保存下一个节点,然后如果当前节点等于下一个节点的值,因为还要继续判断下一位,来一个循环,只要下一个节点和当前节点的值相等,就把pNext往后移一个,直到找到不相等的就退出这个查找的循环了;然后执行删除,也就是把上一个节点pPre的下一个节点变成pNext,当前操作循环的节点变成pNext,然后再去循环判断;

那如果当前节点和下一个节点的值不相等呢:指针往后挪,循环再判断,也就是pPre = pCur;pCur = pCur->next。

最后返回新的头结点newHead的下一个节点即可。

7.复制复杂的链表

分成三个步骤:
(1)复制原始链表的任意节点N并创建新节点N',把节点N'链接到节点N的后面
(2)设置复制出来的节点的random,假设原始链表上的N的random指向节点S,那么其对应复制出来的N'是N的next指向的节点,同样S'也是S的next指向的节点
(3)把整个链表根据奇数偶数分出两个链表来,偶数的就是拷贝的链表

/*struct RandomListNode {    int label;    struct RandomListNode *next, *random;    RandomListNode(int x) :            label(x), next(NULL), random(NULL) {    }};*/class Solution {public:    RandomListNode* Clone(RandomListNode* pHead)    {        if(pHead == nullptr)            return nullptr;        //分成三个步骤:        //1.复制原始链表的任意节点N并创建新节点N',把节点N'链接到节点N的后面        //2.设置复制出来的节点的random,假设原始链表上的N的random指向节点S,那么其对应复制出来的N'是N的next指向        //的节点,同样S'也是S的next指向的节点        //3.把整个链表根据奇数偶数分出两个链表来,偶数的就是拷贝的链表        ClonedNode(pHead);        SetRandom(pHead);        return ReconnectNodes(pHead);    }    void ClonedNode(RandomListNode* pHead)    {        RandomListNode* pNode = pHead;        while(pNode != nullptr)        {            RandomListNode* newNode = new RandomListNode(0);//创建新节点            newNode->label = pNode->label;            newNode->next = pNode->next;            newNode->random = nullptr;            pNode->next = newNode;            pNode = newNode->next;        }    }    void SetRandom(RandomListNode* pHead)    {        RandomListNode* pNode = pHead;        while(pNode != nullptr)        {            RandomListNode* pCloned = pNode->next;            if(pNode->random != nullptr)//这一步判断别忘了,如果为空,会造成程序瘫痪                pCloned->random = pNode->random->next;            else                pCloned->random = nullptr;            pNode = pCloned->next;//pNode往后移一位        }    }    RandomListNode* ReconnectNodes(RandomListNode* Head)    {        RandomListNode* pNode = Head;//循环操作        RandomListNode* pClonedHead = nullptr;        RandomListNode* pClonedNode = nullptr;//创建一个新节点,作为拷贝链表的头结点        if(pNode != nullptr)        {            pClonedHead = pClonedNode = pNode->next;            pNode->next = pClonedNode->next;            pNode = pNode->next;        }        while(pNode != nullptr)        {            pClonedNode->next = pNode->next;            pClonedNode = pClonedNode->next;            pNode->next = pClonedNode->next;            pNode = pNode->next;        }                return pClonedHead;    }};

8.翻转链表

给定一个链表,旋转链表,将链表每个节点向右移动 个位置,其中 是非负数。

这个题说是翻转链表,其实就是改变了链表的头尾节点而已。

(1)先判断特殊情况,比如链表是否为空,是否只有一个元素,翻转的k是否为0等,否则直接返回

(2)然后处理一般情况,得先遍历一次得到链表的总长度size,然后将链表首尾相接,成为一个循环链表

(3)根据size和k的关系,计算出循环链表循环的次数loot= size - (k%size);

(4)然后进行指针循环,之前的头尾节点指针开始移动,次数为loop,直到移动结束,原先的尾结点指针指向的为新链表的尾部,原先的头结点指针指向的为新链表的头部。

9.扁平化多级双向链表

多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

给你位于列表第一级的头节点,请你扁平化列表,使所有结点出现在单级双链表中。

这题太经典了!

/*// Definition for a Node.class Node {public:    int val;    Node* prev;    Node* next;    Node* child;};*/class Solution {public:    vector<Node*> v;    //这题就是二叉树的前序遍历    //可以先前序遍历,将所有节点放入容器中,然后再顺序进行前后连接    void dfs(Node* head)    {        if(head == nullptr)            return;        v.push_back(head);        dfs(head->child);        dfs(head->next);    }    Node* flatten(Node* head) {        dfs(head);//这样就将所有的节点放入了容器中,按照前序的方式        int n = v.size();//得到容器中的节点数        for(int i=0;i<n;i  )        {            if(i<n-1)                v[i]->next = v[i 1];            if(i>0)                v[i]->prev = v[i-1];            v[i]->child = nullptr;        }        return head;    }};

10.判断是不是回文链表

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:    bool isPalindrome(ListNode* head) {        //先判断特殊情况        if(head == nullptr || head->next == nullptr)            return true;        //然后一般情况的判断方式是:先找到链表的中点,然后翻转后半部分,然后进行比较        ListNode* fast = head;        ListNode* slow = head;        while(fast != nullptr)        {            slow = slow->next;            fast = fast->next? fast->next->next : fast->next;        }        //最后的slow为原来链表的中间节点,也是待会儿需要翻转链表的头结点        //fast则指向了nullptr                //然后需要进行翻转        ListNode* pPre = nullptr;        while(slow != nullptr)        {            ListNode* temp = slow->next;//存储下一个节点            slow->next = pPre;            pPre = slow;            slow = temp;        }        //然后进行判断        while(pPre != nullptr && head != nullptr)        {            if(pPre->val != head->val)                return false;            pPre = pPre->next;            head = head->next;        }        return true;    }};

tips:遇到链表为题时候的思考方式

(1)是否能用双指针解决

(2)知否可以将一个指针先走几步,再两个同时走解决

(3)是否需要知道链表节点的个数

(4)是否需要将链表变成循环链表处理

(5)考虑是否需要容器,容器可以放节点,容器的好处也是能知道链表的长度

(6)找链表的中点、翻转链表都是最基本的操作,在拔高的题目里头有可能会用到

来源:https://www.icode9.com/content-4-774451.html

(0)

相关推荐

  • 每日一题 剑指offer(合并两个排序列表)

    编程是很多偏计算机.人工智能领域必须掌握的一项技能,此编程能力在学习和工作中起着重要的作用.因此小白决定开辟一个新的板块"每日一题",通过每天一道编程题目来强化和锻炼自己的编程能力 ...

  •  【C语言】链表及单链表基本操作

    &#160;【C语言】链表及单链表基本操作

  • C语言笔记-双向链表和循环链表

    两种链表的增删改查操纵类似于单向链表. 双向链表: 一种更复杂的链表是"双向链表"或"双面链表".每个节点有两个连接:一个指向前一个节点,(当此"连接 ...

  • 高考攻略 | 试题研究:如何写好历史小论文?最全小论文题型和解题思路~

    如何写好历史小论文 1.具备历史小论文的三要素  (1)论点(观点):观点应明确.清楚 (2)论据(证明观点的证据):证据要准确求真.要选择能证明论点的典型史实. (3)论证(用证据证明观点的过程): ...

  • 中考常见几何证明题解题思路总结

    几何证明题重点考察的是学生的逻辑思维能力,能通过严密的"因为"."所以"逻辑将条件一步步转化为所要证明的结论. 这类题目出法相当灵活,不像代数计算类题目容易总结 ...

  • 学霸高分秘籍。初中数学题型的解题思路是有...

    学霸高分秘籍.初中数学题型的解题思路是有一定技巧的.常见考点也是有模型的.吃透这套资料一定让您的孩子"数学成绩"更上一层楼. 面对一些需要单独琢磨和思考的题型,普通孩子还在读题思考 ...

  • 议论文阅读理解3大题型及解题思路 (内附经典例题)

    阅读理解在中考语文中占比很高,其中记叙文和说明文比较常见,议论文出现频次较低, 这可能会导致同学们在平时的阅读理解中大量训练记叙文和说明文,却忽略了对议论文的学习和训练. 如果考场上出现了议论文类的阅 ...

  • 小学奥数题型与解题思路60讲(word版,无水印)

    奥数能够很好地锻炼孩子的思维能力,很多父母都送孩子去上奥数培训班. 今天,给大家带来的是:小学奥数题型与解题思路60讲! 看完这些讲解,你家孩子就是高手! 部分截图 更多内容,请下载后查看! 下载地址 ...

  • 高中历史如何写好历史小论文?小论文题型和解题思路

    如何写好历史小论文 1.具备历史小论文的三要素  (1)论点(观点):观点应明确.清楚 (2)论据(证明观点的证据):证据要准确求真.要选择能证明论点的典型史实. (3)论证(用证据证明观点的过程): ...

  • 浅谈中考化学除杂题型的解题思路

    从接触化学到中考参加考试,只有10个月的时间.学生要在这10个月的时间里,学习化学最基本的理论知识,同时还要学会化学题目的读题以及解题思路.知识是基础的,但题目是多变的,除了要让学生扎实掌握基础知识外 ...

  • 2021年中考常见几何证明题解题思路总结

    几何证明题重点考察的是学生的逻辑思维能力,能通过严密的'因为'.'所以'逻辑将条件一步步转化为所要证明的结论. 这类题目出法相当灵活,不像代数计算类题目容易总结出固定题型的固定解法,而更看重的是对重要 ...

  • 高中数学经典90种常见基础题型及解题方法模板汇总

    https://m.toutiao.com/is/J4KtukU/ 高中数学很多关键就是题型的解题的训练! 而每个题型,其实都跟高中知识的内在联系,而且,每个题型的归纳,其实都可以发现,每个题型下有' ...