单链表算法题(二)(超详细版)

news2024/10/18 20:39:44

前言 : 通过算法题 , 学习解决问题的思路 , 再面对类似的算法题时 , 能快速定位解决方案

一 . 链表的回文结构

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

思路一 :

创建新链表 , 对原链表进行反转,结果存储在新链表中,然后遍历新旧链表进行比较

思路二 :

因为牛客网里给了 ---> 链表的长度小于等于900 的这个条件 , 所以我们可以创建一个新数组,遍历链表,把链表里的值存储在数组中,然后定义两个变量(left,right),分别指向数组的最左端和最右段,然后进行对比,相等的时候,left++,right--

1 . 创建数组

2 . 遍历链表,把 链表结点的值 存到数组中

3 . 创建两个变量 (left = 0 , right = i-1)

4 . left 与 right 往中间走 ,相等继续往中间走,都相等返回true , 不相等时返回false

思路三 :

1 . 找到中间结点(快慢指针)

2 . 反转以中间结点为头的链表(三指针法)

3 . 遍历【原链表】和【以中间结点为头】的链表

思路二代码: 

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code here
        int arr[900] = {0};
        int i = 0;
        ListNode* pcur = A;
        //遍历链表,赋值给数组
        while(pcur){
            arr[i++] = pcur->val;
            pcur = pcur->next;
        }
        int left = 0;
        int right = i-1;
        while(left < right)
        {
            if(arr[left] != arr[right])
            {
                return false;
            }
            left++;
            right--;
        }
        return true;;

    }
};

 思路三代码 : 快慢指针和三指针法在单链表算法题(一)(超详细版)-CSDN博客有详细说明

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
  public:
     ListNode* middleNode(ListNode* head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
    ListNode* reverseList( ListNode* head) {
    if(head == NULL)
    {
        return head;
    }
    ListNode* n1,*n2,*n3;
    n1 = NULL;
    n2 = head;
    n3 = head->next;
    while(n2)
    {
        n2->next = n1 ;
        n1 = n2 ;
        n2 = n3 ;
        if(n3)
            n3 = n2->next;
    }
    return n1;
    }

    bool chkPalindrome(ListNode* A) {
       //1.找到中间结点
       ListNode* mid = middleNode(A);

       //2.反转以中间结点为头的链表
       ListNode* right = reverseList(mid);
       
       //3. 遍历左右链表
       ListNode* left = A;
       while(right)
       {
        if(left->val != right->val)
        {
            return false;;
        }
        left = left->next;
        right = right->next;
       }
        return true;
    }
};

二 . 相交链表

相交链表 : . - 力扣(LeetCode)

思路 :

1 . 求两个链表的长度

2 . 计算两个链表的长度差

3 . 找到大/小链表,让大链表先走 长度差步

4 . 遍历两个链表,比较是否存在相同的结点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //计算两个链表长度
    ListNode* pa = headA , *pb = headB;
    int sizeA = 0,sizeB = 0;
    while(pa){
        ++sizeA;
        pa = pa->next;
    }
    while(pb)
    {
        ++sizeB;
        pb = pb->next;
    }
    //计算长度差 -- 绝对值(可以使用函数abs)
    int gap = abs(sizeA - sizeB);

    //找大小链表 -- 让大链表走gap 步
    ListNode* longList = headA;
    ListNode* shortList = headB;
    if(sizeA < sizeB){
        longList = headB;
        shortList = headA;
    }
    while(gap--)
    {
        longList = longList->next;
    }
    //遍历链表 -- 找相交结点
    //存在相交结点
    while(longList){
        if(longList == shortList){
            return longList;
        }
        longList = longList->next;
        shortList = shortList->next;
    }
    //不存在相交结点
    return NULL;

}

三 . 环形链表I

环形链表I : . - 力扣(LeetCode)

思路 : 快慢指针

慢指针每次走一步,快指针每次走两步 ,两个指针从链表起始的位置开始运行;若链表带环 ,快指针在环内追逐 ,快慢指针会相遇 ; 若不带环 ,快指针率先走到链表的末尾 ; 

这道题重在证明思路 :

1 ) 为什么慢指针每次走一步,快指针每次走两步,快慢指针会相遇?会遇不上吗?

2 ) 快指针能不能一次走3步,走4步,...n步?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* slow = head;
    ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //快慢指针相遇,存在环
        if(slow == fast)
        {
            return true;
        }
    }
    //没相遇,不存在环
    return false;
}

3.1 证明一:

1 ) 为什么慢指针每次走一步,快指针每次走两步,快慢指针会相遇?会遇不上吗?

fast 一次走两步 , slow 一次走一步 , 如果链表中存在环 , 那么快指针先进入环 ,假设slow也走完入环前的距离为N(最大距离),在接下来的追逐过程中,每追击一次,他们之间的距离缩小一步

追击过程中 fast 和slow 的变化 :

N - 1   =>      N - 2         =>      N-3       =>  ......   =>    2    =>    1   =>    0 (相遇了)

3.2 证明二 : 

2 ) 快指针能不能一次走3步,走4步,...n步?

 按照上面的分析,慢指针每次走一步,快指针每次走三步,此时快慢指针的最大距离为N,接下来的追逐中,每追逐一次,他们之间的距离缩小2步

追逐过程中fast和slow之间的距离变化:

分析 :

1 . 如果N是偶数 , 第一轮就追上了

2 . 如果N是奇数 , 第一轮追不上,错过了,距离变为-1,即C-1 , 进入新的一轮追击

    1) C-1如果是偶数,那么下一轮就追上了

    2) C-1如果是奇数,那么就追不上

总结一下追不上的条件 :N是奇数,C是偶数 (证明是否存在N为奇数,C为偶数的情形)

假设 :

  环的周长为C,头结点到slow的长度为L , slow 走一步 ,falst 走三步 , 当slow 指针 入环后,slow和fast 指针在环中开始进行追逐 , 假设此时的fast 已经绕环 n 周

 同理 : 对快指针走 4 , 5 ... 步最终也会相遇,证明方法同上!

typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {
    ListNode *slow, *fast;
    slow = fast = head;
    while (fast && fast->next) {
        slow = slow->next;
        int n = 3; // fast每次⾛三步
        while (n--) {
            if (fast->next)
                fast = fast->next;
            else
                return false;
        }
        if (slow == fast) {
            return true;
        }
    }
    return false;
}

 提示 :

虽然已经证明了 , 快指针无论走多少步都可以满足在带环链表中相遇 , 但是在编写代码的时候,会有额外的步骤引入,涉及快慢指针的算法题中,通常习惯使用快指针每次走两步,慢指针走一步的方式。

四 . 环形链表II

环形链表II :. - 力扣(LeetCode)

主要思路 : 快慢指针的思想

(快指针 fast 每次走两步 , 慢指针 slow 每次走一步)

创建两个指针(快慢指针) , 如果链表带环 , 存在相遇结点 , 此时的相遇结点到入环结点的距离头结点到入环结点的距离  相同 ! 

1 . 创建指针变量(快慢指针)

2 . 判断快慢指针走到的结点是否相同 ---> 相同(带环) 

3 . 创建指针变量(pcur)(初始化为头结点)

4 . pcur 与 slow 不相等时 , 两个指针一直往后走

5 . 返回相等时的结点

证明 : 为什么带环链表中 , 相遇点到入环结点的距离 等于 头结点到入环结点的距离?

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode; 
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇点
        if(slow == fast)
        {
            ListNode* pcur = head;
            while(slow != pcur)
            {
                pcur = pcur->next;
                slow = slow->next;
            }
            //找到入环点了
            return pcur;
        }
    }
    return NULL;
}

4.1 证明

证明 : 为什么带环链表中 , 相遇点到入环结点的距离 等于 头结点到入环结点的距离?

( 如下图 )

假设 : 头结点到入环结点的距离为 L , 相遇结点为M , 环的长度为X , 链表如果带环 , 在相遇点 , 快指针到入环结点的距离为   R-L

1 .  求出相遇时 , 快慢指针所走的路径长度

fast = L + X + nR

slow = L + X

注 :  当慢指针进入环的时候 , 快指针可能已经在环中绕了n 圈 , n 至少为 1 (因为快指针先进环 ,先走到 M的位置 , 最后在M的位置和慢指针相遇)

2 .  快指针 = 2 * 慢指针

fast = 2 * slow

L + X + nR = 2 * ( L + X )

nR = L + X

( n-1 )R + R - X = L

( n 为 1, 2 , 3 , 4 .... , n 的大小取决于环的大小 , 环越小n 越大)

极端情况下 , 假设 n = 1 , 此时 : L = R - X

即 : 一个指针从链表起始结点运行 , 一个指针从相遇结点绕环 , 每次走一步 , 两个指针最终会在入口结点相遇 

五 . 随机链表的复制

随机链表的复制 : . - 力扣(LeetCode)

难点 : 深拷贝 --> 重新申请一块空间存储一样的结点 ---> 可是如果创建新链表 , 但是random 指向的结点还没有创建好 , 会报错,非法访问空间

所以我们可以在原链表中 , 先把值进行拷贝 , 然后此时random 指向的结点是存在的 ,通过遍历拷贝的结点 和 原链表的结点把random的指向处理好 , 最后断开与原链表的结点的连接即可

1 )  拷贝结点

2 )  置random

3 )  断开连接

注意 : 当链表为空时 ,直接返回head

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
typedef struct Node Node;
Node* buyNode(int x)
{
    Node* node = (Node*)malloc(sizeof(Node));
    node->val = x ;
    node->next = node->random = NULL;
    return node;
}
void AddNode(Node* head)
{
    Node* pcur = head;
    while(pcur)
    {
        Node* next = pcur->next;
        Node* newnode = buyNode(pcur->val);
        //尾插
        newnode->next = next;
        pcur->next = newnode;
        pcur = next;
    }
}

void SetRandom(Node* head)
{
    Node* pcur = head;
    while(pcur)
    {
        Node* copy = pcur->next;
        if(pcur->random)
            copy->random = pcur->random->next;
        pcur = copy->next;
    }
}
struct Node* copyRandomList(struct Node* head) {
	if(head == NULL)
    {
        return head;
    }
    //1.拷贝结点
    AddNode(head);
    //2.置randow
    SetRandom(head);
    //3.断开链表
    Node* pcur = head;
    Node* newHead,*newTail;
    newHead = newTail = pcur->next;
    while(newTail->next)
    {
        pcur = newTail->next;
        newTail->next = pcur->next;
        newTail = pcur->next;
    } 
    return newHead;
}

 

 写算法题目时 , 如果暂时没有解题思路可以先画画图 , 打开新大陆!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218003.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计Python深度学习房价预测 房源可视化 房源爬虫 二手房可视化 二手房爬虫 递归决策树模型 机器学习 深度学习 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 房地产是促进我国经济持续增…

Google play开发者账号被封,申诉就有机会,别不信

在谷歌上架&#xff0c;开发者账号被封对很多开发者来说已经是家常便饭了&#xff0c;虽说一直都有在流传申诉没有用。别灰心啊&#xff0c;申诉就有机会&#xff0c;不少开发者都申诉成功了。 尤其是用一个少一个、价值好几个w的老号&#xff0c;不申诉就认栽实在是太亏了&…

Bootstrap 弹出框(Popover)插件

弹出框&#xff08;Popover&#xff09;与工具提示&#xff08;Tooltip&#xff09;类似&#xff0c;提供了一个扩展的视图。如需激活弹出框&#xff0c;用户只需把鼠标悬停在元素上即可。弹出框的内容完全可使用 Bootstrap 数据 API&#xff08;Bootstrap Data API&#xff09…

Mysql底层原理详细剖析

1. 索引数据结构 索引是帮助mysql 是帮助数据排序 且高效获取数据的数据结构 索引的数据结构有&#xff1a; 二叉树红黑树hash表b树 1.1 二叉查找树 二叉查找树 如果要查找&#xff0c;通过二分查找的复杂度进行查找数据&#xff0c;确实优化了性能&#xff0c;减少了io的…

【中危】Oracle TNS Listener SID 可以被猜测

一、漏洞详情 Oracle 打补丁后&#xff0c;复测出一处中危漏洞&#xff1a;Oracle TNS Listener SID 可以被猜测。 可以通过暴力猜测的方法探测出Oracle TNS Listener SID&#xff0c;探测出的SID可以用于进一步探测Oracle 数据库的口令。 建议解决办法&#xff1a; 1. 不应该使…

机器学习—特性缩放

特性缩放的技术能使梯度下降运行得更快&#xff0c;让我们先来看看功能大小之间的关系&#xff0c;这就是该特性的数字和相关参数的大小&#xff0c;作为一个具体的例子&#xff0c;让我们用两个特征来预测房子的价格&#xff0c;X1代表一个房子的大小&#xff0c;X2代表两个卧…

python爬取themoviedb电影网站信息

python爬取themoviedb电影网站信息 一、寻找数据接口二、解析主页数据,获取详情页url三、向详情页url发送请求、获取并解析数据四、完整代码一、寻找数据接口 打开网站首页,F12打开开发者工具,刷新页面。 向下滑动页面,点击页面上的“Load More”图标。 寻找到数据接口,…

掘金2.计算位置 x 到 y 的最少步数(简单01)

public class Main {public static int solution(int xPosition, int yPosition) {int diff (yPosition - xPosition);// 计算差值if(diff < 0)diff * -1;int steps 0; // 初始化步数int begin 0;// 初始化当前位置int step 1;//初始化步长// 循环直到到达目标位置while…

DSVPN简介与应用

目录 简介 DSVPN 封装模式 Nomal&#xff08;动态&#xff09;方式建立DSVPN 一、配置缺省包过滤 二、划分区域&#xff08;以总部为例&#xff09; 三、配置IP地址&#xff08;以R1为例&#xff09; 四、配置DSVPN 简介 DSVPN DSVPN&#xff08;Dynamic Secure Vir…

一种3D打印跑车模型LED安全夜灯

我学习入门单片机及3Dmax的副产品&#xff0c;小玩意。MCU用8脚的就好&#xff0c;多脚功能复用&#xff0c;涉及长短按中断、ADC、掉电唤醒及LED切换控制&#xff0c;硬件的充放电监控及光控等等麻雀虽小五脏俱全。发使用指南不是广告&#xff0c;感觉这样才能毫无遗漏的说明其…

动态规划(1)斐波那契数列模型

动态规划算法流程&#xff1a; 1、状态表示&#xff1a; 指的是dp&#xff08;dynamic programming&#xff09;表里面的值所表示的含义 如何得出&#xff1a;1、题目要求 2、经验题目要求 3、分析问题的过程中发现重复子问题 2、状态转移方程 dp[i]等于什么 3、初始化 保证…

RAG拉满-上下文embedding与大模型cache

无论怎么选择RAG的切分方案&#xff0c;仍然切分不准确。 最近&#xff0c;anthropics给出了补充上下文的embedding的方案&#xff0c;RAG有了新的进展和突破。 从最基础的向量查询&#xff0c;到上下文embedding&#xff0c;再到rerank的测试准确度都有了明显的改善&#xf…

【无标题】如何在Costura.Fody生成时排除掉某些dll

有个场景需要排除掉某些dll让他不要打包到exe中,这样做,修改FodyWeavers.xml

配合工具,快速学习与体验electron增量更新

有任何问题&#xff0c;都可以私信博主&#xff0c;共同探讨学习。 正文开始 前言一、如何使用源码1.1 下载代码1.2 下载资源1.3 运行项目 二、如何使用工具2.1 打包新版本更新包2.2 创建nginx文件服务器2.3 在文件服务器保存软件更新包 三、如何测试更新3.1本地运行低版本3.2 …

九、PESocket通信

知识点&#xff1a;高并发 1、下载PESocket 地址&#xff1a;PlaneZhong/PESocket: A C# Network Library. (github.com) 2、示例代码 发过去一个Hello&#xff0c;返回一个hello 当一个客户端关闭了&#xff0c;会出现一个提示 当一个客户端开启&#xff0c;会显示已连接 3…

运放类公式计算

简介 很多运放的GAIN采用dB的方式表达放大倍数&#xff0c;然而我们有时候习惯使用电压的倍数代表运放放大关系&#xff0c;本章主要简单介绍dB与电压转换的关系。 例如某运放的放大倍数如下&#xff1a; G1G2GAIN(dB)0029.60119.110131116 以上放大倍数我们无法知道输入的信号…

有趣的在线可视化网站:探索神经网络与矩阵运算

有趣的在线可视化网站&#xff1a;探索神经网络与矩阵运算 文章目录 有趣的在线可视化网站&#xff1a;探索神经网络与矩阵运算一 TensorFlow Playground 神经网络二 Symbolab 的矩阵迹计算器三 Matrixmultiplication 可视化教学工具 本文推荐了几个非常有趣且实用的在线可视化…

sql实战解析-sum()over(partition by xx order by xx)

该窗口函数功能 sum( c )over( partition by a order by b) 按照一定规则汇总c的值&#xff0c;具体规则为以a分组&#xff0c;每组内按照b进行排序&#xff0c;汇总第一行至当前行的c的加和值。 从简单开始一步一步讲&#xff0c; 1、sum( )over( ) 对所有行进行求和 2、sum(…

静态站点生成器哪家强?

有一种方法&#xff0c;让你写好文档后&#xff0c;快速地让同事、用户和合作伙伴看到&#xff0c;这就是静态站点生成器。 静态站点生成器是一种软件&#xff0c;用于创建不需要服务器端脚本的网站。这些网站由纯HTML文件组成&#xff0c;可能还包括CSS和JavaScript来增强功…

【PhpSpreadsheet】ThinkPHP5+PhpSpreadsheet实现批量导出数据

目录 前言 一、安装 二、API使用 三、完整实例 四、效果图 前言 为什么使用PhpSpreadsheet&#xff1f; 由于PHPExcel不再维护&#xff0c;所以建议使用PhpSpreadsheet来导出exlcel&#xff0c;但是PhpSpreadsheet由于是个新的类库&#xff0c;所以只支持PHP7.1及以上的版…