leetcode热题系列18

173. 二叉搜索树迭代器

实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。

调用 next() 将返回二叉搜索树中的下一个最小的数。
BSTIterator iterator = new BSTIterator(root);
iterator.next(); // 返回 3
iterator.next(); // 返回 7
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 9
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 15
iterator.hasNext(); // 返回 true
iterator.next(); // 返回 20
iterator.hasNext(); // 返回 false
提示:

next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 h 是树的高度。
你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
第一种思路:

利用BST的性质:BST的中序遍历为升序数组。

所以直接在init里用中序遍历然后存进数组里就好了。

时间复杂度:next() 和 hasNext()为O(1)

空间复杂度:O(N)

class BSTIterator(object):
 
    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.res = list()
        def inorder(node):
            if not node:
                return
            
            inorder(node.left)
            self.res.append(node.val)
            inorder(node.right)
        inorder(root)
        self.index = 0
 
    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        self.index += 1
        # print self.index - 1, self.res
        return self.res[self.index - 1]
        
 
    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return self.index < len(self.res)

第二种思路:

利用一个stack来存。

pushLeft这个函数的功能是把 一个节点和它的左孩子以及左孩子的左孩子… 压入栈。

时间复杂度:next() 和 hasNext()为O(1)

空间复杂度:O(h)

class BSTIterator(object):
 
    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.stack = []
        self.pushLeft(root)
 
    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        popedNode = self.stack.pop()
        self.pushLeft(popedNode.right)
        return popedNode.val
 
    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return len(self.stack) != 0
    
    def pushLeft(self, node):
        while(node):
            self.stack.append(node)
            node = node.left

第三种思路:

类似二叉树的迭代法实现中序遍历。

时间复杂度:next() 和 hasNext()为O(1)

空间复杂度:O(h)

class BSTIterator(object):
 
    def __init__(self, root):
        """
        :type root: TreeNode
        """
        self.stack = []
        self.cur = root
        
    def next(self):
        """
        @return the next smallest number
        :rtype: int
        """
        while self.cur or self.stack:
            if self.cur:
                self.stack.append(self.cur)
                self.cur = self.cur.left
            else:
                self.cur = self.stack.pop()
                res = self.cur.val
                self.cur = self.cur.right
 
                return res
            
 
    def hasNext(self):
        """
        @return whether we have a next smallest number
        :rtype: bool
        """
        return self.cur or self.stack

767. 重构字符串

题目描述
给定一个字符串 s ,检查是否能重新排布其中的字母,使得两相邻的字符不同。
返回 s 的任意可能的重新排列。若不可行,返回空字符串 “” 。

示例 :
示例 1:
输入: s = “aab”
输出: “aba”
示例 2:
输入: s = “aaab”
输出: “”

思路
抽屉原理

c > n/2上取整,一定无解;
c <= n/2上取整,一定有解
-当n为奇数,当c > n/2上取整 ,先放偶数位置;
-当c <= n/2, 先放奇数,放满后再放偶数;
解题思路: 设置一个大顶堆,每次取出现最多的字符和次多的字符。将这两个字符排入结果。每次重构完后要重新将更新的字符数量push进入大顶堆,直到大顶堆为空。注意python的heapq是小顶堆,所有对字符出现的次数取负,可以实现大顶堆。
题解:

import heapq

class Solution:
    def reorganizeString(self, S: str) -> str:
        if len(S) < 2:
            return ""

        count = collections.Counter(S)
        # 只有一个元素且数量大于1
        if len(count) == 1 and len(S) > 1:
            return ""

        # 出现的次数取负,构建大顶堆
        queue = [(-x[1], x[0]) for x in count.items()]
        heapq.heapify(queue)


        res = ""
        while len(queue) > 1:
            # 出现次数最多和次多的
            _, c1 = heapq.heappop(queue)
            _, c2 = heapq.heappop(queue)
            res += c1 + c2

            # 出现次数减一
            count[c1] -= 1
            count[c2] -= 1

            # 重新构建大顶堆
            if count[c1] > 0:
                heapq.heappush(queue, (-count[c1], c1))
            if count[c2] > 0:
                heapq.heappush(queue, (-count[c2], c2)) 

        # print(count)
        print(queue)
        if queue:
            if (queue[0][0]) == -1:
                res += queue[0][1]
            else:
            	# 最后剩余的数量大于1,返回""
                return ""
        
        return res

            

补充题24. 双栈排序

剑指 Offer 59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值

[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 创建一个队列
        deque = collections.deque()
        res,n = [], len(nums)
        for i,j in zip(range(1-k, n + 1 - k), range(n)):
            # 因为 i > 0,时候在进行处理的,则i - 1是窗口的尾部,j是窗口的头部,
            # 如果队列中首个数字等于窗口尾部的要弹出的数字,则进行删除.
            if i > 0 and deque[0] ==nums[i - 1]:
                # 删除deque中对应的nums[i-1],popleft将队列左侧的数字进行删除
                deque.popleft()
            # 如果deque不为空,且如果队列的尾部小于窗口的首的数字,则进行删除,如果一直小直到删除结束为止
            while deque and deque[-1] < nums[j]:
                deque.pop()
            # 将窗口首部的数字放入队列中
            deque.append(nums[j])
            #  i >= 0,这就说明窗口尾部已经位于nums列表上了
            if i >= 0:
                # 这个时候队列的首部就是这个窗口中的最大值
                res.append(deque[0])
        return res
            

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        if not nums or not k:
            return  []
        result = []
        for i in range(len(nums) - k + 1):
            result.append(max(nums[i: i + k]))
        return result

836. 矩形重叠

矩形以列表 [x1, y1, x2, y2] 的形式表示,其中 (x1, y1) 为左下角的坐标,(x2, y2) 是右上角的坐标。

如果相交的面积为正,则称两矩形重叠。需要明确的是,只在角或边接触的两个矩形不构成重叠。

给出两个矩形,判断它们是否重叠并返回结果。

示例 1:

输入:rec1 = [0,0,2,2], rec2 = [1,1,3,3]
输出:true

示例 2:

输入:rec1 = [0,0,1,1], rec2 = [1,0,2,1]
输出:false

class Solution(object):
    def isRectangleOverlap(self, rec1, rec2):
        """
        :type rec1: List[int]
        :type rec2: List[int]
        :rtype: bool
        """
        x1, y1, x2, y2 = rec1
        x3, y3, x4, y4 = rec2
        return (x4 - x1) * (x2 - x3) > 0 and (y4 - y1) * (y2 - y3) > 0

875. 爱吃香蕉的珂珂

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例 1:
输入: piles = [3,6,7,11], H = 8
输出: 4
示例 2:

输入: piles = [30,11,23,4,20], H = 5
输出: 30
示例 3:

输入: piles = [30,11,23,4,20], H = 6
输出: 23
思路:

二分查找,已知左右极值为ceil(sum(piles)//H),max(piles),

然后可以用二分法缩小范围。跟1014非常相似。

class Solution(object):
    def minEatingSpeed(self, piles, H):
        """
        :type piles: List[int]
        :type H: int
        :rtype: int
        """
        import math
 
        if len(piles) == 1:
            return int(math.ceil(piles[0] // H) + 1)
 
        lo, hi = math.ceil(sum(piles)/H), max(piles)
 
        while(lo < hi):
            mid = (lo + hi)// 2
            
            cnt = 0
            for pile in piles:
                cnt += math.ceil(pile / mid)
                    
            # print k, cnt
            if cnt > H:# 吃得慢了
                lo = mid + 1
            elif cnt <= H:
                hi = mid
                
        return int(lo)

772. 基本计算器 III

127. 单词接龙

1206. 设计跳表

题目
不使用任何库函数,设计一个 跳表 。

跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。

例如,一个跳表包含 [30, 40, 50, 60, 70, 90] ,然后增加 80、45 到跳表中,以下图的方式操作:
在这里插入图片描述
跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。

了解更多 : https://en.wikipedia.org/wiki/Skip_list

在本题中,你的设计应该要包含这些函数:

bool search(int target) : 返回target是否存在于跳表中。

void add(int num): 插入一个元素到跳表。

bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回false. 如果存在多个 num ,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。

示例
示例 1:
输入
[“Skiplist”, “add”, “add”, “add”, “search”, “add”, “search”, “erase”, “erase”, “search”]
[[], [1], [2], [3], [0], [4], [1], [0], [1], [1]]

输出
[null, null, null, null, false, null, true, false, true, false]

解释
Skiplist skiplist = new Skiplist();
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
skiplist.search(0); // 返回 false
skiplist.add(4);
skiplist.search(1); // 返回 true
skiplist.erase(0); // 返回 false,0 不在跳表中
skiplist.erase(1); // 返回 true
skiplist.search(1); // 返回 false,1 已被擦除

提示:
0 <= num, target <= 2 * 104
调用search, add, erase操作次数不大于 5 * 104

思路
维持一个哈希表,键为要存储的值,值为要存储值的数量
详细见代码

class Skiplist:
    def __init__(self):
        # 维持哈希表
        self.temp = {}
    def search(self, target: int) -> bool:
        # 看target是否在哈希表的键中,或哈希表的值为0
        return target in self.temp.keys() and self.temp.get(target) >= 1
    def add(self, num: int) -> None:
        # 哈希表中存在,数量加一
        if num in self.temp.keys():
            self.temp[num] += 1
        else:
            # 哈希表中不存在,直接添加
            self.temp[num] = 1
    def erase(self, num: int) -> bool:
        # 检查num是否在哈希表的键中,或者数量已经减为零
        if num not in self.temp.keys() or self.temp.get(num) < 1:
            return False
        # 存在的情况,哈希表中对应的值减一
        self.temp[num] -= 1
        return True

1233. 删除子文件夹

你是一位系统管理员,手里有一份文件夹列表 folder,你的任务是要删除该列表中的所有 子文件夹,并以 任意顺序 返回剩下的文件夹。

如果文件夹 folder[i] 位于另一个文件夹 folder[j] 下,那么 folder[i] 就是 folder[j] 的 子文件夹 。

文件夹的「路径」是由一个或多个按以下格式串联形成的字符串:‘/’ 后跟一个或者多个小写英文字母。例如,“/leetcode” 和 “/leetcode/problems” 都是有效的路径,而空字符串和 “/” 不是。

二、示例
2.1> 示例 1:
【输入】folder = [“/a”,“/a/b”,“/c/d”,“/c/d/e”,“/c/f”]
【输出】[“/a”,“/c/d”,“/c/f”]
【解释】“/a/b” 是 “/a” 的子文件夹,而 “/c/d/e” 是 “/c/d” 的子文件夹。

2.2> 示例 2:
【输入】folder = [“/a”,“/a/b/c”,“/a/b/d”]
【输出】[“/a”]
【解释】文件夹 “/a/b/c” 和 “/a/b/d” 都会被删除,因为它们都是 “/a” 的子文件夹。

2.3> 示例 3:
【输入】 folder = [“/a/b/c”,“/a/b/ca”,“/a/b/d”]
【输出】 [“/a/b/c”,“/a/b/ca”,“/a/b/d”]

class Solution:
    def removeSubfolders(self, folder: List[str]) -> List[str]:
        ans = []
        last = " "
        folder.sort()
        for f in folder:
            if not f.startswith(last):
                ans.append(f)
                last = f + "/"
        return ans

面试题 02.02. 返回倒数第 k 个节点

在这里插入图片描述

class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        cur = head
        cnt = 1

        while cur.next:
            cnt += 1
            cur = cur.next
        
        cur = head
        for i in range(cnt - k):
            cur = cur.next
        
        return cur.val


class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        fast = head
        slow = head
        while k > 0:
            fast = fast.next
            k -= 1
        while fast != None:
            fast = fast.next
            slow = slow.next
        return slow.val

1658. 将 x 减到 0 的最小操作数

一.题目
给你一个整数数组 n u m s numsnums和一个整数 x xx 。每一次操作时,你应当移除数组 n u m s numsnums 最左边或最右边的元素,然后从 x xx 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x xx 恰好减到0 00,返回最小操作数 ;否则,返回− 1 -1−1 。

示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。

示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1

示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
题解

from typing import List

class Solution:
    def minOperations(self, nums: List[int], x: int) -> int:
        #初始化满足条件的最长子序列长res为0
        s,res = 0,0
        #计算sum -x 
        k = sum(nums) - x
        #初始化字典
        map = {}
        map[0]=-1
        for i in range(len(nums)):
            #累加计算total[i]
            s += nums[i]
            #寻找是否存在键为total[i] - (sum -x)的元素
            if map.get(s - k) != None:
                res = max(i - map.get(s-k),res)
            #存储<total[i],i>
            if map.get(s) == None:
                map[s] = i
        #排除nums数组和为x的情况,这种情况下,res的值也为0
        if sum(nums) == x:
            return len(nums)
        return len(nums) - res if res != 0 else -1


905. 按奇偶排序数组

题目描述:
给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。

返回满足此条件的 任一数组 作为答案。

示例 1:
输入:nums = [3,1,2,4]
输出:[2,4,3,1]
解释:[4,2,3,1]、[2,4,1,3] 和 [4,2,1,3] 也会被视作正确答案。

示例 2:
输入:nums = [0]
输出:[0]

提示:
1 <= nums.length <= 5000
0 <= nums[i] <= 5000

该问题任务较为清晰简单,可以使用双指针法,类似于快速排序算法的思想,两个指针分别从头和尾向尾和头遍历,分别找到第一个奇数和最后一个偶数,将两者交换位置,直至两个指针的相对位置改变。

class Solution:
    def sortArrayByParity(self, nums: List[int]) -> List[int]:
        left, right = 0, len(nums)-1
        while left<right:
            while left<right and nums[left]%2==0:
                left += 1
            while right>left and nums[right]%2:
                right -= 1
            if left==right:
                break
            nums[left], nums[right] = nums[right], nums[left]
        return nums

解题思路一:双指针,用last指向数组尾部,用i指向当前遍历位置,若遇到奇数与last交换,若交换过来的仍然是奇数,则让–i。注意for循环需要i<n且i<last

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        int n = nums.size();
        int last = n-1;
        for(int i=0;i<n&&i<last;++i){
            if(nums[i]%2==1){
                int temp=nums[i];
                nums[i]=nums[last];
                nums[last]=temp;
                --last;
            }
            if(nums[i]%2==1) --i;
        }
        return nums;
    }
};

解题思路二:两次遍历,第一次加入偶数,第二次加入奇数

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        vector<int> res;
        for (auto & num : nums) {
            if (num % 2 == 0) {
                res.push_back(num);
            }
        }
        for (auto & num : nums) {
            if (num % 2 == 1) {
                res.push_back(num);
            }
        }
        return res;
    }
};

解题思路三:双指针 + 一次遍历。遇到偶数,保存在数组前端,遇到奇数保存在数组后端

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n);
        int left = 0, right = n - 1;
        for (auto & num : nums) {
            if (num % 2 == 0) {
                res[left++] = num;
            } else {
                res[right--] = num;
            }
        }
        return res;
    }
};

解题思路四:原地交换,左边找一个奇数,右边找一个偶数。然后交换,注意left一直要小于right

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            while (left < right and nums[left] % 2 == 0) {
                left++;
            }
            while (left < right and nums[right] % 2 == 1) {
                right--;
            }
            if (left < right) {
                swap(nums[left++], nums[right--]);
            }
        }
        return nums;
    }
};

补充题20. 立方根

1280. 学生们参加各科测试的次数

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

select st.student_id,st.student_name,st.student_name,count(ex.student_id) attended_exams
from students st
    join subjects su
    left join examinations ex
    on st.student_id = ex.student_id and su.subject_name = ex.subject_name
group by st.student_id,su.subject_name;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值