链表面试题

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    //迭代法列表反转
    ListNode* reverse(ListNode* node)
    {
        ListNode *prev=NULL;
        ListNode *nxt;
        while(node!=NULL)
        {
            nxt=node->next;
            node->next=prev;
            prev=node;
            node=nxt;
        }
        return prev;//注意这里返回的是prev,而while里面是node
    }
    bool isPail(ListNode* head) {
        if(head==NULL) return -1;
        if(head->next==NULL) return true;
        //有两个节点
        if(head->next->next==NULL) return head->val==head->next->val?true:false;
        //三个节点 
        ListNode *fast,*slow;//快慢指针:奇数返回中点,偶数返回上中点(少于三个节点的时候都返回的是head节点)
        slow=head;
        fast=head->next;
        while(fast->next!=NULL&&fast->next->next!=NULL)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        ListNode *pH=head;
        ListNode *pT=reverse(slow->next);//pT是反转以后的节点
        ListNode *pT2=pT;
        slow->next=NULL;//中节点末尾接一个NULL,构造成新的链表
        bool res=true;
        while(pH!=NULL&&pT!=NULL)
        {
            if(pH->val!=pT->val)
            {
                res=false;
                break;
            }
            pH=pH->next;
            pT=pT->next;
            
        }
        
        reverse(pT2);
        
        return res;
    }
};

复制特殊链表

种特殊的单链表节点类描述如下

class Node {

int value;

Node next;

Node rand; 了一条指针)

Node(int val) { value = val; }

}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null

给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。

要求

时间复杂度O(N),额外空间复杂度O(1)

利用哈希表把原来的结构全部存起来。

【算法】复制含有随机指针节点的链表 - 云+社区 - 腾讯云 (tencent.com)

进阶解法

思路

1、拷贝节点,例如对于 1->2->3->4 我们插入每个节点的后面插入其copy节点,使之为 1->1'->2->2'->3->3'->4->4' 2、那么我们通过找到源节点,即可找到其copy节点的位置(源节点.next),相当于哈希表的作用 3、最后根据原链表的rand关系,链接copy节点的rand指针 4、最后将链表拆分为原链表和copy链表

先复制节点,达到哈希表的作用,然后利用原链表的rand、next关系进行重新连接,最后一个一个拆分,因为一个隔一个是自己复制的

注意:一定要把rand复制完再进行分裂,不然后面的元素要连在前面的节点上,会找不到的

链表找环 

 基础解法:hash set,先查询每个节点,节点不在set里就加入;第一个查询到已经存在在set里的节点就是第一个入环节点;

快慢指针:使初始的slow、fast指针不在同一位置,有环的链表,slow、fast会相遇(所以一开始的slow、fast不要指在同一位置),然后让fast指针回到远点,slow和fast同时一步一步前进,再次相遇的节点就是入环节点。

单链表只有一个next方向,意味着入环节点只有一个,不会进了再出各种复杂的结构

链表相交

节点相交,相交的节点与节点的值无关,只与节点的内存大小有关。

基础解法:哈希表:把一个链表全放入hash set,然后遍历另一个链表看是否在set中存在,有的话就是第一个相交节点

进阶解法:分别遍历链表,找到链表的长度,末尾节点,

链接:https://www.nowcoder.com/questionTerminal/db55f7f21127403cb268ffad9d23af37
来源:牛客网

大体流程分两部分:
第一部分——判断单个链表是否有环
使用两个指针,一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步;
若快指针最后变为空,则单链表为无环单链表,返回空指针;
若快慢指针在某一时刻指向同一节点,即二者完全一样,则为有环单链表,
此时让快指针重新指向链表头结点,然后快慢指针均一次走一步,
当快慢指针再次相同时,则此节点即为链表入环节点,将其返回。
第二部分——判断链表是否相交
情况一:两链表中一个为有环链表,一个为无环链表,则此时两链表一定不相交,返回空即可;
情况二:两个链表都是无环链表,此时有两种情况,1)两个无环链表相交;2)两个无环链表不相交;
首先遍历两链表,分别得到两个链表的长度,如果两个末尾节点内存地址不同,不可能相交;
取两个长度的差值,然后让长链表先遍历差值长度,此时长链表剩余部分与短链表长度相同,
然后两条链表同时遍历,直到遇到相同节点为止,若相同节点为空,则两链表不相交,返回空,
否则两链表相交,返回相交节点即可;
情况三:两个链表都是有环链表,此时有三种情况,1)两个有环链表不相交;2)两个有环链表相交,且交点在环外;3)两个有环链表相交,且交点在环内。
首先获得两个链表的入环节点,若入环节点相同,则可转变为情况二,此时将入环节点作为链表的终止节点即可;若两个入环节点不同,以一个入环节点开始遍历,若遍历一遍过程中都没有遇见另一个入环节点,则两链表不相交,返回空,若遇到另一入环节点,则说明两链表相交,此时返回任意一个入环节点即可。

代码可参考: 

数据结构与算法 -- 返回两个链表的相交的第一个节点_牛客博客 (nowcoder.net)  

我的leetcode提交 

找单链表的入环节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
 //找到链表第一入环节点,无环返回NULL
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==NULL||head->next==NULL) return NULL;

        //fast的节点初始值要在slow节点的初始值
        ListNode *slow=head->next;
        ListNode *fast=head->next->next;
        while(fast!=NULL&&fast->next!=NULL&&slow!=fast)
//因为有环,不会走到空,不用判断.!!!因为要写slow和fast相等的判断条件,所以初始值要让fast比slow快一
        {
            //也可以把fast、fast->next的判空放到这里,如果为空直接返回NULL但其实都是一样的,重复的代码少了些
            slow=slow->next;
            fast=fast->next->next;
        }
        if(fast==NULL||fast->next==NULL) return NULL;//无环
       //有环
        fast=head;
        while(fast!=slow)
        {
            slow=slow->next;
            fast=fast->next;
        }
        return slow;
    }
};

 

 找两个无环链表的公共部分

/**
 * 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) {
        if(headA==NULL||headB==NULL) return NULL;
         //遍历两个链表,得到长短链表
        int n=0;
        ListNode *pHA=headA;
        ListNode *pHB=headB;
        while(pHA!=NULL)//如果是有环链表,这里的NULL就应该改成第一个入环节点
        {
            n++;
            pHA=pHA->next;
        }
          while(pHB!=NULL)
        {
            n--;
            pHB=pHB->next;
        }

        pHA=n>0?headA:headB;//pHA指向长链表
        pHB=pHA==headA?headB:headA;
        n=abs(n);
        while(n!=0)
        {
            pHA=pHA->next;//长链表遍历差值长度
            n--;
        }
        while(pHA!=NULL&&pHA!=pHB)
        {
            pHA=pHA->next;
            pHB=pHB->next;
        }
        if(pHA==NULL) return NULL;
         return pHA;
            
        
    }
};

删除链表中的某一个节点

给出了给定的节点,那么把下一个节点的值赋值到给定节点,然后直接让给定节点的next赋值成下下个节点;//借尸还魂

但是本身这个节点没有被删除,且无法删除最后一个节点。

要删除某一个节点,一定要给出头结点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值