leetcode热题系列19

68. 文本左右对齐

给定一个单词数组和一个最大宽度 maxWidth,格式化文本使其每行恰好有 maxWidth 个字符并完全(左右两端)对齐。

你应该使用贪心算法来放置尽可能多的单词在每一行。行中的单词之间用空格隔开,增加的空格应尽量均匀分配在单词之间。若行中只有一个单词,或者是最后一行,则左对齐,并在行的末尾填充空格。

每个单词只能出现在新的一行中,单词不能分解。

输入格式
words:一个字符串列表,每个字符串表示一个单词。
maxWidth:一个整数,表示每行的最大宽度。
输出格式
返回格式化后的字符串列表。
示例

示例 1
输入: 
words = ["This", "is", "an", "example", "of", "text", "justification."]
maxWidth = 16
输出:
[
   "This    is    an",
   "example  of text",
   "justification.  "
]

方法一:模拟行填充
解题步骤
初始化变量:设置当前行的单词列表和当前行的字符长度。
填充行:遍历所有单词,将单词添加到当前行直到超出最大宽度。
处理空格:对于非最后一行,均匀分配空格。如果只有一个单词或是最后一行,则左对齐。
生成最终文本:将处理后的行添加到结果列表中。

def fullJustify(words, maxWidth):
    """
    文本左右对齐
    :param words: List[str], 输入的单词列表
    :param maxWidth: int, 最大行宽
    :return: List[str], 对齐后的文本行列表
    """
    res, cur, num_of_letters = [], [], 0
    for w in words:
        if num_of_letters + len(w) + len(cur) > maxWidth:
            for i in range(maxWidth - num_of_letters):
                cur[i % (len(cur) - 1 or 1)] += ' '
            res.append(''.join(cur))
            cur, num_of_letters = [], 0
        cur += [w]
        num_of_letters += len(w)

    return res + [' '.join(cur).ljust(maxWidth)]

# 示例调用
words = ["This", "is", "an", "example", "of", "text", "justification."]
maxWidth = 16
print(fullJustify(words, maxWidth))  # 输出见上例

方法二:贪心算法
解题步骤
遍历单词:使用贪心算法尽量多地填充每一行。
调整空格:确保空格分布均匀或符合题目要求。
构造结果:根据每行的单词和空格构造最终的字符串。

def fullJustify(words, maxWidth):
    """
    使用贪心算法进行文本左右对齐
    :param words: List[str], 输入的单词列表
    :param maxWidth: int, 最大行宽
    :return: List[str], 对齐后的文本行列表
    """
    cur, num_of_letters = [], 0
    res = []
    for word in words:
        if num_of_letters + len(word) + len(cur) > maxWidth:
            if len(cur) == 1:
                res.append(cur[0].ljust(maxWidth))
            else:
                num_spaces = maxWidth - num_of_letters
                space_between_words, extra = divmod(num_spaces, len(cur) - 1)
                for i in range(extra):
                    cur[i] += ' '
                res.append((' ' * space_between_words).join(cur))
            cur, num_of_letters = [], 0
        cur.append(word)
        num_of_letters += len(word)

    # Handle the last line
    res.append(' '.join(cur).ljust(maxWidth))
    return res

# 示例调用
words = ["This", "is", "an", "example", "of", "text", "justification."]
maxWidth = 16
print(fullJustify(words, maxWidth))  # 输出见上例

119. 杨辉三角 II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

在这里插入图片描述

class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        res=[]
        for i in range(0,rowIndex+1):
            t=[1]*(i+1)#将每一行的元素初始化为1
            if i>1:#从第三行开始
                for j in range(1,i):#除了第一个元素和最后一个元素
                    t[j]=res[i-1][j]+res[i-1][j-1] #每个元素是它左上方和右上方的数的和
            res.append(t)
        return res[-1]

补充题3. 求区间最小数乘区间和的最大值

1293. 网格中的最短路径

57. 插入区间

给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:
输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]

示例 2:
输入: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval =[4,8]
输出: [[1,2],[3,10],[12,16]]
解释: 这是因为新的区间[4,8]与[3,5],[6,7],[8,10]重叠。

解题思路
感觉此题和56题合并区间基本一样,就多了一个插入区间。把插入的区间添加进原来的区间集合,然后就是合并区间的问题,就和56题一样了,合并区间的解题思路见56题解(56.合并区间)。

# Definition for an interval.
# class Interval:
#     def __init__(self, s=0, e=0):
#         self.start = s
#         self.end = e
 
class Solution:
    def insert(self, intervals, newInterval):
        """
        :type intervals: List[Interval]
        :type newInterval: Interval
        :rtype: List[Interval]
        """
        intervals.append(newInterval)#比56题多的一行代码
        ans=[]
        intervals = sorted(intervals,key = lambda start: start.start)
        ans.append(intervals[0])
        for i in range(1,len(intervals)):
            if ans[-1].end<intervals[i].start:
                ans.append(intervals[i])
            elif intervals[i].end>ans[-1].end:
                ans[-1].end=intervals[i].end                             
        return ans

29. 两数相除

722. 删除注释

30. 串联所有单词的子串

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。注意子串要包含所有的单词,且不能有重复和遗漏。

示例 1:

输入:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar”。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
输出:[]
1.滑动窗口+哈希表
这是一个查找子串的问题,可以通过滑动窗口配合哈希表来解决。主要思路是:

建立词频表:首先统计 words 数组中各单词的频率。
滑动窗口搜索:由于单词长度固定,我们可以每次移动一个单词的长度,而不是逐个字符移动。对于每个可能的单词起始位置(0 到单词长度),使用滑动窗口检查是否可以匹配 words 中的所有单词。
匹配验证:使用另一个哈希表记录窗口中单词的出现次数,如果与 words 的词频表相匹配,则记录当前开始位置。

def findSubstring(s: str, words: list) -> list:
    from collections import Counter
 
    if not s or not words:
        return []
    
    word_len = len(words[0])
    word_count = len(words)
    total_len = word_len * word_count
    words_count = Counter(words)
    
    def check(start):
        seen = Counter()
        # 检查每个窗口内的单词频率
        for i in range(start, start + total_len, word_len):
            word = s[i:i + word_len]
            if word in words_count:
                seen[word] += 1
                if seen[word] > words_count[word]:
                    return False
            else:
                return False
        return True
    
    results = []
    # 只需要检查 word_len 种情况
    for i in range(word_len):
        for j in range(i, len(s) - total_len + 1, word_len):
            if check(j):
                results.append(j)
                
    return results
 
# 测试代码
s1 = "barfoothefoobarman"
words1 = ["foo","bar"]
print(findSubstring(s1, words1))  # Output: [0, 9]
 
s2 = "wordgoodgoodgoodbestword"
words2 = ["word","good","best","word"]
print(findSubstring(s2, words2))  # Output: []



# encoding = utf-8
# 开发者:xxx
# 开发时间: 15:01 
# "Stay hungry,stay foolish."
 
 
class Solution(object):
    def findSubstring(self, s, words):
        import itertools
        res = []
        len(words)
        permutations_lst = list(itertools.permutations(words, len(words)))
        for i in permutations_lst:
            str1 = ''.join(i)
            s1 = s
            for _ in range(len(s)+1):
                index = s1.find(str1)
                if index != -1:
                    if index not in res:
                        res.append(index)
                        s1 = s1[:index]+"_"+s1[index+1:]
        res.sort()
        return res
 
 
 
if __name__ == '__main__':
    sol = Solution()
    print(sol.findSubstring("aaaaaaaaaaaaaaaaaaaaa", ["aa","aa"]))

剑指 Offer 25. 合并两个排序的链表

在这里插入图片描述

方法一:迭代
注意到题目中要求的空间复杂度为O(1),因此只能使用迭代的方法来实现,因为迭代不使用额外的空间,只利用几个额外的指针来完成链表节点的重新连接。

思路:
1.使用ListNode(0)构造一个虚拟链表,其头节点值pHead为0,后面节点为空;

2.使用cur指针构建新链表,cur指向pHead,后续其他排完序的节点都会接在cur之后;

3.迭代条件:pHead1和pHead2都不为空。比较pHead1和pHead2的大小,cur指针指向较小者,同时头节点的值较小的链表,其头节点需向后移一个节点(因为原先的头节点已经接在cur之后)。由于现在cur指向虚拟节点pHead的位置,因此迭代结束后cur指向下一个节点,即新链表的首元节点。

4.当最初的两个链表有一个排列完后,迭代结束,如果某个链表还有剩余,则直接将其接到新链表的末尾,最后返回虚拟头结点的下一个节点,即合并后的链表头。

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pHead1 ListNode类 
# @param pHead2 ListNode类 
# @return ListNode类
#
class Solution:
    def Merge(self, pHead1: ListNode, pHead2: ListNode) -> ListNode:
        pHead = ListNode(0)  # 创建一个虚拟头结点
        cur = pHead  # 使用 cur 指针来构建新链表
        while pHead1 and pHead2:
            if pHead1.val < pHead2.val:
                cur.next = pHead1
                pHead1 = pHead1.next
            else:
                cur.next = pHead2
                pHead2 = pHead2.next
            cur = cur.next
        # 如果某个链表还有剩余,则直接将其接到新链表的末尾
        if pHead1:
            cur.next = pHead1
        if pHead2:
            cur.next = pHead2
        return pHead.next  # 返回虚拟头结点的下一个节点,即合并后的链表头
 
 

方法二:递归
使用递归的方法不满足题目中对于空间复杂度的限制,但其解题的方式值得学习。

思路:
1.递归条件:比较当前两个链表中值较小的头节点,把它链接到已经合并的链表中,然后对两个链表剩余的节点依旧做排序操作,也就是再次比较两个链表的剩余部分的头节点的值;

2.基线条件:两个链表都为空.

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pHead1 ListNode类 
# @param pHead2 ListNode类 
# @return ListNode类
#
class Solution:
    def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode:
        # write code here
        if pHead1 is None:     #对空链表的特殊分析
            return pHead2
        if pHead2 is None:
            return pHead1
        pHead = None
        if pHead1.val < pHead2.val:
            pHead = pHead1
            pHead.next = self.Merge(pHead1.next,pHead2)
        else:
            pHead = pHead2
            pHead.next = self.Merge(pHead1,pHead2.next)
        return pHead

1109. 航班预订统计

每次预定的时候只有两个地方会导致预定位置数发生变化,即
「预定开始的航班」 和 「预定结束的航班号+1的航班」
开始的地方会导致这个地方增加m个航班,结束的地方不再需要这些位置,所以会减去m个航班。
我们并不需要真的记录每个位置的状态,只需要记录有变化的位置即可,他们所持有的信息其实是一样的。

在「有变化的位置」添加变化的状态,之后以【叠加前面状态】的方式,来持续这个变化的状态
在「变化结束的位置」减去这个变化的状态,还原原来的状态
这里有 n 个航班,它们分别从 1 到 n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,里面的元素是每个航班预定的座位总数。

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号        1   2   3   4   5
预订记录 110  10
预订记录 220  20
预订记录 325  25  25  25
总座位数:      10  55  45  25  25
因此,answer = [10,55,45,25,25]
示例 2:

输入:bookings = [[1,2,10],[2,2,15]], n = 2
输出:[10,25]
解释:
航班编号        1   2
预订记录 110  10
预订记录 215
总座位数:      10  25
因此,answer = [10,25]
//官方题解 python 篇
class Solution:
    def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
        nums = [0] * n
        for left, right, inc in bookings:
            nums[left - 1] += inc
            if right < n:
                nums[right] -= inc
    
        for i in range(1, n):
            nums[i] += nums[i - 1]
        
        return nums

剑指 Offer 41. 数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:
输入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:
输入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

思路
要求随时可以提取一段数据流中的中位数,由题意可知,分两种情况:数据流个数为奇数,或者数据流个数为偶数。
我们可以想像,一串数据,我们以中间为节点切成两段,我们要求随时可以提取其中一段的顶部,或者两端的顶部求平均值。
在这里插入图片描述
如上图所示,如果保持整个数据有序(这里设置为递增)则,a比左边都大,b比左边都小。维护某一段数据,保持有序,最常用的办法就是堆排序。
那么,我们分别用两个堆,维护左边较小的数,使之成为大顶堆;右边较大的数,使之成为小顶堆。
由于python中自带的堆为小顶堆,那么要构建大顶堆,只需要对需要维护的数据取反即可。(值得注意的是,进入堆和出堆的时候,都要取反)

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.A = []#维护较小数的大顶堆
        self.B = []#维护较大数的小顶堆


    def addNum(self, num: int) -> None:
        # if self.A is None and self.B is None:
        #     heappush(self.A,-num)
        # elif len(self.A)==len(self.B):
        #     heappush(self.B,num)
        #     heappush(self.A,-heappop(self.B))
        # else:
        #     heappush(self.A,-num)
        #     heappush(self.B,-heappop(self.A))
        ############优化########################
        if len(self.A)==len(self.B):
            heappush(self.A,-heappushpop(self.B,num))
        else:
            heappush(self.B,-heappushpop(self.A,-num))
    def findMedian(self) -> float:
        if len(self.A)==len(self.B):
            return (-self.A[0]+self.B[0])/2
        else:return -self.A[0]


1438. 绝对差不超过限制的最长连续子数组

补充题17. 两个有序数组第k小的数

eg1:
A=[1,3,4,5,7,13,19]
B=[2,6,7,9,13,15,20]
k=6
啥意思呢?
请你找到第k小的数是多少?返回的结果是A/B中的某一个值,这个值,是AB融合之后再排序的数组C中,第k小那个数。
AB融合排序:1,2,3,4,5,6,7,7,9,13,13,19,20;
显然,第6小就是6,返回6即可。

二、笔试AC普通解法
1.双指针移动计数法
先别急着融合,我们不需要将整体数组全部融合,再去遍历求,那样速度将是o(N+M),太慢。
笔试为了简单编写代码,还能快速AC,怎么办呢?
设立2个指针,p1指着A的0位置,p2指着B的0位置;
然后领count==0做计时器,在指针滑动过程中,count计数,count计数到k就停止,说明找到了第k小的数。
p1,p2怎么滑动呢?

比较A[p1]和B[p2]的大小,谁小谁滑动,说明小的先排前面,做的工作就相当于融合AB数组为C。
但是count计数到了k就停

public static int findKthMinNum(int[] A, int[] B, int k){
        int p1 = 0;
        int p2 = 0;

        int m = A.length;
        int n = B.length;
        int count = 0;//计数器
        int ans = A[p1] <= B[p2] ? A[p1++] : B[p2++];//先定为这个
        if (k == 1) return ans;
        count++;//刚刚已经排好了一个了

        while (p1 < m && p2 < n){
            //同时不越界,其中一个越界,就只会剩另一个,之前有讲过merge代码就这样
            ans = A[p1] <= B[p2] ? A[p1++] : B[p2++];//谁小谁移动
            count++;
            if (count == k) return ans;
        }
        while (p1 < m){
            //如果A还没有搞定全
            ans = A[p1++];//谁小谁移动
            count++;
            if (count == k) return ans;
        }
        while (p1 < m){
            //如果B还没有搞定全
            ans = B[p2++];//谁小谁移动
            count++;
            if (count == k) return ans;
        }
        //全部都越界了还没有搞定,那只能是k过大
        return -1;
    }

补充题13. 中文数字转阿拉伯数字

214. 最短回文串

给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。

示例 1:

输入: “aacecaaa”
输出: “aaacecaaa”
示例 2:

输入: “abcd”
输出: “dcbabcd”

class Solution:
    def shortestPalindrome(self, s: str) -> str:
        #思路:直觉:从第一个字符开始找回文串,剩下的再反转到前面,必然是正确答案,不可能通过在开头插入字符来得到更短的回文。
        rev = s[::-1]
        idx = 0
        n = len(s)
        for i in range(n):
            if s[:n-i] == rev[i:]:
                return rev[:i]+s
        return ""

716. 最大栈

设计一个最大栈数据结构,既支持栈操作,又支持查找栈中最大元素。

实现 MaxStack 类:

MaxStack() 初始化栈对象
void push(int x) 将元素 x 压入栈中。
int pop() 移除栈顶元素并返回这个元素。
int top() 返回栈顶元素,无需移除。
int peekMax() 检索并返回栈中最大元素,无需移除。
int popMax() 检索并返回栈中最大元素,并将其移除。如果有多个最大元素,只要移除 最靠近栈顶 的那个。
示例:

输入
["MaxStack", "push", "push", "push", "top", "popMax", "top", "peekMax", "pop", "top"]
[[], [5], [1], [5], [], [], [], [], [], []]
输出
[null, null, null, null, 5, 5, 1, 5, 1, 5]

解释
MaxStack stk = new MaxStack();
stk.push(5);   // [5] - 5 既是栈顶元素,也是最大元素
stk.push(1);   // [5, 1] - 栈顶元素是 1,最大元素是 5
stk.push(5);   // [5, 1, 5] - 5 既是栈顶元素,也是最大元素
stk.top();     // 返回 5[5, 1, 5] - 栈没有改变
stk.popMax();  // 返回 5[5, 1] - 栈发生改变,栈顶元素不再是最大元素
stk.top();     // 返回 1[5, 1] - 栈没有改变
stk.peekMax(); // 返回 5[5, 1] - 栈没有改变
stk.pop();     // 返回 1[5] - 此操作后,5 既是栈顶元素,也是最大元素
stk.top();     // 返回 5[5] - 栈没有改变

class MaxStack:

    def __init__(self):
        self.stack = []
        self.max_stack = [-10000001]

    def push(self, x: int) -> None:
        self.stack.append(x)
        self.max_stack.append(max(x, self.max_stack[-1]))

    def pop(self) -> int:
        self.max_stack.pop()
        return self.stack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def peekMax(self) -> int:
        return self.max_stack[-1]

    def popMax(self) -> int:
        val = self.max_stack.pop()

        temp = [self.stack.pop()]
        while temp[-1] != val:
            self.max_stack.pop()
            temp.append(self.stack.pop())

        temp.pop()

        while temp:
            v = temp.pop()
            self.stack.append(v)
            self.max_stack.append(max(v, self.max_stack[-1]))

        return val

381. O(1) 时间插入、删除和获取随机元素 - 允许重复

解题思路: 这道题其实就是考一些语言特性,对于Python 语言我们可以直接用list来完成,插入使用append,删除使用remove,随机取数就随机一个int作为下标直接取。

import random

class RandomizedCollection:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.val_list = []

    def insert(self, val: int) -> bool:
        """
        Inserts a value to the collection. Returns true if the collection did not already contain the specified element.
        """
        if val in self.val_list:
            self.val_list.append(val)
            return False
        else:
            self.val_list.append(val)
            return True

    def remove(self, val: int) -> bool:
        """
        Removes a value from the collection. Returns true if the collection contained the specified element.
        """
        if val in self.val_list:
            self.val_list.remove(val)
            return True
        else:
            return False

    def getRandom(self) -> int:
        """
        Get a random element from the collection.
        """
        list_len = len(self.val_list)
        
        if list_len > 0:
            index = random.randint(0, list_len - 1)
            return self.val_list[index]
        else:
            return None



# Your RandomizedCollection object will be instantiated and called as such:

# obj = RandomizedCollection()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()

273. 整数转换英文表示

示例 1:

输入:num = 123
输出:“One Hundred Twenty Three”

示例 2:

输入:num = 12345
输出:“Twelve Thousand Three Hundred Forty Five”

示例 3:

输入:num = 1234567
输出:“One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven”

示例 4:

输入:num = 1234567891
输出:“One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One”

class Solution:
    def numberToWords(self, num: int) -> str:
        to19 = 'One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve ' \
               'Thirteen Fourteen Fifteen Sixteen Seventeen Eighteen Nineteen'.split()
        tens = 'Twenty Thirty Forty Fifty Sixty Seventy Eighty Ninety'.split()
        
        def helper(num):
            if num < 20:
                return to19[num - 1:num]
            if num < 100:
                return [tens[num // 10 -2]] + helper(num % 10)
            if num < 1000:
                return [to19[num // 100 - 1]] + ["Hundred"] + helper(num % 100)
            for p, w in enumerate(["Thousand", "Million", "Billion"], 1):
                if num < 1000 ** (p + 1):
                    return helper(num // 1000 ** p) + [w] + helper(num % 1000 ** p)
        return " ".join(helper(num)) or "Zero"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值