【剑指offer专项突破版】链表篇——“C“

news2024/11/17 21:50:15

文章目录

  • 前言
  • 一.删除链表的倒数第 n 个结点
    • 题目分析
    • 思路分析
    • 细节分析
    • 步骤
    • 代码
  • 二.链表中环的入口节点
    • 题目分析
    • 思路分析
    • 写法①代码
    • 写法②代码:
  • 三.两个链表的第一个重合节点
    • 题目分析
    • 思路分析
    • 代码
  • 四.反转链表
    • 题目分析
    • 思路分析
    • 法①代码
    • 法②代码
    • 法③代码
  • 五.链表中的两数相加
    • 题目分析
    • 思路分析
    • 代码
  • 六.重排链表
    • 题目分析
    • 思路分析
    • 代码
  • 七.回文链表
    • 题目分析
    • 思路分析
    • 代码
  • 八. 展平多级双向链表
    • 题目分析
    • 思路分析
    • 代码
  • 九.排序的循环链表
    • 题目分析
    • 思路分析
    • 代码

前言

剑指offer专项突破版(力扣官网)——> 点击进入
本文所属专栏——>点击进入

一.删除链表的倒数第 n 个结点

题目分析

![在这里插入图片描述](https://img-blog.csdnimg.cn/8db53ca053dc44afbce2bb45824524a5.

  • 总结

数据格式——链表——结点数大于等于1,且n是符合要求的!
要求——删除链表的n个结点
返回——返回链表的头结点

思路分析

倒数第n个结点——>到最后一个结点的步长为n-1。到最后一个结点很容易,所以我们只需一个步长n-1距离即可,那么思路就很明显了,定义一个end指针走n-1步,再定义一个cur指针,从头节点开始跟end指针一块走,当end指针到最后一个结点(非空)时,这时cur与最后一个结点的距离为n-1,此时cur指向的不就是倒数第n个结点吗?

细节分析

 1. 为了删除这个结点,我们还需要保存前一个节点。
 2.当删除倒数第n个结点时,我们只能删除头结点,并将之指向下一个节点。

步骤

1.定义end指针,走n-1步停下来。
2.定义cur指针,跟end再一块走,直到end指向最后一个非空节点。
3.处理最后情况: 如果cur等于头指针,则进行头删操作;如果不等于则,cur前一个节点的下一个节点指向cur的下一个节点。然后释放cur节点。

代码

typedef struct ListNode Node;
struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
    Node* end = head;
    while(--n)
    {
        end = end->next;
    }
    Node* cur = head;
    Node* prev = head;
    while(end->next)
    {
        end = end->next;

        prev = cur;
        cur = cur->next;
    }
    //分析情况,看是否需要修改头结点
    if(cur==head)
    {
        head = cur->next;
    }
    else
    {
        prev->next = cur->next;
    }
    free(cur);
    return head;
}

二.链表中环的入口节点

题目分析

在这里插入图片描述

  • 总结

数据格式——可能是环形链表也可能不是环形链表。
要求——找到入环节点。
返回值——入环节点。

思路分析

这里对下面的图补充一个词——相遇。
在这里插入图片描述

写法①代码

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head,* slow = head;
    while(fast&&fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            struct ListNode* start = fast;
            struct ListNode* begin = head;
            while(start!=begin)
            {
                begin = begin->next;
                start = start->next;
            }
            return start;
        }
    }
    return NULL;   
}

写法②代码:

typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) 
{
    if(head == NULL)
    {
        return NULL;
    }
    Node* fast = head->next;
    Node* slow = head;
    while(fast&&fast->next&&fast!=slow)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    if(fast==NULL||fast->next==NULL)
    {
        return NULL;
    }
    Node* begin = fast->next;
    Node* start = head;
    while(begin!=start)
    {
        begin = begin->next;
        start = start->next;
    }
    return begin;
}

三.两个链表的第一个重合节点

题目分析

在这里插入图片描述

  • 总结

1.数据——所给两个链表,可能不相交,但不存在环。
2.要求——找到第一个相交的结点。
3.返回值——第一个相交的结点。

思路分析

  • 双指针——对齐一块走
    在这里插入图片描述

代码

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    int len_A = 0,len_B = 0;
    struct ListNode* A = headA,*B = headB;
    while(A||B) 
    {
        if(A)
        {
            A = A->next;
            len_A++;
        }
        if(B)
        {
            B = B->next;
            len_B++;
        }
    }
    A = headA;
    B = headB;
    int k =abs(len_B-len_A);
    //长度对齐
    while(k--)
    {
        if(len_A>len_B)
        {
            A= A->next; 
        }
        else
        {
            B = B->next;
        }
    }
    while(A!=B)
    {
        A = A->next;
        B = B->next;
    }
    //如果没有相交节点,则最后结果为空时,相等也会结束循环
    return A;
}

四.反转链表

题目分析

在这里插入图片描述

  • 总结

数据——单链表
要求——反转整个链表
返回——反转后链表的头结点

思路分析

方法一:

法①代码

typedef struct ListNode Node;
struct ListNode* reverseList(struct ListNode* head)
{
    //在原链表的基础上进行操作
    //首先我们知道链表需要反转的条件是有两个或两个以上的结点
    if(head&&head->next)
    {
        //我们需要换方向的话需要三个指针
        //当前的需要改变方向的两个指针
        Node* cur = head->next;
        Node* prev = head;
        Node* next = cur->next;
        while(cur)
        {
            if(prev==head)
            {
                prev->next = NULL;
            }
            cur->next = prev;
            prev = cur;
            cur = next;
            if(next)
            {
                next = next->next;
            }
        }
        return prev;
    }
    else
    {
        //这里只有一个结点直接返回头结点即可
        return head;
    }
}

方法二:
在这里插入图片描述

法②代码

typedef struct ListNode Node;
struct ListNode* reverseList(struct ListNode* head)
{
    Node* cur = head;
    Node* prev = head;
    Node* new_head = NULL;
    while(cur)
    {
        prev = cur;
        cur = cur->next;
        prev->next = new_head;
        new_head =prev;
    }
    return new_head;
}

方法三:
 反转链表问题可以不断拆解为,反转前一个结点和剩余结点,以此类推最终反转整个链表,不过需要注意的是第一次反转的一个空结点和头结点,最后一次反转遇到空节点,直接返回最后一个非空节点即可。

法③代码

typedef struct ListNode Node;
Node* reverse(Node* cur,Node* next,Node* new_head)
{
    if(next&&next->next)
    {
        new_head = reverse(next,next->next,new_head);
    }
    if(next&&next->next==NULL)
    {
        new_head = next;
    }
    if(next)
    {
        next->next = cur;
    }
    return new_head;
}
struct ListNode* reverseList(struct ListNode* head)
{
    return reverse(NULL,head,NULL);
}

五.链表中的两数相加

题目分析

在这里插入图片描述

  • 总结

数据格式——非空链表。
要求——将链表的值进行相加。
返回——一个新链表。

思路分析

在这里插入图片描述

代码

typedef struct ListNode Node;
Node* reverselist(Node* head)
{
    Node* prev = head;
    Node* cur = head;
    Node* new_head = NULL;
    while(cur)
    {
        prev =  cur;
        cur = cur->next;
        prev->next = new_head;
        new_head = prev;
    }
    return new_head;
}

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2)
{
    Node* head1 = reverselist(l1);
    Node* head2 = reverselist(l2);
    Node* sum_head = NULL;
    int carry = 0;
    while(head1||head2)
    {
        int sum = (head1 != NULL ? head1->val : 0)\
         +(head2 != NULL ? head2->val : 0) + carry;
        int cur = sum%10;
        carry = sum/10;
        Node* NewNode = (Node*)malloc(sizeof(Node));
        NewNode->val = cur;
        NewNode->next = sum_head;
        sum_head = NewNode;
        head1 = head1 != NULL ? head1->next : NULL;
        head2 = head2 != NULL ? head2->next : NULL;
    }
    if(carry)
    {
        Node* NewNode = (Node*)malloc(sizeof(Node));
        NewNode->val = carry;
        NewNode->next = sum_head;
        sum_head = NewNode;
    }
    return sum_head;
}

六.重排链表

题目分析

在这里插入图片描述

思路分析

找到中间结点,反转之后的链表,再进行重新进行排序。
在这里插入图片描述

代码

typedef struct ListNode Node;
struct ListNode* reverseList(struct ListNode* head)
{
    Node* cur = head;
    Node* prev = head;
    Node* new_head = NULL;
    while(cur)
    {
        prev = cur;
        cur = cur->next;
        prev->next = new_head;
        new_head =prev;
    }
    return new_head;
}
void reorderList(struct ListNode* head)
{
    //找到中间结点
    Node* fast = head;
    Node* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    //此时slow即为中间结点
    //反转slow后面的链表

    Node* head1 = slow->next;
    slow->next = NULL;
    head1 = reverseList(head1);

    //head1链表插在head的前半个链表中
    Node *prev = head;
    Node* cur = head;
    Node* cur1 = head1;
    Node* prev1 = head1;
    while(cur&&cur1)
    {
        prev = cur;
        cur = cur->next;

        prev1 = cur1;
        cur1 = cur1->next;

        prev->next = prev1;
        prev1->next = cur;
    }
    return head;    
}

七.回文链表

题目分析

在这里插入图片描述

思路分析

找到中间结点,然后让中间结点及其以后的结点进行逆转,再进行判断即可。
在这里插入图片描述

代码

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur = head;
    struct ListNode* prev = head;
    struct ListNode* new_head = NULL;
    while(cur)
    {
        prev = cur;
        cur = cur->next;
        prev->next = new_head;
        new_head = prev;
    } 
    return new_head;
}

bool isPalindrome(struct ListNode* head)
{
    //找到中心结点
    struct ListNode* slow = head,*fast = head,*prev = head;
    while(fast&&fast->next)
    {
        prev = slow;
        fast = fast->next->next;
        slow = slow->next;
    }
    struct ListNode* new_head = slow;
    //进行后半部分链表的反转工作
    new_head = reverseList(new_head);
    struct ListNode* begin = head;
    //判断是否是回文
    while(new_head&&begin)
    {
        if(new_head->val!=begin->val)
        {
            return false;
        }
        new_head = new_head->next;
        begin = begin->next;
    }
    return true;
}

八. 展平多级双向链表

题目分析

在这里插入图片描述

  • 总结

数据——多级双向链表
要求——将此种链表展平
返回——头结点

思路分析

找到就进行展平。
在这里插入图片描述

代码

 Node* flatten(Node* head) 
    {
        Node* head1 = head;
        while(head1)
        {
            Node* child = head1->child;
            Node* child_end = NULL;
            Node* prev = child;
            if(child)
            {
                child_end = child;
                while(child_end)
                {
                    prev = child_end;
                    child_end = child_end->next;
                }
                if(head1->next)
                {
                    head1->next->prev = prev;
                    prev->next = head1->next;

                    child->prev = head1;
                    head1->next = child;
                }
                else
                {
                    child->prev = head1;
                    head1->next = child;
                }
                //展平操作
                head1->child = NULL;
            }
            head1 = head1->next;
        }
        return head;
    }

九.排序的循环链表

题目分析

在这里插入图片描述

思路分析

1.当所给链表为空时,这时我们需要自己把插入结点,构造成环,即自己指向自己,并返回构造的结点。
2.当所给链表不为空时,我们需要找到最大最小结点,并且判断插入结点的值是否大于最大的结点值。
3. 继续处理找到第一个大于等于插入值的结点,同时保存此节点的前一个节点。将插入结点插入在两节点之间。
4. 返回头结点。

代码

typedef struct Node Node;
struct Node* insert(struct Node* head, int insertVal) 
{
    Node* inser = (Node*)malloc(sizeof(Node));
    inser->val = insertVal;
    //处理为空的情况
    if(head == NULL)
    {
        inser->next = inser;
        return inser;
    }
    else
    {
        //找到最大和最小节点
        Node* max = head;
        Node* min = head->next;
        while(min->val >= max->val && min!=head)
        {
            min = min->next;
            max = max->next;
        }
        //处理插入结点大于等于最大节点的情况
        if(inser->val >= max->val)
        {
            inser->next = min;
            max->next = inser;
        }
        else
        {
            //找到大于等于inser的第一个节点
            Node* prev = max;
            Node* cur = min;
            while(inser->val > cur->val)
            {
                prev = cur;
                cur = cur->next;
            }
            prev->next = inser;
            inser->next = cur;
        }
    }
    return head;
}

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

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

相关文章

西南交通大学智能监测 培训课程练习4

2023.056.07和09培训 项目实战 目录 一、infracore(基础核心层) 1.1database 1.2config 1.3util 二、业务领域模块 2.1structure模块 2.1.1domain层 2.1.2application层 2.1.3adapter层 2.2sensor模块 2.2.1domian层 2.2.2application层 2.2.…

一文搞懂什么是Docker

一、什么是Docker 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中,依赖的组件非常多,不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署,环境不一定一致,会遇…

Java ~ Reference ~ Finalizer【总结】

前言 文章 相关系列:《Java ~ Reference【目录】》(持续更新)相关系列:《Java ~ Reference ~ Finalizer【源码】》(学习过程/多有漏误/仅作参考/不再更新)相关系列:《Java ~ Reference ~ Final…

基于Python的接口自动化-Requests模块

目录 引言 一、模块说明 二、Requests模块快速入门 1 发送简单的请求 2 发送带参数的请求 3 定制header头和cookie 4 响应内容 5 发送post请求 6 超时和代理 三、Requests实际应用 引言 在使用Python进行接口自动化测试时,实现接口请求…

2023春期末考试选择题R2-9AVL树插入调整详解

题目: 将 8, 9, 7, 2, 3, 5, 6, 4 顺序插入一棵初始为空的AVL树。下列句子中哪句是错的? A. 4 和 6 是兄弟 B. 5 是 8 的父结点 C. 7 是根结点 D. 3 和 8 是兄弟 解题要点: 需要对AVL树的4种旋转方式熟悉。 AVL旋转过程: 根据…

体验ChatGPT使用

ChatGPT是一种基于GPT(Generative Pre-train Transformer)模型的大型语言模型,由OpenAI公司开发。 交互时,有一定的技巧,可以快速准确的反馈正确答案。 一、开发贪吃蛇游戏 浏览器访问:https://chat.opena…

taro使用小记 —— 持续更新

目录 1、在 taro 中使用 axios2、在 taro 中添加全局组件自动引入和方法自动引入3、在 taro 中使用 pinia 1、在 taro 中使用 axios taro 3.6 版本已经支持了网络请求库。 需安装插件 tarojs/plugin-http 使用和注意事项说明: https://www.npmjs.com/package/taroj…

【笔试强训选择题】Day22.习题(错题)解析

作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言 一、…

mac电脑m1搭建java开发环境参考手册

1 背景介绍 开发人员经常会换电脑,或者换新电脑,意味着重新搭建开发环境,很麻烦。但新电脑到手里面了,不换又不好,此篇专门用来记录mac电脑m1搭建java开发环境的步骤。希望对读者有所帮助,一条龙服务。 后…

初探 transformer

大部分QA的问题都可以使用seq2seq来实现。或者说大多数的NLP问题都可以使用seq2seq模型来解决。 但是呢最好的办法还是对具体的问题作出特定的模型训练。 概述 Transformer就是一种seq2seq模型。 我们先看一下seq2seq这个模型的大体框架(其实就是一个编码器和一个解码器)&a…

OpenGL 光照贴图

1.简介 现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成。想想一辆汽车:它的外壳非常有光泽,车窗会部分反射周围的环境,轮胎不会那么有光泽,所以它没有镜面高光,轮毂非常闪亮。 2.漫反射…

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行触发同步(C#)

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行触发同步(C#) Baumer工业相机Baumer工业相机BGAPISDK和触发同步的技术背景Baumer工业相机使用BGAPISDK进行双相机主从相机触发1.引用合适的类文件2.使用BGAPISDK设置主相机硬件触发从相机…

ReentrantLock 底层原理

目录 一、ReentrantLock入门 二、AQS原理 1、AQS介绍 2、自定义锁 三、ReentrantLock实现原理 1、非公平锁的实现 加锁流程 释放锁流程 2、可重入原理 3、可打断原理 4、公平锁原理 5、条件变量原理 await流程 signal流程 一、ReentrantLock入门 相对于synchron…

对测试外包的一些粗略看法

什么叫外包,外包最直接理解就是让别人做事;外包其中一项目的就是降低企业经营成本。 从外包的含义和目的来看,就是我们帮人做事、听人指挥,当企业经济不好的时候,我们就成为了降低成本的最佳方案。说这些是让大家比较…

高并发编程:线程池

一、概述 线程池首先有几个接口先了解第一个是Executor,第二个是ExecutorService,在后面才是线程池的一个使用ThreadPoolExecutor。 二、Executor Executor看它的名字也能理解,执行者,所以他有一个方法叫执行,那么执…

JVM原理:JVM垃圾回收算法(通俗易懂)

目录 前言正文垃圾标记算法引用类型强引用软引用弱引用虚引用 引用计数法循环引用问题 根可达性分析法虚拟机栈(栈帧的局部变量表)中的引用方法区中类静态属性引用方法区中常量引用本地方法栈(Native方法)引用 垃圾回收算法标记清…

Java语法进阶及常用技术(八)--线程池

初识线程池 什么是“池” ---- 软件中的“池”,可以理解为计划经济。 我们的资源是有限的,比如只有十个线程,我们创造十个线程的线程池,可能我们的任务非常多,如1000个任务,我们就把1000个任务放到我们十个…

shell脚本学习记录(流程控制)

前言: 在shell脚本中,()、{}、[]都是用来表示命令或者变量的范围或者属性。它们的具体区别如下: ():表示命令在子shell中运行。括号中的命令会在一个子shell中运行,并且该子shell拥符有自己的环境变量和文件描述&#…

【youcans动手学模型】DenseNet 模型-CIFAR10图像分类

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【youcans动手学模型】DenseNet 模型-CIFAR10图像分类 1. DenseNet 神经网络模型1.1 模型简介1.2 论文介绍1.3 改进方法与后续工作1.4 分析与讨论 2. 在 PyTorch 中定义 DenseNet 模型类2.1 DenseBlo…