链表OJ面试题浅刷< 2 >(较难)

目录

一 链表的回文结构

二 链表的相交

三 复制带随机指针的链表


一 链表的回文结构

原题链接:

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

⭐:回文结构也就是对称结构,即一串结构正着读和反着读一模一样。

例如:1 2 2 1  或者 1 2 3 2 1

思路:

先找到链表的中间结点,然后再对链表中间结点往后的结点进行逆置。逆置后与中间结点前面的结点进行比较。(求链表中间结点和链表逆置在上篇博客已经讲过,这里不再过多赘述.)

 注:因为C++包容C,所以题解用的C语言写的。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    //求链表的中间结点
    struct ListNode* middleNode(struct ListNode* head) {
        struct ListNode* slow, * fast;
        slow = fast = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }

        return slow;
     }
    //反转链表
    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 = cur;

            cur = next;
        } 
	      return newHead;
    } 
    bool chkPalindrome(ListNode* A) {
        struct ListNode*mid=middleNode(A);
        struct ListNode*rHead=reverseList(mid);
        while(A && rHead)
        {
            if(A->val == rHead->val)
            {
                A = A->next;
                rHead = rHead->next;
            }
            else
            {
                return false;
            }
        }
        return true;
    }
};

二 链表的相交

原题链接:

160. 相交链表 - 力扣(LeetCode) (leetcode-cn.com)

 思路1:

A链表的每个结点跟B链表依次比较,如果有相等,就是相交,第一个相等的结点就是交点。此种方法的时间复杂度为O(M*N).

 思路2:

对比A链表和B链表的尾结点,如果两个链表的尾结点地址相同,则链表就是相交。然后我们可以求出两个链表的长度La和Lb,让长的先走|La-Lb|步,再一起走,第一个地址相等的结点就是相交点。此种方法的时间复杂度为O(M+N).

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
	struct ListNode* tailA = headA, * tailB = headB;
	int lenA=1, lenB=1;//因为后面找链表的尾结点会少算一个,所以将它们都初始化为1
	//找链表的尾结点 
	while (tailA->next)
	{
		tailA = tailA->next;
		lenA++;
	}

	while (tailB->next)
	{
		tailB = tailB->next;
		lenB++;
	}
	//如果尾结点不相同,则说明两个链表不相交,返回空指针
	if (tailA != tailB)
	{
		return NULL;
	}

	//相交,求交点,长的先走差距步,再同时走找交点。
	struct ListNode* shortList = headA, * longList = headB;
	if (lenA > lenB)
	{
		longList = headA;
		shortList = headB;
	}

	//求出差距步
	int gap = abs(lenA - lenB);
	while (gap--)
	{
		longList = longList->next;
	}

	 //求相交结点
	while (shortList && longList)
	{
		if (shortList == longList)
		{
			return shortList;
		}
		shortList = shortList->next;
		longList = longList->next;
	}

	//按照语法逻辑,这里仍需要写返回值,要不然会报语法错误即编译出错
	return NULL;
}

三 复制带随机指针的链表(较难)

原题链接:

力扣

 这道题的题目看起来挺吓人的,现在让我们慢慢的分析一下。

法一:

题目要求我们拷贝一个新的链表,那么我们可以malloc出一个新的链表。我们创建一个指针 变量cur依次遍历链表,比如cur遍历到第一个结点,那么我们就malloc出一个和第一个结点数据相同的结点。然后再遍历再malloc,继续尾插,直到结束。对于random指针,我们可以用找相对距离的方法,原链表的random指向第几个,新链表的random也指向第几个。此方法时间复杂度为O(N^2).

法二:

首先我们把所需要拷贝的结点连接在原结点的后面

struct Node* cur = head;
//拷贝节点链接在原节点后面
while (cur)
{
    struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
    copy->val = cur->val;
    copy->next = cur->next;
    cur->next = copy;
    cur = cur->next->next;
}

然后通过原结点找到所对应的random

比如我们所拷贝出来的数据为13的结点,拷贝出来的结点就是原来数据为13的结点的next。因为在原链表中13的random指向7,所以在我们拷贝出来的13的random也要指向拷贝出来的7,而在原链表中7的next指向拷贝出的7。所以拷贝出来的13的random就是原来的13的random的next。其他结点类推。

//更新拷贝节点的random
cur = head;
while (cur)
{
    struct Node* copy = cur->next;
    if (cur->random == NULL)
    {
        copy->random = NULL;
    }
    else
    {
        copy->random = cur->random->next;
    }
    cur = cur->next->next;
}

恢复原链表,把拷贝的链表连接在一起。

//拷贝节点与原结点分离,连接在一起
cur = head;
struct Node* copyTail = NULL;
struct Node* copyHead = NULL;
while (cur)
{
    struct Node* copy = cur->next;
    struct Node* next = copy->next;
    cur->next = next;
    if (copyTail == NULL)
    {
        copyHead = copyTail = copy;
    }
    else
    {
        copyTail->next = copy;
        copyTail = copyTail->next;
    }
    cur = next;
}
return copyHead;
}

总代码:

struct Node* copyRandomList(struct Node* head) {
    struct Node* cur = head;
    //拷贝节点链接在原节点后面
    while (cur)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = cur->next->next;
    }
    //更新拷贝节点的random
    cur = head;
    while (cur)
    {
        struct Node* copy = cur->next;
        if (cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }
        cur = cur->next->next;
    }
    //拷贝结点与原结点分离,连接到一起
    cur = head;
    struct Node* copyTail = NULL;
    struct Node* copyHead = NULL;
    while (cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        cur->next = next;
        if (copyTail == NULL)
        {
            copyHead = copyTail = copy;
        }
        else
        {
            copyTail->next = copy;
            copyTail = copyTail->next;
        }
        cur = next;
    }
    return copyHead;
}

勿忘点赞噢~

  • 54
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 44
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值