链表的OJ题_续

目录

1.链表分割

2. 链表的回文结构

 3.相交链表

 4.判断环形链表

 5.环形链表II

 延展问题

 延展问题

6.复制带随机指针的链表 


1.链表分割

链表分割_牛客题霸_牛客网【牛客题霸】收集各企业高频校招笔面试题目,配有官方题解,在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking

我们需要将比x小的链接lessTail保存在一起,将比x大的链接greaterTail保存在一起。

最后lessTail的链接指向greaterTail的第一个元素 ,且我们要返回新连接的头指针

故此还需要定义一个lessHead和greaterHeat

开一个哨兵位头结点,方便尾插 

	struct ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;
	//开一个哨兵位头结点,方便尾插
	lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	lessTail->next = NULL;
	greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	greaterTail->next = NULL;

如何进行比较?

定义一个cur  ,将cur->val 与  x 进行比较。

如果比x小,那便让lesstail 链接到 cur上,并将lesstail走到cur处。  结束后 cur = cur->next

如果比x大,那便让greatertail 链接到 cur上,并将greatertail走到cur处。  结束后 cur = cur->next

	struct ListNode* cur = pHead;
	while (cur)
	{
		if (cur->val < x) {
			lessTail->next = cur;
			lessTail = cur;
		}
		else {
			greaterTail->next = cur;
			greaterTail = cur;
		}
		cur = cur->next;
	}

 比较完后,便需要将两者链接起来,由于我们开头都定义了一个哨兵位,故此是将

lessTail->next = greaterHead->next;

返回的lessHead的地址,但结束后我们需要    free(lessHead)和free(greaterHead)的空间,所以我还需要以将lessHead的地址保存下来

	lessTail->next = greaterHead->next;
	greaterTail->next = NULL;
	struct ListNode* newHead = lessHead->next;
	free(lessHead);
	free(greaterHead);
	return newHead;

完整代码如下: 

ListNode* partition(ListNode* pHead, int x) {
	struct ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;
	lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	lessTail->next = NULL;
	greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
	greaterTail->next = NULL;
	struct ListNode* cur = pHead;
	while (cur)
	{
		if (cur->val < x) {
			lessTail->next = cur;
			lessTail = cur;
		}
		else {
			greaterTail->next = cur;
			greaterTail = cur;
		}
		cur = cur->next;
	}
	lessTail->next = greaterHead->next;
	greaterTail->next = NULL;
	struct ListNode* newHead = lessHead->next;
	free(lessHead);
	free(greaterHead);
	return newHead;
}

2. 链表的回文结构

链表的回文结构_牛客题霸_牛客网

此题思路我只需要将原链表逆置保存到一个新的链表中,在于原链表一一对比,若相一致便是回文结构,不一致便返回false

    struct ListNode* reverseList(struct ListNode* head)
    {
        struct ListNode* newhead = NULL;
        struct ListNode* cur = head;
        while(cur)
        {
            struct ListNode* next = cur->next;
            //头插
            cur->next = newhead;//将newhead看作首地址
            newhead = cur;
            //迭代往后走
            cur = next;
        }
        return newhead;
    }
    bool chkPalindrome(ListNode* A) 
    {
        // write code here
        struct ListNode* curA = A;
        struct ListNode* curR = reverseList(A);
        while(curA && curR)
        {
            if(curA->val != curR->val){
                return false;
            }
            else{
                curA = curA->next;
                curR = curR->next;
            }
        }
        return true;
    }

 3.相交链表

力扣https://leetcode.cn/problems/intersection-of-two-linked-lists/description/

此处,我们判断A和B链表相交,只需要判断他们是否出现地址相同即可。

两个链表都先遍历第一遍,如果尾部的数据地址不相同,那必然没有相交,返回NULL即可,倘若尾部地址相同,

那么我们需要让他们同时走到地址相同处,但我们又不知道他们的长度如何,所以两者都是一步一步的迭代,但是长的链表要先迭代他们的长度之差步。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* tailA = headA;
    struct ListNode* tailB = headB;
    int lenA = 1;
    int lenB = 1;
    while(tailA->next){
        ++lenA;
        tailA = tailA->next;
    }
    while(tailB->next){
        ++lenB;
        tailB = tailB->next;
    }
    if(tailA != tailB){
        return NULL;
    }
    int gap = abs(lenA-lenB);
    //让长的先走gap步
    //假设 A 长B短,再判断,如果A短B长则相反赋值
    struct ListNode* longList = headA;
    struct ListNode* shorList = headB;
    if(lenA < lenB){
        longList = headB;
        shorList = headA;
    }
    while(gap--){
        longList = longList->next;
    }
    while(longList != shorList){
        longList = longList->next;
        shorList = shorList->next;
    }
    return longList;
}

 4.判断环形链表

力扣https://leetcode.cn/problems/linked-list-cycle/description/设定一快一慢两个链表, slow每次迭代一步,fast每次迭代两步。

如果存在环形链表,那么slow和fast必定会在环形中相遇。

bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            return true;
        }
    }
    return false;
}

 5.环形链表II

此处主要是需要证明一个数学问题,证明在下面

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            //相遇 
            struct ListNode* meet = slow;
            while(meet != head){
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

 延展问题

1.为什么slow 和 fast一定会在环内相遇?会不会在环内错过,永远遇不上? 证明一下 
 结论:一定会相遇
 分析证明:
 第一步: slow和fast,fast一定先进环,这是 slow走了fast进环前的一半路程
 第二步: 随着slow进环,fast已经在环里面走了一段,走了多少未知
                假设fast进环的时候,slow与fast的距离为N,fast开始追slow
                slow往前走一步,fast往前走2步,每追一次,判断一次相距
                每追一次,fast和slow的距离变化为n-1、n-2、n-3 直至变成0时相遇
 如何求相遇点?
 第一种方法:
 一个指针从相遇点开始走,一个指针从链表头开始走,他们会在环的入口点相遇
 追上的过程中:(L是环前的链表前度,C是环的长度,X是慢指针在和快指针的相遇点)
 慢指针走的距离:L+X
 快指针走的距离:L+N*C+X
 快指针走的距离是慢指针的两倍:
 2(L+X) = L+N*C+X(N>=1)
 L = N*C-X
 L = (N-1)*C + C-X;
     整数圈    
 第二种方法:
 转换为链表相交问题
 将相遇点指向NULL
 从相遇点的下一个节点 当作新的链表,走到相遇点
 再从原链表的表头开始走,走到fast和slow相遇点, 
 中间两者地址相同的地方,就是环的第一个节点


 延展问题

2.为什么slow走一步,fast走两步?能不能fast一次走3、4、5、n步?请证明一下 
 结论:fast一次走n步,n>2 不一定会相遇
 假设 fast一次走三步,
        slow进环后,fast开始追slow,距离为N
 他们间的距离变化:
        N、N-2、N-4…… 
        如果N是 偶数  …… 2、0    可以追上
        如果N是 奇数  …… 1、-1一次追不上。距离变成-1,意味着他们之间的距离变成C-1(C是环的长度)
        如果C-1 是奇数,那么永远追不上,如果C-1是偶数,那么就可以追上

6.复制带随机指针的链表 

1.先复制每一个节点,插入原节点的后面,

2.将copy的random指向原链表的random的下一个

3.最后将原链表与复制的链表分别链接

struct Node* copyRandomList(struct Node* head) {
	//1.复制节点,插入到每一个原节点的后面
    struct Node* Cur = head;
    while(Cur)
    {
        struct Node* Copy = (struct Node*)malloc(sizeof(struct Node));
        Copy->val = Cur->val;
        
        //插入copy点
        Copy->next = Cur->next;
        Cur->next = Copy;

        Cur = Copy->next;
    }

    //2.根据原节点,处理copy节点的random
    Cur =  head;
    while(Cur)
    {
        struct Node* Copy = Cur->next;
        if(Cur->random == NULL){
            Copy->random = NULL;
        }
        else{
            Copy->random = Cur->random->next;
        }
        Cur = Copy->next;
    }
    //把拷贝节点解下来,连接成新链表,同时恢复原链表
    struct Node* CopyHead = NULL, *CopyTail = NULL;
    Cur = head;
    while(Cur)
    {
        struct Node* Copy = Cur->next;
        struct Node* next = Copy->next;

        if(CopyTail == NULL){
            CopyHead = CopyTail = Copy; 
        }
        else{
            CopyTail->next = Copy;
            CopyTail = Copy;
        }
        Cur->next = next;
        Cur = next;
    }
    return CopyHead;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值