IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【算法】链表常见算法3 -> 正文阅读

[数据结构与算法]【算法】链表常见算法3

【算法】—— 链表常见算法3

1. 相交链表

1.1 问题描述

给你两个单链表的头节点headAheadB,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回NULL

图示两个链表在节点c1开始相交:

相交链表1

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

1.2 实现思路

? 首先想到的是暴力求解,但是我们不这样实现,通过遍历链表得到两个链表的长度,判断尾结点是否相等,若是相等则为相交链表,若是不相等则不相交,对于相交链表,让长的链表先走够差值,再让两个链表一起走,相遇的位置就是相交的链表处

  1. 分别遍历两个链表,记录出两个链表的长度为7和5,比较两个链表尾结点相等,链表相交

相交链表2

  1. 先遍历长的链表,遍历的结点个数是两个链表长度的差

相交链表3

  1. 两个链表一起遍历,相遇位置就是相交的链表

相交链表4

相交链表5

1.3 代码实现

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //判断链表是否有空
    if (headA == NULL || headB == NULL)
    {
        return NULL;
    }
    
    //求出两个链表的长度
    struct ListNode *curA = headA, *curB = headB;

    int lenA = 0;
    while (curA->next != NULL)
    {
        curA = curA->next;
        lenA++;
    }

    int lenB = 0;
    while (curB->next != NULL)
    {
        curB = curB->next;
        lenB++;
    }
    
    //两个链表的尾结点不相等,则不相交
    if (curA != curB)
    {
        return NULL;
    }

    //长链表先走长度差步
    struct ListNode *longList = headA, *shortList = headB;
    if (lenA < lenB)
    {
        longList = headB;
        shortList = headA;
    }

    int sub = abs(lenA - lenB);
    while (sub--)
    {
        longList = longList->next;
    }

    //两个链表同时走,直到相遇将结果返回
    while (longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return longList;
}

2. 环形链表I

2.1 问题描述

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

2.2 实现思路

带环链表不能直接遍历,否则会进入死循环,所以我们使用快慢指针来判断,快指针走两步慢指针走一步,若是链表带环则两个指针总会相遇,若是不带环,则快指针一定先到NULL

环形链表一1

n圈之后:快慢指针相遇
环形链表一2

为什么快慢指针最后能够相遇?

  1. 快指针先进入环中,慢指针再次进入环中,快慢指针之间就会有距离,此时快指针追赶慢指针。
  2. 快指针走2步,慢指针走一步,两个指针每走一次它们的距离就会缩小一个,直到完全相遇。

快指针走3步,慢指针走1步,是否也能相遇?

不一定。

  1. 快慢指针进入环时的距离为N,快慢指针每走一次它们的差距就会缩小2个。
  2. 若是N是奇数,则追赶上时快指针会跨过慢指针,两者距离为-1,快指针相对于慢指针的距离就成了环长减一。
  3. 若是环长减一也是偶数,则再追一圈就追上了,若环长减一为奇数,则永远也追不上。

即:N为奇数且环长为偶数时不会相遇

2.3 代码实现

//链表结点声明
struct ListNode 
{
    int val;
    struct ListNode *next;
};
bool hasCycle(struct ListNode *head) 
{
    struct ListNode *first = head, *slow = head;
    
    while (first != NULL && first->next != NULL)
    {
        first = first->next->next;
        slow = slow->next;

        if (slow == first)
        {
            return true;
        }
    }

    return false;
}

3. 环形链表II

3.1 问题描述

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

3.2 实现思路

方法一(公式法)

? 快指针走2步,慢指针走1步

? 假设入环前的长度为L,环的长度为C,入环点到相遇点的距离为X

环形链表二1

  1. 因为:slow入环后不可能走过一圈,因为fastslow的距离不可能超过一圈
    所以:slow走过的距离为: L + X L + X L+X

  2. 因为:fast入环后不一定只转了一圈,若是L很长,圈很小,slow入环前fast就已经转了很多圈,假设fastslow入环之前转了n圈( n > = 1 n >= 1 n>=1
    所以:fast走过的距离为: L + n × C + X L + n\times C + X L+n×C+X

  3. 因为:快指针走2步,慢指针走一步,即: f a s t 的距离 = 2 × s l o w 的距离 fast的距离 = 2 \times slow的距离 fast的距离=2×slow的距离
    所以: 2 × ( L + X ) = L + n × C + X 2 \times (L + X) = L+n \times C + X 2×(L+X)=L+n×C+X
    化简为: L + X = n × C L+X = n \times C L+X=n×C
    化简为: L = n × C ? X L = n \times C - X L=n×C?X
    分配律: L = ( n ? 1 ) × C + C ? X L = (n-1) \times C + C - X L=(n?1)×C+C?X
    加括号: L = [ ( n ? 1 ) × C ] + [ C ? X ] L = [(n-1) \times C] + [C - X] L=[(n?1)×C]+[C?X]

  1. L的意义是从开始位置到入环点的距离。
  2. (n-1)*C的意义是转了 n ? 1 n-1 n?1
  3. C-X的意义是从相遇点顺时针到入环点的位置

环形链表二2

结论:指针A从头开始走,B从相遇点开始走,第一个相遇的位置一定是入环点。

实现步骤:

  1. 找到链表的相遇点
  2. 定义A、B指针分别从头和相遇点开始出发,相遇的地方即是入环点

代码实现

//链表结点声明
struct ListNode 
{
    int val;
    struct ListNode *next;
};
struct ListNode *detectCycle(struct ListNode *head) 
{
    //快慢指针寻找相遇点
    struct ListNode *slow = head, *fast = head;
    while (fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        
        //找到相遇点时,A、B分别从头和相遇点开始找入环点
        if (fast == slow)
        {
            struct ListNode *A = head, *B = fast;
   			while (A != B)
    		{
        		A = A->next;
        		B = B->next;
    		}
    		return A;
        }
    }
    return NULL;
}

方法二(转换为相交问题)

  1. 找到相遇点,将相遇点断开,形成相交的两个链表

环形链表二3

  1. 分别以相遇点的下一个结点和开始结点作为两个链表的头结点,找链表的交点

环形链表二4

  1. 还原链表并返回入环点

代码实现

相交链表的函数(在本文第一个算法处实现)

//相交链表函数,返回值为相交结点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB);
struct ListNode *detectCycle(struct ListNode *head) 
{
    //快慢指针寻找相遇点
    struct ListNode *slow = head, *fast = head;
    while (fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        
        if (fast == slow)
        {
            //断开相遇点
            struct ListNode* next = fast->next;
            fast->next = NULL;
            
            //调用链表相交函数找到相交点,就是入环点
            struct ListNode* meet = getIntersectionNode(head, next);
            
            //还原环形链表,并返回入环点
            fast->next = next;
            return meet;
        }
    }
    return NULL;
}

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

4.1 问题描述

? 给你一个长度为n的链表,每个节点包含一个额外增加的随机指针random,该指针可以指向链表中的任何节点或空节点。

? 构造这个链表的深拷贝。 深拷贝的链表应与原链表的数据域完全一致,随机指针的指向与原链表的随机指针的指向一一对应,但是不能直到原链表的结点中

示例 1:

复制链表1

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

复制链表2

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

复制链表3

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

4.2 实现思路

最容易实现的是暴力求解的方法:

  1. 复制原链表结点
  2. 更新random,遍历链表,找到每个结点的random在原链表中指向第几个结点,然后使新链表的random指向自身的第几个结点
  3. 时间复杂度是 O ( n 2 ) O(n^2) O(n2),容易理解但是效率低,我们不实现这种方法

我们使用原地拷贝的方法对如下链表实现深拷贝:

原地拷贝1

  1. 复制每个结点,将其连接在源链表的每个结点的后面

原地拷贝2
展开为下图:
原地拷贝3

  1. 更新random,通过源结点的random找到复制结点的random指向,源节点的下一个结点就是该节点的复制结点

原地拷贝4

展开如下图:
原地拷贝5

  1. 将拷贝链表解下来,形成新链表。将新链表返回

原地拷贝6

代码实现

//链表结点声明
struct Node
{
    int val;
    struct Node *next;
    struct Node *random;
};
struct Node* copyRandomList(struct Node* head)
{
    //链表为空不复制
    if (head == NULL)
    {
        return head;
    }
    
	//复制链表结点
    struct Node* cur = head;
    struct Node* copy = NULL;
    while (cur != NULL)
    {
        copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = copy->next;
    }
    
    //更新random
    cur = head;
    while (cur != NULL)
    {
        copy = cur->next;
        if (cur->random != NULL)
        {
            copy->random = cur->random->next;
        }
        else
        {
            copy->random = NULL;
        }
        
        cur = copy->next;
    }
    
    //解开拷贝链表
    struct Node* copyHead = head->next;
    cur = head;
    while (cur->next->next != NULL)	//最后一个结点没有下一个结点的拷贝结点,所以要单独处理
    {
        copy = cur->next;
        
        //分离拷贝链表
        cur->next = copy->next;
        copy->next = copy->next->next;
        
        cur = cur->next;
    }
    cur->next = NULL;	//源链表的最后一个结点要置空
    return copyHead;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:36:44  更:2022-09-04 01:41:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:15:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码