训练营day4|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点 、面试题02.07. 链表相交 、142.环形链表II

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

  • 题目链接24. 两两交换链表中的节点
  • 独立思路:❌ 没想出来,只想得出交换结点值的
  • 解法1(递归):我悟了!重点是取一对相邻结点A->B为研究对象,假设后续的pairs全部都已经交换完成,且返回的是已经完成交换的链表部分的新头结点newHead。则本对结点交换后的新头结点 newHead = BA->next = swapPairs(B->next),最后将newHead->next = head即可。
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
class Solution{
public:
	ListNode* swapPairs(ListNode* head){
        if(!head || !head->next)	//如果链表为空或者只有一个结点,则没什么好交换的
            return head;
        ListNode* newHead = head->next;
        head->next = swapPairs(newHead->next);	//后面链表的开头结点是原来的B->next
        newHead->next = head;
        return newHead;
    }
};

  • 解法2(迭代):重点是设置一个dummyHead,以及一个cur指针来从dummy开始遍历整个链表,需要进行循环处理的是紧接temp之后的两个结点node1和node2,直到cur指针后面没有结点或者只有1个结点时退出循环。
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
//解法2:迭代
class Solution{
public:
    ListNode* swapPairs(ListNode* head){
        if(!head)
            return head;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* cur = dummy;
        
        while(cur->next && cur->next->next){
            ListNode* node1 = cur->next;
            ListNode* node2 = node1->next;
            cur->next = node2;
            node1->next = node2->next;
            node2->next = node1;
            cur = node1;
        }
        
        ListNode* newHead = dummy->next;
        delete dummy;
        return newHead;
    }
};

19.删除链表的倒数第N个节点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *left = head;
        ListNode *right = head;
        ListNode *pre;
        while(n != 0 && right != NULL){
            right = right->next;
            n--;
        }
        if(n != 0){     //n还没有减到0时,right就已经为NULL,说明链表根本不够长
            return NULL;
        }
        else if(right == NULL){     //n=0且right已经到了NULL,说明要删除的是第一个结点
            return head->next;
        }
        else{       //n=0且right还没到NULL时
            while(right != NULL){
                pre = left;
                left = left->next;
                right = right->next;
            } //结束循环时,left指向倒数第n个结点,pre指向其前一个结点
            pre->next = left->next;
            return head;
        }
    }
}; //还可以添加个dummyHead更简单点

面试题02.07. 链表相交

  • 题目链接面试题02.07. 链表相交
  • 独立思路:计算两个链表的长度,更长的那个链表重新设置开始结点,让两个指针cur1和cur2分别从两个链表的长度相等的地方开始遍历,cur1 == cur2时即为相交处
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(1)
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int len1 = 0; int len2 = 0;
        ListNode* cur1 = headA;
        ListNode* cur2 = headB;
        while(cur1){
            len1++;
            cur1 = cur1->next;
        }
        while(cur2){
            len2++;
            cur2 = cur2->next;
        }//计算A、B链表的长度

        cur1 = headA;
        cur2 = headB;   //两个遍历指针归位
        if(len1 == len2){   //两链表等长
            while(cur1){
                if(cur1 == cur2)
                    return cur1;
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
        }else if(len1 < len2){     //链表B更长
            while(len2 != len1){
                cur2 = cur2->next;
                len2--;
            }//强行让A、B同长度开始遍历
            while(cur1){
                if(cur1 == cur2)
                    return cur1;
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
        }else{      //链表A更长
            while(len1 != len2){
                cur1 = cur1->next;
                len1--;
            }//强行让A、B同长度开始遍历
            while(cur1){
                if(cur1 == cur2)
                    return cur1;
                cur1 = cur1->next;
                cur2 = cur2->next;
            }
        }

        return NULL;
    }
};

  • 另解1(哈希表):遍历A链表,将A表中的结点全部存入set表中(set可以用find函数找元素),再遍历B链表,判断当前遍历到的B表结点是否存在于set表中,(若存在则后续链表结点都存在),即直接返回该结点
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(m)
//解法2:哈希表
class Solution{
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB){
        unordered_set<ListNode*> visited;
        ListNode* cur1 = headA;
        ListNode* cur2 = headB;
        while(cur1){
            visited.insert(cur1);
            cur1 = cur1->next;
        }

        while(cur2){
            if(visited.find(cur2) != visited.end()){
                return cur2;
            }
            cur2 = cur2->next;
        }

        return NULL;
    }
};

  • 另解2(循环双指针):假设链表A和B公共部分的长度为x,非公共部分的长度分别为a和b。cur1和cur2分别从头开始遍历两个链表,当cur1≠cur2,且遍历到了尾部,指针走到另一个链表的开头继续走,直到cur1=cur2。原理就是a+c+b = b+c+a。
  • 问题(已解决):while循环内的两个判断,只能判断cur是否为空而不能判断cur->next是否为空!因为在两个链表不相交的情况下,如果是判断cur->next为空时就跳转到另一个链表的开头,那么两个cur永远不可能同时为NULL,会陷入死循环。
  • 时间复杂度:O(m+n)
  • 空间复杂度:O(1)
//解法3: 循环双指针
class Solution{
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB){
        if(!headA || !headB)
            return NULL;
        ListNode* cur1 = headA;
        ListNode* cur2 = headB;
        while(cur1 != cur2){	
            cur1 = cur1? cur1->next: headB;	//只能判断cur而不能判断cur->next
            cur2 = cur2? cur2->next: headA;
        }

        return cur1;

    }
};

142.环形链表II

  • 题目链接142.环形链表II
  • 独立思路:如果有环,快慢指针一定会相遇(好像做过一模一样的题,但是想不起来后面的了)
  • 官方题解:快慢指针两轮运动,设链表从head到环起点部分长度为a,环的部分长度为b。
    ① slow走一步,fast走两步,直到fast==slow,此时f = 2s且f = s+nb。得f = 2nb s = nb

② 将fast又重新置为head,这一轮slow与fast同步走了a步,相遇的位置即为环的起点位置(fast走到入口,slow则总共走了nb+a,也走到了入口)

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
//想法:如果有环,快慢指针一定会相遇
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head || !head->next)    //0或1个结点无法构成环
            return NULL;
        ListNode* slow = head;
        ListNode* fast = head;
        
        while(fast && fast->next){
            slow = slow->next;      //第一轮,slow慢走,fast快走
            fast = fast->next->next;

            if(fast == slow){       
                fast = head;    //第二轮出发,slow、fast同速走
                while(fast != slow){    
                    fast = fast->next;
                    slow = slow->next;
                }
                return fast;
            }
        }

        return NULL;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值