冲刺春招-精选笔面试 66 题大通关 day17

day17题目:141. 环形链表236. 二叉树的最近公共祖先92. 反转链表 II

学习计划链接:冲刺春招-精选笔面试 66 题大通关

今日知识点:快慢指针、dfs、链表,难度为简单、中等、中等

昨日题目链接:冲刺春招-精选笔面试 66 题大通关 day16

141. 环形链表

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

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

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 链表中节点的数目范围是 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos-1 或者链表中的一个 有效索引

进阶: 你能用 O(1)(即,常量)内存解决此问题吗?

思路

快慢指针,快指针一次走两步,慢指针一次走一步,若有环则一定会遇上的,否则没有环

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    if(!head) return false
    let [f, s] = [head, head]
    while(s.next && f.next && f.next.next) {
        f = f.next.next;
        s = s.next
        if(f === s) return true; 
    }
    return false;
};

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入: root = [1,2], p = 1, q = 2
输出: 1

提示:

  • 树中节点数目在范围 [2, 10^5] 内。
  • -10^9 <= Node.val <= 10^9
  • 所有 Node.val 互不相同
  • p != q
  • pq 均存在于给定的二叉树中。

思路

先从一个节点往上走将沿途的vis置为true,再从另一个结点往上走,遇到vis为true的就是最近共同祖先,返回。

代码

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    let fa = new Map()
    let vis = new Map()
    let init = function(rt) {
        vis.set(rt.val, false);
        if(rt.left) {
            fa.set(rt.left.val, rt)
            init(rt.left)
        }
        if(rt.right) {
            fa.set(rt.right.val, rt)
            init(rt.right)
        }
    }
    fa.set(root.val, null)
    init(root)
    let ptr = p
    while(ptr && fa.has(ptr.val)) {
        vis.set(ptr.val, true)
        ptr = fa.get(ptr.val)
    }
    ptr = q
    while(ptr && fa.has(ptr.val)) {
        if(vis.get(ptr.val)) return ptr
        ptr = fa.get(ptr.val)
    }
}

92. 反转链表 II

给你单链表的头指针 head 和两个整数 leftright ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

示例 1:

输入: head = [1,2,3,4,5], left = 2, right = 4
输出: [1,4,3,2,5]

示例 2:

输入: head = [5], left = 1, right = 1
输出: [5]

提示:

  • 链表中节点数目为 n
  • 1 <= n <= 500
  • -500 <= Node.val <= 500
  • 1 <= left <= right <= n

进阶: 你可以使用一趟扫描完成反转吗?

思路

哎,先来一个反转链表的函数,day1的K组一个反转链表里边我们就写过:冲刺春招-精选笔面试 66 题大通关 day1

var reverseList = function(s, e) {
    let prev = e.next
    let nowv = s
    while(prev != e) {
        let temp = nowv.next
        nowv.next = prev
        prev = nowv
        nowv = temp
    }
    return [e, s]
}

然后,当务之急是找到 leftright 对应的结点指针 lr,以及 l 前面的 prev

  • 翻转 let [s, e] = reverseList(l, r), 记得在之前先把 r.next 存到 temp
  • 为了让反转后子链表能够顺利的拼回去原链表,需将反转后的 pre.next 指向se.next 指向 temp

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
 var reverseList = function(s, e) {
    let prev = e.next
    let nowv = s
    while(prev != e) {
        let temp = nowv.next
        nowv.next = prev
        prev = nowv
        nowv = temp
    }
    return [e, s]
}
/**
 * @param {ListNode} head
 * @param {number} left
 * @param {number} right
 * @return {ListNode}
 */
var reverseBetween = function(head, left, right) {
    let ehead = new ListNode(0, head)
    let [prev, nowv, prel, l, r] = [ehead, head, ehead, head, head]
    for(let cnt = 1; nowv; ++cnt, nowv = nowv.next) {
        if(cnt === left) {
            prel = prev
            l = nowv
        }
        if(cnt === right) r = nowv
        prev = nowv
    }
    let temp = r.next
    let [s, e] = reverseList(l, r)
    prel.next = s
    e.next = temp
    return ehead.next
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
2023年3月11日,美团聘笔试中共包含五道编程目。以下是对每道目的简要说明: 1. 目一:这道目要求解决一个数字统计的问。可能涉及到的知识点包括数据结构、循环和条件判断等。解决问的思路可能是使用字典等数据结构来保存统计结果,并使用循环逐个读取输入数据并进行统计。 2. 目二:这道目可能是一个字符串处理的问。需要使用字符串的方法进行操作,如提取、拼接、查找和替换等。可能的解决思路包括使用正则表达式、切片和遍历等。 3. 目三:这道目可能涉及到算法和数据结构的知识。可能是一道涉及到数组、链表、树等数据结构的问。解决思路可能包括遍历、递归、搜索和排序等。 4. 目四:这道目可能是一个动态规划的问。需要根据给定的条件和规则,通过动态规划的方式求解问。解决思路包括定义状态和转移方程,使用递推或记忆化搜索进行求解。 5. 目五:这道目可能是一个图论或网络问。需要根据给定的图或网络结构,解决一个相关的问。可能涉及到广度优先搜索、深度优先搜索、最短路径等知识。解决思路可能包括使用图或网络的相关算法进行求解。 以上只是对这五道编程目的一些可能情况进行的简要描述,具体的目内容可能会有所不同。希望这些信息能对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余cos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值