2024-01-03 每日一练

发布时间 2024-01-03 17:49:57作者: 逝玄

LeetCode 每日一题

2487. 从链表中移除节点

问题

给你一个链表的头节点 head 。

移除每个右侧有一个更大数值的节点。

返回修改后链表的头节点 head 。

解答

  1. 首先可以看出这是一个非常典型的单调栈,所以直接用单调栈即可解决
  2. 其次,单调栈是栈,那么就可以考虑递归,类似于 拓展1 的反转链表递归方法,可以利用后续遍历的思想进行递归
  3. 最后,也可以使用反转链表实现

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNodes(struct ListNode* head) {
    struct ListNode *stack[100000] = {0};
    stack[0] = head;
    int top = 1;
    head = head -> next;
    while (head) {
        while (top && head -> val > stack[top - 1] -> val) {
            top--;
        }
        stack[top++] = head;
        head = head -> next;
    }
    for(int i = 0; i < top - 1; i++) {
        stack[i] -> next = stack[i + 1];
    }
    stack[top - 1] -> next = 0;
    return stack[0];
}

拓展

  1. 206. 反转链表
  2. 92. 反转链表 II

依据灵神的网站题目顺序

1325. 删除给定值的叶子节点

问题

给你一棵以 root 为根的二叉树和一个整数 target ,请你删除所有值为 target 的 叶子节点 。

注意,一旦删除值为 target 的叶子节点,它的父节点就可能变成叶子节点;如果新叶子节点的值恰好也是 target ,那么这个节点也应该被删除。

也就是说,你需要重复此过程直到不能继续删除。

解答

很显然的 后序遍历

代码:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]:
        def dfs(p, node, flag):
            if not node:
                return None
            l = dfs(node, node.left, 0)
            r = dfs(node, node.right, 1)
            if not l and not r and node.val == target:
                node = None
                if p and flag == 0:
                    p.left = node
                elif p and flag == 1:
                    p.right = node
            return node
        return dfs(None, root, 0)
# 优化一下
class Solution:
    def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]:
        def dfs(node):
            if not node:
                return None
            node.left = dfs(node, node.left, 0)
            node.right = dfs(node, node.right, 1)
            if not node.left and not node.right and node.val == target:
                node = None
            return node
        return dfs(None, root, 0)

拓展

1217. 玩筹码

问题

有 n 个筹码。第 i 个筹码的位置是 position[i] 。

我们需要把所有筹码移到同一个位置。在一步中,我们可以将第 i 个筹码的位置从 position[i] 改变为:

position[i] + 2 或 position[i] - 2 ,此时 cost = 0
position[i] + 1 或 position[i] - 1 ,此时 cost = 1
返回将所有筹码移动到同一位置上所需要的 最小代价 。

解答

因为整个数组的下标都是在自然数域内,所以可以考虑将操作扩充至移动 n 次需要花多少钱,此为原子操作复杂化
将两个操作合在一起则变为 移动偶数次不需要花钱,移动奇数次需要花 1 元

代码:

class Solution:
    def minCostToMoveChips(self, position: List[int]) -> int:
        cnt = [0, 0]
        for i in position:
            cnt[i & 1] += 1
        return min(cnt)

拓展

1006. 笨阶乘

问题

  1. 数学问题数学方法解,从中寻找数学规律
  2. 公式问题栈来解,类似于扩展的三道题,其本质是符号的优先级,讲到优先级,我们应该联想其本质 偏序关系,而本质上,单调栈 就是获取 偏序集 的一种思想

解答

代码:

int clumsy(int n) {
    int stk[n], top = 0;
    stk[top++] = n;
    n--;

    int index = 0; // 用于控制乘、除、加、减
    while (n > 0) {
        if (index % 4 == 0) {
            stk[top - 1] *= n;
        } else if (index % 4 == 1) {
            stk[top - 1] /= n;
        } else if (index % 4 == 2) {
            stk[top++] = n;
        } else {
            stk[top++] = -n;
        }
        index++;
        n--;
    }

    // 把栈中所有的数字依次弹出求和
    int sum = 0;
    while (top) {
        sum += stk[--top];
    }
    return sum;
}

// 作者:力扣官方题解
// 链接:https://leetcode.cn/problems/clumsy-factorial/solutions/692629/ben-jie-cheng-by-leetcode-solution-deh2/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
int clumsy(int n) {
    if (n == 1) {
        return 1;
    } else if (n == 2) {
        return 2;
    } else if (n == 3) {
        return 6;
    } else if (n == 4) {
        return 7;
    }

    if (n % 4 == 0) {
        return n + 1;
    } else if (n % 4 <= 2) {
        return n + 2;
    } else {
        return n - 1;
    }
}

// 作者:力扣官方题解
// 链接:https://leetcode.cn/problems/clumsy-factorial/solutions/692629/ben-jie-cheng-by-leetcode-solution-deh2/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

拓展

  1. 150. 逆波兰表达式求值
  2. 224. 基本计算器
  3. 227. 基本计算器 II

2110. 股票平滑下跌阶段的数目

问题

给你一个整数数组 prices ,表示一支股票的历史每日股价,其中 prices[i] 是这支股票第 i 天的价格。

一个 平滑下降的阶段 定义为:对于 连续一天或者多天 ,每日股价都比 前一日股价恰好少 1 ,这个阶段第一天的股价没有限制。

请你返回 平滑下降阶段 的数目。

解答

对于某一天,当前的利益只与之前的几天有关,这是典型的动态规划问题

代码:

class Solution:
    def getDescentPeriods(self, prices: List[int]) -> int:
        n = len(prices)
        res = 1   # 平滑下降阶段的总数,初值为 dp[0]
        prev = 1   # 上一个元素为结尾的平滑下降阶段的总数,初值为 dp[0]
        # 从 1 开始遍历数组,按照递推式更新 prev 以及总数 res
        for i in range(1, n):
            if prices[i] == prices[i-1] - 1:
                prev += 1
            else:
                prev = 1
            res += prev
        return res

# 作者:力扣官方题解
# 链接:https://leetcode.cn/problems/number-of-smooth-descent-periods-of-a-stock/solutions/1167833/gu-piao-ping-hua-xia-die-jie-duan-de-shu-w3hi/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

拓展