leetcode数组中的问题(三)

 

目录

252. 会议室

253. 会议室 II

1094. 拼车

452. 用最少数量的箭引爆气球

435. 无重叠区间

125. 验证回文串

680. 验证回文字符串 Ⅱ

344. 反转字符串

345. 反转字符串中的元音字母

1119. 删去字符串中的元音


252. 会议室

https://leetcode-cn.com/problems/meeting-rooms/comments/

给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],...] (si < ei),请你判断一个人是否能够参加这里面的全部会议。

示例 1:输入: [[0,30],[5,10],[15,20]],输出: false
示例 2:输入: [[7,10],[2,4]],输出: true

思路

一:参考leetcode数组中的问题(二)的56合并区间,该题还要更简单一点,直接按起点排序,区间是否重叠,可以看前一个区间的终点是否大于该区间的起点。附上leetcode的官方简答的图https://leetcode-cn.com/problems/meeting-rooms/solution/hui-yi-shi-by-leetcode/

class Solution(object):
    def canAttendMeetings(self, intervals):
        """
        :type intervals: List[List[int]]
        :rtype: bool
        """
        if len(intervals) <= 1:
            return True
        # 按起点排序
        sorted_intervals = sorted(intervals, key = lambda a: a[0])
        
        for i in range(1, len(sorted_intervals)):
            if sorted_intervals[i - 1][1] > sorted_intervals[i][0]:
                return False
        return True

253. 会议室 II

https://leetcode-cn.com/problems/meeting-rooms-ii/

给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],...] (si < ei),为避免会议冲突,同时要考虑充分利用会议室资源,请你计算至少需要多少间会议室,才能满足这些会议安排。

示例 1:输入: [[0, 30],[5, 10],[15, 20]],输出: 2
示例 2:输入: [[7,10],[2,4]],输出: 1

思路

一:通常遇到区间相关问题,排序是很必要的,开辟一个数组res,用来记录某一个会议是否要征用新的办公室,征用新的为1,不要征用新的为0,则最后res数组求和就是所求解。如何判断是否征用新的会议室,若已开会议室中最后一个会议已结束,则可以接上。刚开始还本着节约资源的原则,接在最早结束的会议室,以减少会议室控制时间,即题解一。但是后面发现,其实只要会议结束,接上就行,无所谓接在哪个后面,因为所有能接受该会议的会议室,必然能接受他后面的所有会议(后面的开始的更晚)。

class Solution(object):
    def minMeetingRooms(self, intervals):
        """
        :type intervals: List[List[int]]
        :rtype: int
        """
        if not intervals:
            return 0
        if len(intervals) == 1:
            return 1

        intervals_sort = sorted(intervals, key=lambda a: a[0])
        # res中0-表示该会议不用征用一个新的会议室,1-表示要征用新的会议室。
        res= [0] * len(intervals_sort)
        # 初始化,第一场会议必须征用一个新的会议室
        res[0] = 1
        # 记录最后一场会议的结束时间,方便后续会议的安排
        rec_high = [item[1] for item in intervals_sort]

        for i in range(1, len(intervals_sort)):
            min_high = intervals_sort[i][1]
            idx = -1
            for j in range(0, i):
                # 若有一个会议开辟了新的会议室,且i号会议在该会议是最后一场结束后才开始,安插在其后就好。
                if res[j] == 1 and intervals_sort[i][0] >= rec_high[j]:
                    if min_high > rec_high[j]:
                        min_high = rec_high[j]
                        idx = j
            if idx == -1:
                res[i] = 1
            else:
                rec_high[idx] = intervals_sort[i][1]

        return sum(res)

 二:只要能接上的会议室随意找到一个就行,不拘哪个,找不到则该会议要征用一个新的会议室。让我们换个角度,从一组想要开会但还没有分配会议室的人员的角度来看这个问题。他们会怎么做?这组人会依次查看会议室,检查是否有任何会议室是空闲的。如果他们找到一间空闲的会议室,就会在那个房间里开始开会。否则,他们会等待一个会议室空出来。一旦会议室空出来,他们就会占用它。这就是我们将遵循的基本方法。可以说(但不确切),这是一个模拟的过程。在最坏的情况下,我们可以为每个会议分配一个新房间,但这显然不是最优的,除非它们相互冲突。我们需要高效地判断当前会议是否有房间可用,并只在分配的房间目前没有空闲时才分配新房间。一个朴素的方法是,每当有新会议时,就遍历所有房间,查看是否有空闲房间。O(n^2)的时间复杂度。

class Solution(object):
    def minMeetingRooms(self, intervals):
        if not intervals:
            return 0
        if len(intervals) == 1:
            return 1

        intervals_sort = sorted(intervals, key=lambda a: a[0])
        # res中0-表示该会议不用征用一个新的会议室,1-表示要征用新的会议室。
        res= [0] * len(intervals_sort)
        # 初始化,第一场会议必须征用一个新的会议室
        res[0] = 1

        for i in range(1, len(intervals_sort)):
            idx = -1
            for j in range(0, i):
                # 若有一个会议开辟了新的会议室,且i号会议在该会议是最后一场结束后才开始,安插在其后就好。
                if res[j] == 1 and intervals_sort[i][0] >= intervals_sort[j][1]:
                    intervals_sort[j][1] = intervals_sort[i][1]
                    idx = j
                    break
            if idx == -1:
                res[i] = 1
        return sum(res)

三:我们可以将所有房间保存在最小堆中,堆中的键值是会议的结束时间,而不用手动迭代已分配的每个房间并检查房间是否可用。这样,每当我们想要检查有没有 任何 房间是空的,只需要检查最小堆堆顶的元素,它是最先开完会腾出房间的。如果堆顶的元素的房间并不空闲,then 其他所有房间都不空闲。这样,我们就可以直接开一个新房间。最终最小堆的元素的长度即是解。我们可以理解为最小堆中的放的是占用的会议室,若有空闲,将该会议室释放,否则不做操作,但是一个会议必须开辟会议室,只不过有释放的逻辑上理解为用之前的会议室,没有释放的理解为开新会议室。

class Solution(object):
    def minMeetingRooms(self, intervals):
        if not intervals:
            return 0
        if len(intervals) == 1:
            return 1

        free_rooms = []
        intervals.sort(key=lambda item: item[0])
        # 借助堆,给第一个会议分配会议室
        heapq.heappush(free_rooms, intervals[0][1])

        for i in range(1, len(intervals)):
            # 查看最早结束的会议是否已经结束,若结束则将会议室释放出来,可以用来给该次会议
            if free_rooms[0] <= intervals[i][0]:
                heapq.heappop(free_rooms)
            # 无论是否有空房间,我们都必须给这次会议分配会议室
            heapq.heappush(free_rooms, intervals[i][1])
        return len(free_rooms)

1094. 拼车

https://leetcode-cn.com/problems/car-pooling/submissions/

假设你是一位顺风车司机,车上最初有 capacity 个空座位可以用来载客。由于道路的限制,车 只能 向一个方向行驶(也就是说,不允许掉头或改变方向,你可以将其想象为一个向量)。

这儿有一份行程计划表 trips[][],其中 trips[i] = [num_passengers, start_location, end_location] 包含了你的第 i 次行程信息必须接送的乘客数量;乘客的上车地点;以及乘客的下车地点。这些给出的地点位置是从你的 初始 出发位置向前行驶到这些地点所需的距离(它们一定在你的行驶方向上)。请你根据给出的行程计划表和车子的座位数,来判断你的车是否可以顺利完成接送所用乘客的任务(当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false)。

示例 1:输入:trips = [[2,1,5],[3,3,7]], capacity = 4,输出:false
示例 2:输入:trips = [[2,1,5],[3,3,7]], capacity = 5,输出:true
示例 3:输入:trips = [[2,1,5],[3,5,7]], capacity = 3,输出:true
示例 4:输入:trips = [[3,2,7],[3,7,9],[8,3,9]], capacity = 11,输出:true
提示:你可以假设乘客会自觉遵守 “先下后上” 的良好素质
trips.length <= 1000
trips[i].length == 3
1 <= trips[i][0] <= 100
0 <= trips[i][1] < trips[i][2] <= 1000
1 <= capacity <= 100000

思路

一:用上题的方法一,也可以做,res[i]为True表示i行程的乘客还在车上,False表示已经下车,乘客上车要检查空座c是否能满足此行程的乘客数,要遍历上一个行程乘客上车后还在车上的所有乘客,看看是否这一段路途中有乘客下车释放空座位。

class Solution(object):
    def carPooling(self, trips, capacity):
        """
        :type trips: List[List[int]]
        :type capacity: int
        :rtype: bool
        """
        if not trips:
            return True

        trips.sort(key=lambda item: item[1])

        res = [False] * len(trips)
        res[0], c = True, capacity - trips[0][0]
        if c < 0:
            return False
        for i in range(1, len(trips)):
            for j in range(0, i):
                if res[j] and trips[i][1] >= trips[j][2]:
                    c += trips[j][0]
                    res[j] = False
            if c - trips[i][0] < 0:
                return False
            c -= trips[i][0] 
            res[i] = True

        return True

二:借用上题思路,和上题不同的是,在该站点前所有下车的乘客都会释放空位,故所以用循环来找出所有的空位量,而上题只要有一个空会议室就可以了,顾用if判断。

class Solution(object):
    def carPooling(self, trips, capacity):
        """
        :type trips: List[List[int]]
        :type capacity: int
        :rtype: bool
        """
        if not trips:
            return True

        trips.sort(key=lambda item: item[1])

        free_seats = []
        heapq.heappush(free_seats, [trips[0][2], trips[0][0]])
        c = capacity - trips[0][0]
        if c < 0:
            return False

        for i in range(1, len(trips)):
            while free_seats and trips[i][1] >= free_seats[0][0]:
                c += heapq.heappop(free_seats)[1]
            if c - trips[i][0] < 0:
                return False
            heapq.heappush(free_seats, [trips[i][2], trips[i][0]])
            c -= trips[i][0]
        return True

三:转leetcode上一位大佬的题解https://leetcode-cn.com/problems/car-pooling/solution/tong-ji-by-jerring/

本题直接解答比较复杂,可以采用统计学的方法进行加加减减操作,可以很容易解答本题。
因为 0 <= trips[i][1] < trips[i][2] <= 1000,可以开一个大小为 1001 的数组 cnt 来代表每个地点的人数。
遍历 trips,在上车点加上对应人数,在下车点减去对应人数。
最终数组 cnt 每个位置的前缀和就是对应地点上的乘客数量,判断是否满足条件就比较简单了。

配上这位大神的就更好理解了,https://leetcode-cn.com/problems/car-pooling/solution/chao-jian-dan-si-lu-by-chen-wen-jie/,这道题关键是只要人数不超出座位数就行,然后我们就用一个数组来存储每个点的人数就是了,我真是个小机灵鬼^_^。
这道题有限制终点不会大于1000,我们就搞一个数组a[1001],用来存储每一个点的人数就行了。我举个栗子:(2,3,6)由于终点都是先下后上的所以需要终点人就下车,起点和终点前一个点都在车上,在a[3]到a[6-1]都需要+2,全部行程加完后,然后判断数组有没有出现超过座位的数字就行,时间复杂度O(n),是不是简单到爆又容易理解!!!

这一段的解释,因为只要人数不超出座位数就行,人数:我们数该路段上的车上的人数就行,因为前面在上车点加上对应人数,在下车点减去对应人数,故累加就是每个点上的人的数量。

for i in range(1, 1002):
    cnt[i] += cnt[i-1]
    if cnt[i] > capacity:
        return False
class Solution(object):
    def carPooling(self, trips, capacity):
        if not trips:
            return True

        cnt = [0] * 10001

        for trip in trips:
            cnt[trip[1]] += trip[0]
            cnt[trip[2]] -= trip[0]
        if cnt[0] > capacity:
            return False
        for i in range(1, 1002):
            cnt[i] += cnt[i-1]
            if cnt[i] > capacity:
                return False
        # for trip in trips:
        #     if cnt[trip[1]] > capacity:
        #         return False
        return True

452. 用最少数量的箭引爆气球

https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/submissions/

在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足  xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

Example:输入:[[10,16], [2,8], [1,6], [7,12]],输出:2,解释:对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

思路

一:该题可以理解为,箭只能穿过终点,不能穿过起点,在有重叠的区域(交集)放箭,可以穿越所有重叠的气球的终点。其实与合并区间(并集)几乎完全一样,只不过合并区间,有重叠的区间,随着合并越来越大,此处,有重叠的区间(交集)随着遍历越来越小。

class Solution(object):
    def findMinArrowShots(self, points):
        """
        :type points: List[List[int]]
        :rtype: int
        """
        if not points:
            return 0

        points.sort(key=lambda item: item[0])
        print(points)

        cnt, start, end = 0, points[0][0], points[0][1]
        for i in range(1, len(points)):
            if points[i][0] <= end:
                start = points[i][0]
                end = min(end, points[i][1])
            else:
                cnt += 1
                start, end = points[i]
        return cnt + 1

 二:参照合并区间的方法二

class Solution(object):
    def findMinArrowShots(self, points):
        if not points:
            return 0

        points.sort(key=lambda item: item[0])

        cnt, start, end = 0, points[0][0], points[0][1]
        for i in range(1, len(points)):
            start = max(start, points[i][0])
            end = min(end, points[i][1])
            if start > end:
                cnt += 1
                start, end = points[i]
        return cnt + 1

435. 无重叠区间

https://leetcode-cn.com/problems/non-overlapping-intervals/submissions/

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:可以认为区间的终点总是大于它的起点。区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:输入: [ [1,2], [2,3], [3,4], [1,3] ],输出: 1,解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:输入: [ [1,2], [1,2], [1,2] ],输出: 2,解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:输入: [ [1,2], [2,3] ],输出: 0

思路

一:变换一下思路,把最少删除变为最多保留多少个区间。采用动态规划,retain[i]表示以i区间结尾最多保留多少个区间,则需要再遍历[0,i)中与i不重合的区间,即i可以添加在这些区间之后,这些区间中取最大,再加1即可,最多保留区间个数是retain中的最大值。时间复杂度O(n^2)。

class Solution(object):
    def eraseOverlapIntervals(self, intervals):
        """
        :type intervals: List[List[int]]
        :rtype: int
        """
        if len(intervals) <= 1:
            return 0
        intervals.sort(key=lambda item: item[1])
        
        retain = [1] * len(intervals)

        for i in range(1, len(intervals)):
            for j in range(0, i):
                if intervals[i][0] >= intervals[j][1]:
                    retain[i] = max(retain[i], retain[j] + 1)
        res = len(intervals) - max(retain)
        return res

二:贪心算法,时间复杂度O(nlgn),主要是排序的时间复杂度。思路,按结束时间排序,结束时间早的在不与前面的冲突的情况下保留,若冲突不保留。合理性,反证法,若贪心法寻找的不是最终解,则必定存在一个最终解,且该最终解必定在贪心法所求解后结束,则能接上该解的必能接上贪心的解,故不影响后面,矛盾,前面的话,都是目前的可行解,也不影响,矛盾,故合理。

class Solution(object):
    def eraseOverlapIntervals(self, intervals):
        if len(intervals) <= 1:
            return 0
        intervals.sort(key=lambda item: item[1])

        retain, pre = 1, 0
        for i in range(1, len(intervals)):
            if intervals[i][0] >= intervals[pre][1]:
                retain += 1
                pre = i
        res = len(intervals) - retain
        return res

125. 验证回文串

https://leetcode-cn.com/problems/valid-palindrome/

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:输入: "A man, a plan, a canal: Panama",输出: true
示例 2:输入: "race a car",输出: false

思路

一:没有预先处理字符串,利用对撞指针,每次从左往右l,从右往左r,寻找第一个字母或数字字符,看他们是否相等。

class Solution(object):
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if len(s) <= 1:
            return True

        l, r = 0, len(s) - 1
        while l < r:
            while l < r and not s[l].isalnum():
                l += 1
            while l < r and not s[r].isalnum():
                r -= 1
            if s[l].lower() != s[r].lower():
                return False
            l += 1
            r -= 1
        return True

二:先对字符串做了预处理,leetcode上提交貌似快了点。

import re
class Solution(object):
    def isPalindrome(self, s):
        if len(s) <= 1:
            return True

        s = re.sub("[^a-zA-Z0-9]", "", s)

        l, r = 0, len(s) - 1
        while l < r:
            if s[l].lower() != s[r].lower():
                return False
            l += 1
            r -= 1
        return True

680. 验证回文字符串 Ⅱ

https://leetcode-cn.com/problems/valid-palindrome-ii/submissions/

给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

示例 1:输入: "aba",输出: True
示例 2:输入: "abca",输出: True,解释: 你可以删除c字符。
注意:字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。

思路

一:暴力解法,对于给定字符串中的每个索 引i,我们删除该字符,然后检查结果字符串是否是回文。若是(或原始字符串是回文),那么我们将返回 true。时间复杂度O(n^2)。

二:转自https://leetcode-cn.com/problems/valid-palindrome-ii/solution/yan-zheng-hui-wen-zi-fu-chuan-ii-by-leetcode/,shileetcode的官方题解,如果字符串的起始字符和结束字符相同(即 s[0]==s[s.length-1]),则内部字符是否为回文(s[1], s[2], ..., s[s.length - 2])将唯一地确定整个字符串是否为回文。

算法:
假设我们想知道 s[l],s[l+1],...,s[r] 是否形成回文。如果 l>= r,就结束判断。如果 s[l]=s[r],那么我们可以取 l++;r--。否则,回文必须是 s[l+1], s[l+2], ..., s[r] 或 s[l], s[l+1], ..., s[r-1] 这两种情况。

class Solution(object):
    def validPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if len(s) <= 2:
            return True
        
        l, r = 0, len(s) - 1
        while l < r:
            if s[l] == s[r]:
                l += 1
                r -= 1
            else:
                return self.isPalindrome(s, l + 1, r) or self.isPalindrome(s, l, r - 1)
        return True

    def isPalindrome(self, s, l, r):
        if len(s) <= 1:
            return True

        while l < r:
            if s[l].lower() != s[r].lower():
                return False
            l += 1
            r -= 1
        return True

344. 反转字符串

https://leetcode-cn.com/problems/reverse-string/

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:输入:["h","e","l","l","o"],输出:["o","l","l","e","h"]
示例 2:输入:["H","a","n","n","a","h"],输出:["h","a","n","n","a","H"]

思路

一:双指针法,使用两个指针,一个左指针 l,右指针 r,开始工作时 l指向首元素,r 指向尾元素。交换两个指针指向的元素,并向中间移动,直到两个指针相遇。

class Solution(object):
    def reverseString(self, s):
        """
        :type s: List[str]
        :rtype: None Do not return anything, modify s in-place instead.
        """
        if len(s) <= 1:
            return s
        
        i, j = 0, len(s) - 1

        while i < j:
            s[i], s[j] = s[j], s[i]
            i += 1
            j -= 1
        return s

345. 反转字符串中的元音字母

https://leetcode-cn.com/problems/reverse-vowels-of-a-string/

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:输入: "hello",输出: "holle"
示例 2:输入: "leetcode",输出: "leotcede"
说明:元音字母不包含字母"y"。

思路

一:依旧是双指针法,遇到元音字母就交换

class Solution(object):
    def reverseVowels(self, s):
        """
        :type s: str
        :rtype: str
        """
        vowel = "aeiouAEIOU"
        s_l = list(s)
        l, r =0, len(s_l) - 1

        while l < r:
            while l < r and s_l[l] not in vowel:
                l += 1
            while l < r and s_l[r] not in vowel:
                r -= 1
            s_l[l], s_l[r] = s_l[r], s_l[l]
            l += 1
            r -= 1
        return "".join(s_l)

1119. 删去字符串中的元音

https://leetcode-cn.com/problems/remove-vowels-from-a-string/submissions/

给你一个字符串 S,请你删去其中的所有元音字母( 'a','e','i','o','u'),并返回这个新字符串。

示例 1:输入:"leetcodeisacommunityforcoders",输出:"ltcdscmmntyfrcdrs"
示例 2:输入:"aeiou",输出:""

思路

一:一次遍历,遇到元音跳过

class Solution(object):
    def removeVowels(self, S):
        """
        :type S: str
        :rtype: str
        """
        vowel ="aeiou"

        s = ""
        for letter in S:
            if letter not in vowel:
                s += letter
        return s

二:正则

class Solution(object):
    def removeVowels(self, S):
        s = re.sub("[aeiou]", "", S)
        return s
发布了46 篇原创文章 · 获赞 1 · 访问量 5043

猜你喜欢

转载自blog.csdn.net/qq_xuanshuang/article/details/104236324