《剑指offer》——刷题日记

这篇具有很好参考价值的文章主要介绍了《剑指offer》——刷题日记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本期,给大家带来的是《剑指offer》几道题目的讲解。希望对大家有所帮助!!!

本文目录

(一)JZ36 二叉搜索树与双向链表

1、题意分析

2、思路讲解

3、代码演示

4、最终结果

(二)BM6 判断链表中是否有环

1、题意分析

2、思路讲解

3、代码演示

4、最终结果

(三)JZ23 链表中环的入口结点

1、题意分析

2、思路讲解

3、代码演示

4、最终结果

(四 )HJ43 迷宫问题

1、题意分析

2、思路讲解

3、代码演示

4、最终结果


(一)JZ36 二叉搜索树与双向链表(经典题目)

首先,我们第一题给大家讲述的是关于一道  二叉搜索树与双向链表 的问题。首先,拿到题我们还是先从题干入手,理解好题意才能更好进行解题操作。

题目如下:

《剑指offer》——刷题日记

输入描述:

二叉树的根节点

返回值描述:

双向链表的其中一个头节点。

示例1

输入:{10,6,14,4,8,12,16}

返回值:From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;

说明:输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。     

示例2

输入:{5,4,#,3,#,2,#,1}

返回值:
From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1;
说明:
                    5
                  /
                4
              /
            3
          /
        2
      /
    1
树的形状如上图   

1、题意分析

首先我们第一看到这个题目就会发现,如果我们对其进行中序遍历,则得到的结果为【4,6,8,10,12,14,16】,此时就符合我们题干的要求,再次把它转化为双向链表即可。

注意:

  • 此时很多小伙伴就会想到我们直接对这棵树进行中序遍历,最后尾插即可。其实这样是不行的,因为题目告诉我们只能在原树中进行操作,不能创建新结点、因此这种思想是不行的。

2、思路讲解

首先,我们先将一下二叉搜索数的一些概念帮助大家做题:

它要么是一棵空树,要么就具有下列性质的二叉树: 

  • 1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 
  • 2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 
  • 3. 它的左、右子树也分别为二叉排序树。

思路一:

此时可能会有小伙伴这样去想,那我们可不可以用个容器进行操作呢?

  • step1:首先我们先定义一个 vector 用来存放结点;
  • step2:紧接着进行中序遍历,将结点放进vector 中,最后再改链接关系即可。

这其实是一个非常棒的思路,而且 牛客 应该对空间复杂度也没有进行很大的限制,最多限制其时间复杂度。因此,这种方法理论上应该是可行的(我在这里就不去实现了,大家有兴趣可以进行尝试)

思路二:

step1:首先我们需要确定一点,那就是这道题肯定与中序有关。是通过改变为中序这样的关系来进行实现的;

step2:我们先中序遍历这棵树,并记录一个前驱结点(最开始前驱为空)。那我们如何记录一个前驱结点呢? 

  • 在 cur 往下走之前我先给 前驱结点,此时就变为前驱了,在递归往下走;
  • 此时一个结点的左无论如何都是指向前驱的;

注意:

此时又出现一个问题,我的左即前驱可以解决,但是我的 右 该如何解决呢?

  • 我们不能用  ->next 的方法,因为我们还没遍历过去呢?怎么知道后继在哪呢?我们只知道前驱在哪。

step3:此时我们对于可以这样去考虑:

  • 在当前位置我可以改我的前驱,却改不了我的后继;
  • 但是我可以在下一步在动手去改,即我确定上一步的下一步一定是我;
  • 因此关键在于我们先把前驱弄完了,通过前驱再去修改后继即可
cur->left=prev;
prev->right=cur;

注意:

这个题目的思想其实和二叉树的线索化非常相似。在这里简单提一句:

  • 二叉树的线索化就是把那些空指针利用起来,找它的前驱和后继,这样的话可以做一个迭代遍历的操作。

3、代码演示

class Solution {
public:
    void InorderConvert(TreeNode* cur,TreeNode* &prev)
	{
		if(cur == nullptr)
		return;

		InorderConvert(cur->left,prev);
		cur->left=prev;
		if(prev)
		prev->right=cur;

		prev=cur;
		InorderConvert(cur->right,prev);

	}
    TreeNode* Convert(TreeNode* pRootOfTree) {
       TreeNode* prev=nullptr;

	   InorderConvert(pRootOfTree,prev);
	   TreeNode* head=pRootOfTree;
	   while(head && head->left)
	   {
		head=head->left;
	   }
	   return head;
    }
};

4、最终结果

《剑指offer》——刷题日记

我们提交代码,最终程序执行成功!!! 


(二)BM6 判断链表中是否有环

 题目如下:

《剑指offer》——刷题日记

  • 示例1
输入:{3,2,0,-4},1
返回值:true
说明:
第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1(注:头结点为位置0),即-4->2存在一个链接,组成传入的head为一个带环的链表,返回true    
  •  示例2
输入:{1},-1
返回值:false
说明:
第一部分{1}代表一个链表,-1代表无环,组成传入head为一个无环的单链表,返回false    
  • 示例3
输入:{-1,-7,7,-4,19,6,-9,-5,-2,-5},6
返回值:true

1、题意分析

  • 首先我们简单的分析一下这道题目,其实很简单,对于给出的第一个部分即表示我们要操作的链表,第二部分的数字如果为 -1 则表示此链表没有环。
  • 如果为其他的数字则表示链表末尾到数字对于的下标处的结点之间形成环。

《剑指offer》——刷题日记

2、思路讲解

  • step1:使用快慢指针的思想,即快指针买次走两步,慢指针每次走一步;
  • step2:因为快指针会先进入环,慢指针会后进入环。一旦快指针入了环之后就会一直在环里进行循环,因此如果存在环的话,那么快慢指针经过不断的遍历之后一定会在其中的某一点处相遇。一旦相遇返回 true 即可
  • step3:相反的,如果不存在环,那么快慢指针就一定不会相遇。此时只需返回 FALSE 即可。
  • step4:最后就是对于代码的处理了,具体如下。

3、代码演示

class Solution {
public:
    bool hasCycle(ListNode *head) {
        //首先判断链表为空的情况
        if(head == nullptr || head->next == nullptr)
            return false;
        
        //接下来设置快慢指针
        ListNode* fast = head->next;
        ListNode* slow = head;

        //如果两指针没相遇就继续
        while(slow != fast)
        {
            //无环情况判断
            if(fast==nullptr || fast->next == nullptr)
            return false;

            //快指针移动两步
            fast = fast->next->next;
            //慢指针移动一步
            slow = slow->next;
        }
        return true;
    }
};

4、最终结果

《剑指offer》——刷题日记


(三)JZ23 链表中环的入口结点

题目如下:

《剑指offer》——刷题日记

  •  示例1
输入:{1,2},{3,4,5}
返回值:3
说明:返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3
  •  示例2
输入:{1},{}
返回值:"null"
说明:没有环,返回对应编程语言的空结点,后台程序会打印"null"   
  •  示例3
输入:{},{2}
返回值:2
说明:环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2   

1、题意分析

  • 这道题跟上述的题目有很多类似之处,因此在做这道题之前我先给大家给出了一道判断是否存在环的题目,帮助大家更好的理解这道题。
  • 这道题的整体思路跟上述的其实差不多,上述我们是判断有没有环,这里我们则可以先判断是否有环,紧接着再去看链表的入环结点在哪!

《剑指offer》——刷题日记

2、思路讲解

本题的主要步奏其实就是两步:

  1. 第一步就是判断链表是否有环。
  2. 第二步如果有环,则在有环的链表中找到环的入口返回即可

具体步奏如下:

step1:首先还是使用快慢指针的思想,即快指针买次走两步,慢指针每次走一步,此时会产生两种情况:

  • ①无环:当快指针走向链表末端的时候,此时直接返回 null即可
  • ②有环:当有环时,此时当快指针进入环之后依旧还是在环里一直循环遍历的,此时当 fast==slow 的时候,两指针在这时首次相遇。此时,我们需要把 fast 指针指向头结点,紧接着两指针各自在走一步步的走,当走到两指针再次相遇时 ,此时所指向的结点即为我们的入环结点,返回即可。

我以如下图示给大家分析上述:

《剑指offer》——刷题日记

step2:分析原因

  • ①如上图所示,我们假设环外的长度为m ,此时当slow进入环之后,需要再走 n步指针才能与 fast 指针相遇。因为快指针比慢指针走得快,因此假设两指针相遇之前,fast 指针已经走了 a圈,此时 fast 指针走过的距离有  【m+a(n+k)+n】; slow 指针走过的距离为 【m+n】
  • ②此时,我们可以得出一个公式结论。因为 fast 一次走两步, slow 一次走一步 ,因此我们得出以下公式  m+a(n+k)+n=2*(m+n)
  • ③根据上面的公式,我们可以推倒出 m=(a-1)*(n+k)+k
  • ④上面的式子含义即为从链表头部到入环结点距离 等于 从相遇点到入环结点的距离+(a-1)倍的圈长

3、代码演示

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        //快慢双指针
        ListNode* fast = pHead;
        ListNode* slow = pHead;

        //如果没环快指针会先到链表尾
        while(fast != nullptr && fast->next != nullptr)
        {
            //快指针移动两步
            fast = fast->next->next;
            //慢指针移动一步
            slow = slow->next;

            //相遇则有环
            if(fast == slow)
            {
                //此时从首结点开始向后遍历如果与慢指针相遇则当前结点为入环结点
                while(pHead != slow)
                {
                   slow = slow->next;
                   pHead = pHead->next;
                } 
                return slow;
            }
        }
        return nullptr;
    }
};

4、最终结果

《剑指offer》——刷题日记


(四 )HJ43 迷宫问题

题目如下:

《剑指offer》——刷题日记

  • 示例1
输入:
5 5
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

输出:
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
  • 示例2
输入:
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 1
0 1 1 1 0
0 0 0 0 0

输出:
(0,0)
(1,0)
(2,0)
(3,0)
(4,0)
(4,1)
(4,2)
(4,3)
(4,4)

1、题意分析

首先,开始时我们并不知道该结点可以往哪走,上下左右四个方向我们并不知道哪条是可行的。因此不难看出本题我们需要用到 “回溯法”去进行解决。

其次,通过我们的分析也不难发现。对于路径的选择是不确定的。对于题意是找到最短的路径,因此我们还要对路径进行最优判断。

2、思路讲解

step1:首先我们可以从入口处开始进行递归搜索,每次进入一个点,将其加入临时路径数组中;

step2:紧接着我们判断是否进入到了出口处;

step3:如果没有依次搜索当前位置的上、下、左、右四个方向的点,如果搜索的这个点可以进入则路径进入,如果四个方向都没有可以走的路表示该结点走不通,此时需要恢复原场面,回溯——删去路径最后一个,重置该位为0.

step4:找到横纵坐标都等于矩阵最后一位则表示找到路径,复制现有路径然后返回。

3、代码演示

#include <iostream>
#include<vector>
using namespace std;

int ROW, Cal;
vector<vector<int>>arry;
vector<vector<int>>Path_tmp;
vector<vector<int>>Path_way;


void Mazeway(int i, int j) {
    arry[i][j] = 1;
    Path_tmp.push_back({ i, j });
     
     //判断是否到达出口
    if (i == ROW - 1 && j == Cal - 1) {
        if (Path_way.empty() || Path_way.size() > Path_tmp.size())
            Path_way = Path_tmp;
    }

    //向右走
    if (j + 1 < Cal && arry[i][j + 1] == 0) {
        Mazeway(i, j + 1);
    }

    //向左走
    if (j - 1 >= 0 && arry[i][j - 1] == 0) {
        Mazeway(i, j - 1);
    }
    //向下走
    if (i - 1 >= 0 && arry[i - 1][j] == 0) {
        Mazeway(i - 1, j );
    }
    //向上走
    if (i + 1 < ROW && arry[i + 1][j] == 0) {
        Mazeway(i + 1, j);
    }

    //该结点走不通,此时需要恢复原场面
    arry[i][j] = 0;
    //从路径中删除该节点
    Path_tmp.pop_back();
}

int main() {
    while (cin >> ROW >> Cal) {
        arry = vector<vector<int>>(ROW, vector<int>(Cal, 0));
        Path_tmp.clear();
        Path_way.clear();
        for (int i = 0; i < ROW; ++i) {
            for (int j = 0; j < Cal; ++j) {
                cin >> arry[i][j];
            }
        }

        Mazeway(0, 0);

        for (int i = 0; i < Path_way.size(); ++i) {
            cout << "(" << Path_way[i][0] << "," << Path_way[i][1] << ")" << endl;

        }

    }
    return 0;
}
// 64 位输出请用 printf("%lld")

4、最终结果

《剑指offer》——刷题日记

我们提交代码,最终程序执行成功!!! 


到此,便是今天刷题的所有解答啦!希望对大家有所帮助。非常感谢您的阅读!!!文章来源地址https://www.toymoban.com/news/detail-420524.html

到了这里,关于《剑指offer》——刷题日记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python_ACM模式《剑指offer刷题》链表1

    询问面试官是否可以改变链表结构 1. 翻转链表,再遍历链表打印。 2. 想要实现先遍历后输出,即先进后出,因此可借助栈结构。 3. 可用隐式的栈结构,递归来实现。 1. 2. 3. 采用递归的思想 注意是递归到最后一个元素才开始打印 即要先写递归 后写打印代码

    2024年01月23日
    浏览(52)
  • 【leetcode刷题之路】剑指Offer(4)——分治+排序算法+动态规划

    8 分治算法 8.1 【递归】剑指 Offer 07 - 重建二叉树 https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/   前序遍历是根左右,中序遍历是左根右,这也就意味着前序遍历的第一个节点是整棵树的根节点,顺着这个节点找到它在中序遍历中的位置,即为in_root,那么in_root左边的都在左子

    2024年02月11日
    浏览(59)
  • 【剑指offer刷题记录 java版】数组双指针 之 滑动窗口

    本系列文章记录labuladong的算法小抄中剑指offer题目 题目链接:https://leetcode.cn/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/ 题目链接:https://leetcode.cn/problems/MPnaiL/ 题目链接:https://leetcode.cn/problems/VabMRr/ 题目链接:https://leetcode.cn/problems/wtcaE1/ 题目链接:https://leetcode.cn/pr

    2024年02月09日
    浏览(51)
  • 刷题笔记【5】| 快速刷完67道剑指offer(Java版)

    本文已收录于专栏 🌻 《刷题笔记》 题目来源参考阿秀学长的刷题笔记,小戴只是把 C++的题解改成了 Java版本,并整理了其他思路,便于自己的学习~ 如果解题有更好的方法,本文也会及时进行更新~ 希望对你有帮助~ 一起加油哇~ 牛客原题链接 输入两个递增的链表,单个链表

    2023年04月12日
    浏览(38)
  • 【剑指offer刷题记录 java版】数组双指针 之 其它题目

    本系列文章记录labuladong的算法小抄中剑指offer题目 题目链接:https://leetcode.cn/problems/XltzEq/ 题目链接:https://leetcode.cn/problems/fan-zhuan-dan-ci-shun-xu-lcof/ 题目链接:https://leetcode.cn/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/ 题目链接:https://leetcode.cn/problems/he-wei-sde-

    2024年02月11日
    浏览(48)
  • 第8天-代码随想录刷题训练-字符串● 344.反转字符串 ● 541. 反转字符串II ● 剑指Offer 05.替换空格 ● 151.翻转字符串里的单词 ● 剑指Offer58-II.左旋转字符串

    LeetCode链接 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 swap常见的两种交换形式 常见的值交换 通过位运算 LeetCode链接 给定一个

    2024年02月04日
    浏览(64)
  • 今天给大家带来Python炫酷爱心代码

    前言: 这个是小编之前朋友一直要小编去做的,不过之前技术不够所以一直拖欠今天也完成之前的约定吧! 至于他是谁,我就不多说了直接上代码 如果有需要的话,可以联系小编噢!

    2024年02月05日
    浏览(50)
  • 力扣 [344、541、剑指offer 05.、151、剑指offer58-ll]

    双指针:自己的 双指针,左指针指向开头,右指针指向末尾。 交换两个左右指针。 左右指针向中间移动。 时间复杂度:O(n); 空间复杂度:O(1); 实现代码: 分类讨论:自己的 分类讨论: 如果剩余字符少于k个,则将剩余字符全部反转。 如果剩余字符大于或等于k个,则反

    2024年02月15日
    浏览(38)
  • 剑指offer——第8期

    💟💟前言 🥇作者简介:友友们大家好,我是你们的小王同学😗😗 ​🥈个人主页:小王同学🚗 ​🥉 系列专栏:牛客刷题专栏📖 ​📑 推荐一款非常火的面试、刷题神器👉 牛客网 觉得小王写的不错的话 麻烦动动小手点赞👍 收藏⭐ 评论📄 今天给大家带来的刷题系列是

    2024年02月02日
    浏览(20)
  • 剑指offer:动态规划

    JZ42 连续子数组的最大和(一) 简单 通过率:40.77% 时间限制:1秒 空间限制:64M 知识点动态规划贪心 描述 输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。 数据范围: 1=n=2×105 −100=a[i]=100 要

    2024年02月03日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包