专栏名称: 吴师兄学算法
和程序员小吴一起从初学者的角度学习算法,以动画的形式呈现解题的思路。每周四篇原创文章,期待你的鉴赏!
目录
相关文章推荐
北京晚报  ·  补时遭绝杀!遗憾……但要看到希望 ·  8 小时前  
北京晚报  ·  补时遭绝杀!遗憾……但要看到希望 ·  8 小时前  
广电独家  ·  新集团挂牌成立!贵州广电改革再行一步 ·  3 天前  
51好读  ›  专栏  ›  吴师兄学算法

玩转华为OD机试200题:【栈】2024E-空栈压数

吴师兄学算法  · 公众号  ·  · 2024-12-12 11:30

正文

大家好,我是吴师兄。

2024 年 8 月开始,华为 OD 笔试从 2024D 卷改为2024E 卷。

E 卷里 70% 左右的题目都是以前 A/B/C/D 卷复用的旧题。如果之前的题目刷了不少,那么很大概率都能遇到原题。所以刷过往的题目也是非常有效果的。

相比起 D 卷,E 卷的题目难度平均了很多,删去了很多有争议的难题、偏题、错题。

D卷里面的:

  • 难题【最短路问题】2024D-快递员的烦恼、【回溯】2024D-田忌赛马、【最小生成树】2024D-5G网络建设

  • 偏题【模拟】2024D-学生重新排队、【模拟】2024D-移动元素获得最大数组和、【模拟】2024D-攀登者2

  • 错题【DP】2024D-抢7游戏、【贪心】2024D-小朋友来自多少小区、【双指针】2024D-提取字符串中最长数学表达式

都删掉了。

E 卷中难度为困难的题目少了很多,难度为简单的题目也少了很多,超过 50% 的题目(无论分值)都属于中等难度的题目。

这样的难度调整对认真学习、打牢基础的同学是更有利的,能够有效避免因为运气原因遇到太难或描述有问题的题目而考不到理想分数的情况。

截止目前, 我们已经直播讲解了 151 道 E 卷的题目 ,每一道题目都有详细的答题思路、视频讲解、搭配三到六种代码和逐行的中文注释、答疑,同时每周都会迭代和新增三到五题。

题目描述

向一个空栈压入 正整数 ,每当压入一个整数时,执行以下规则(设: 栈顶至栈底 整数依次编号为 n1, n2, ..., nx ,其中 n1 为最新压入的整数)

  1. 如果 n1 = n2 ,则 n1 n2 全部出栈,压入新数据 m (m = 2*n1)
  2. 如果 n1 = n2 + ... + ny ( y 的范围为 [3,x] ) ,则 n1, n2, ..., ny 全部出栈,压入新数据 m (m = 2*n1)
  3. 如果上述规则都不满足,则不做操作。

如:依次向栈压入 6、1、2、3 ,当压入 2 时,栈顶至栈底依次为 [2,1,6] ;当压入 3 时, 3 = 2 + 1 3、2、1 全部出栈,重新入栈整数 6 ,此时栈顶至栈底依次为 [6,6] 6 = 6 ,两个 6 全部出栈,压入 12 ,最终栈中只剩个元素 12

向栈中输入一串数字,请输出应用此规则后栈中最终存留的数字。

输入描述

使用单个空格隔开的正整数的字符串,如 "5 6 7 8" ,左边的数字先入栈。

  • 正整数大小为 [1, 2^31−1]
  • 正整数个数为 [1,1000]

输出描述

最终栈中存留的元素值,元素值使用单个空格隔开,如 "8 7 6 5" ,从左至右依次为 栈顶至栈底 的数字。

示例

输入

10 20 50 80 1 1

输出

2 160

解题思路

注意,本题最初见于华为2023年校招暑期实习的题目,在OJ上直接搜索 空栈压数 即可搜索得到题目。

由于本题的数据量较小,最多仅为 1000 。可以直接使用栈的数据结构,对题目进行模拟即可。

题目所给定的两种出栈情况,实际上可以归纳为同一种表述。

当某一个元素即将入栈时,如果发现栈顶的若干连续元素的和恰好等于该元素,则令栈中的这些若干连续元素出栈,换成将两倍的该元素入栈

所以我们只需要在每一个元素入栈之前,都进行上述过程的判断即可。我们可以构建这样的一个函数

# 检查是否需要弹出若干栈顶元素并进行压缩的函数
def check_stack_top(stack, num):
    # 初始化栈顶元素的和为0
    top_sum = 0
    # 初始化栈顶元素索引为idx,逆序遍历
    idx = len(stack) - 1
    # 进行循环,满足以下两个条件之一,则退出循环:
    # 1. 栈顶元素大于等于num
    # 2. 逆序遍历的索引idx小于0
    while top_sum < num and idx >= 0:
        # 在循环中,
        # 栈顶元素和递增
        # 栈顶元素索引递减
        top_sum += stack[idx]
        idx -= 1
    # 退出循环后,若栈顶元素和top_sum恰好等于num
    if top_sum == num:
        # 需要删除之前计入栈顶元素和中的所有元素,用切片操作即可
        # num更新为原来的两倍,返回这两个参数
        return stack[:idx+1], num * 2
    # 否则
    else:
        # 返回原先的stack和num
        return stack, num

在这个函数中,我们构建了参数 top_sum 表示栈顶元素和的情况,用索引 idx 从后往前逐个遍历。

一旦发现 top_sum >= num 的时候,则退出 while 循环。

退出 while 的时候,我们还需进一步做出判断,若

  • top_sum == num ,说明此时是需要要弹出若干连续栈顶元素的,并且需要令两倍的 num 入栈
  • top_sum != num ,说明此时是无需弹出若干连续栈顶元素的,把 num 入栈

特别需要注意的地方在于,如果此时我们是要把两倍的 num 入栈,那么这个时候又是一个新的数 2 * num 入栈,我们需要 重复判断是否存在若干连续栈顶元素的和为当前入栈的 2 * num 这个过程

比如题目描述中所给的例子,入栈顺序是 6 1 2 3 时,当 3 准备入栈时,发现栈顶元素 2 1 的和为 3 ,此时会弹出 2 1 ,将 3 * 2 = 6 作为即将入栈的元素。而当 6 准备入栈时,又发现了此时栈顶元素6和准备入栈的6相等,因此将栈顶元素 6 弹出,令 6 * 2 = 12 入栈。

由于无法判断上述重复判断入栈的过程需要几次,因此我们在函数外部设置了一个 while 循环。

使用一个 bool 类型标志 flag_continue_loop 来表示,在 check_stack_top(stack, num) 函数中我们 是否弹出了若干连续栈顶元素并且令 2 * num 准备入栈

# 从左到右,遍历每一个数字
for num in nums:
    # 设置变量flag_continue_loop为True
    # flag_continue_loop的值会在函数check_stack_top()中进行修改
    flag_continue_loop = True
    while flag_continue_loop:
        # 调用check_stack_top()函数,更新stack和num的情况
        stack, num = check_stack_top(stack, num)
    # 空栈的情况实际上也包含在上述函数中了
    # 做完计算,必须把最新得到的num加入栈中
    stack.append(num)

相对应的, check_stack_top(stack, num) 函数也需要加上关于 flag_continue_loop 的修改。

我们可以把 flag_continue_loop 作为一个全局变量来使用,当

  • 分支进入 top_sum == num 的时候,修改 flag_continue_loop = True
  • 分支进入 top_sum != num 的时候,修改 flag_continue_loop = False

最终 check_stack_top(stack, num) 呈现的代码为

# 检查是否需要弹出若干栈顶元素并进行压缩的函数
def check_stack_top(stack, num):
    global flag_continue_loop
    # 初始化栈顶元素的和为0
    top_sum = 0
    # 初始化栈顶元素索引为idx,逆序遍历
    idx = len(stack) - 1
    # 进行循环,满足以下两个条件之一,则退出循环:
    # 1. 栈顶元素大于等于num
    # 2. 逆序遍历的索引idx小于0
    while top_sum < num and idx >= 0:
        # 在循环中,
        # 栈顶元素和递增
        # 栈顶元素索引递减
        top_sum += stack[idx]
        idx -= 1
    # 退出循环后,若栈顶元素和top_sum恰好等于num
    if top_sum == num:
        # 需要继续进行循环,故设置flag为True
        flag_continue_loop = True
        # 需要删除之前计入栈顶元素和中的所有元素,用切片操作即可
        # num更新为原来的两倍,返回这两个参数
        return stack[:idx+1






请到「今天看啥」查看全文