牛客刷题day8(滑动窗口最大值,在转动过的有序数组寻找最大值,验证ip地址,树的直径,在二叉树中找到两个节点最近的公共祖先,二叉树根节点到子节点所有路径和)

牛客刷题day8

1.滑动窗口最大值

题目

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
窗口大于数组长度的时候,返回空

解题思路

方法一:暴力方法
算法步骤如下:
枚举每个窗口的左边界 i
根据窗口的左边界i可以对应计算出右边界j
遍历窗口,计算出最大值
方法二:单调队列
方法一种存在很多大量重复计算,比如说,对于数组,假设我们当前遍历到下标i,对于下标i+1的元素(假设i和i+1都在同一个窗口),如果比arr[i]大,说明了什么?
如果arr[i+1] 已经大于了 arr[i], 那么还要arr[i]有什么用。
如果arr[i+1] < arr[i]呢?显然arr[i]还是需要保留的。
假设这里有那么一个队列可以保留上述操作。
遍历数组的每一个元素,
如果队列为空,则直接将当前元素加入到队列中。
如果队列不为空,则让当前元素和队列的最后一个元素比较,如果大于,则将队列的最后一个元素删除,然后继续讲当前元素和队列的最后一个元素比较,重复上面操作,最后将元素加入队列中
如果当前元素小于队列的最后一个元素,则直接将当前元素加入到容器的末尾
如果队列头部的元素(最大值)已经不属于当前窗口的边界,则应该将头部元素删除
总结一下,首先队列中放的元素应该是单调递减的。然后还有删除队列头部元素和最后一个元素的操作。因此,这样的数据结构就是双端队列。python用列表就可以实现。
如何判断队列中头部的元素是否过期呢?
这里我们可以存数组的下标,根据下标的比较来判断。比如,当前遍历到下标为5的元素,窗口的大小为3, 显然显然下标为2的已经过期了。

核心代码

方法一:

class Solution:
    def maxInWindows(self, num, size):
        if not num or size > len(num) or size==0:
            return []
        maxList = []
        for i in range(len(num) - size + 1):
            maxList.append(max(num[i: i + size]))
        return maxList

方法二

class Solution:
    def maxInWindows(self, num, size):
        if size>len(num) or size==0:return []
        queue = []
        for i in range(0,size):
            while queue and queue[-1]<num[i]:
                queue.pop(-1)
            queue.append(num[i])
        result = []
        result.append(queue[0])
        for i in range(size,len(num)):
            if num[i-size]==queue[0]:
                queue.pop(0)
            while queue and queue[-1]<num[i]:
                queue.pop(-1)
            queue.append(num[i])
            result.append(queue[0])
        return result

2.在转动过的有序数组中寻找最大值

题目

给出一个转动过的有序数组,你事先不知道该数组转动了多少
(例如,0 1 2 4 5 6 7可能变为4 5 6 7 0 1 2).
在数组中搜索给出的目标值,如果能在数组中找到,返回它的索引,否则返回-1。
假设数组中不存在重复项。
输入:[3,2,1],1
输出:2

解题思路

这种题如果不考虑算法用python十分简单
但是如果考虑算法的话,相等于使用二分法的思想进行改编
对旋转数组进行均等划分后,总有一边是有序的,如:
10 11 12 13 14 15 1 2 3
10 11 15 1 2 3 4 5 6 7 8
我们定位到有序的一边后,对比target与有序子数组的左右边界,就可以作出搜索左侧还是右侧的决策。

核心代码

不考虑算法

class Solution:
    def search(self , A , target ):
        # write code here
        if target in A:
            return A.index(target)
        else:
            return -1

考虑算法:

class Solution:
    def search(self , A , target ):
        # write code here
        s = 0
        e = len(A)-1
        while s <= e:
            mid = (s + e)/2
            if A[mid] == target:
                return mid
            if A[mid] >= A[s]:
                if A[mid] > target and A[s] <= target:
                    e = mid-1
                else:
                    s = mid+1
            else:
                if A[mid] < target and A[e] >= target:
                    s = mid+1
                else:
                    e = mid-1
        return -1

3.验证ip地址

题目

编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址

IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。

IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。

然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (:😃 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。

说明: 你可以认为给定的字符串里没有空格或者其他特殊字符
示例1
输入
“172.16.254.1”
返回值
“IPv4”
(说明:这是一个有效的 IPv4 地址, 所以返回 “IPv4”)
示例2
输入:
“2001:0db8:85a3:0:0:8A2E:0370:7334”
返回值:
“IPv6”
(说明:这是一个有效的 IPv6 地址, 所以返回 “IPv6”)
示例3
输入
“256.256.256.256”
返回值
“Neither”
(说明:这个地址既不是 IPv4 也不是 IPv6 地址)
备注:
ip地址的类型,可能为IPv4, IPv6, Neither

解题思路

这个题其实就是考察能是否熟练的操作字符串

核心代码
class Solution:
    def solve(self, IP):
        if IP.find('.') != -1:
            if self.IPV4(IP):
                return 'IPv4'
        else:
            if self.IPV6(IP):
                return 'IPv6'
        return 'Neither'

    def IPV4(self, IP):
        IpList = IP.split('.')
        while IpList:
            ip = IpList.pop(-1)
            if ip.startswith('0'):
                return False
            if int(ip) > 255 or int(ip) < 0:
                return False
        return True

    def IPV6(self, IP):
        if IP.find('::') != -1:
            return False
        IpList = IP.split(':')
        while IpList:
            ip = IpList.pop(-1)
            if len(ip) > 4:
                return False
        return True

4.树的直径

题目

给定一棵树,求出这棵树的直径,即树上最远两点的距离。
包含n个结点,n-1条边的连通图称为树。
示例1的树如下图所示。其中4到5之间的路径最长,是树的直径,距离为5+2+4=11

思路

不要被题目欺骗了,整体其实就是构建一个无向图,遍历所有节点,求出每个节点第一大和第二大路径权重,加起来就是最大权重

核心代码

在这里插入图片描述

from collections import defaultdict
 
class Interval:
    def __init__(self, a=0, b=0):
        self.start = a
        self.end = b

class Solution:
    def __init__(self):
        self.max_distance = float("-inf")
 
    def solve(self, n, tree_edge, edge_weight):
        tree = defaultdict(list)
        for edge, weight in zip(tree_edge, edge_weight):
            tree[edge.start].append((edge.end, weight))
            tree[edge.end].append((edge.start, weight))
        self.dfs(tree, 0, defaultdict(bool))
        return self.max_distance
 
    def dfs(self, tree, position, visit):
        bigest, second = 0, 0
        visit[position] = True
        for child, weight in tree[position]:
            if visit[child]:
                continue
            weight += self.dfs(tree, child, visit)
            if weight > bigest:
                second = bigest
                bigest = weight
            elif weight > second:
                second = weight
            self.max_distance = max(bigest + second, self.max_distance)
        return bigest

5.在二叉树中找到两个节点最近的公共祖先

题目

给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点

解题思路

在这里插入图片描述
在这里插入图片描述

核心代码
class Solution:
    def lowestCommonAncestor(self, root, o1, o2):
        self.o1 = o1
        self.o2 = o2
        # write code here
        return self.dfs(root)

    def dfs(self, root):
        if not root:
            return root
        if root.val == self.o1 or root.val == self.o2:
            return root.val
        left = self.dfs(root.left)
        right = self.dfs(root.right)
        if left and right:
            return root.val
        if left:
            return left
        else:
            return right
            

6.二叉树根节点到子节点所有路径和

题目

给定一个仅包含数字 0−9 的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。
例如根节点到叶子节点的一条路径是1→2→3,那么这条路径就用123 来代替。
找出根节点到叶子节点的所有路径表示的数字之和
在这里插入图片描述

解题思路

这道题要求所有路径的和,那么首先想到使用dfs遍历出所有的从根节点到叶子节点的路径,使用递归的方法,从根节点开始计算,左右子树的值,左子树的值不就是root.val*10+left.val,同理计算右子树的值,需要注意的是子节点就返回该路径的值

核心代码
class Solution:
    def sumNumbers(self, root):
        # write code here
        if not root:
            return 0
        if not root.left and not root.right:
            return root.val
        return self.dfs(root.left, root.val) + self.dfs(root.right, root.val)

    def dfs(self, root, sum):
        if not root:
            return 0
        sum = sum * 10 + root.val
        if not root.left and not root.right:
            return sum
        return self.dfs(root.left, sum) + self.dfs(root.right, sum)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值