华为OD机试 - 求幸存数之和(Java & JS & Python & C & C++),华为OD机试,求幸存数之和(多语言解决方案)

马肤

温馨提示:这篇文章已超过467天没有更新,请注意相关的内容是否还可用!

摘要:华为OD机试中的求幸存数之和问题,要求使用Java、JavaScript、Python、C和C++等编程语言解决。该问题旨在寻找数组中所有元素的和,同时排除掉重复出现的元素。通过不同的编程语言和算法实现,测试应聘者的编程能力和逻辑思考能力。

题目描述

给一个正整数数列 nums,一个跳数 jump,及幸存数量 left。

运算过程为:从索引0的位置开始向后跳,中间跳过 J 个数字,命中索引为 J+1 的数字,该数被敲出,并从该点起跳,以此类推,直到幸存 left 个数为止,然后返回幸存数之和。

约束:

  1. 0是第一个起跳点
  2. 起跳点和命中点之间间隔 jump 个数字,已被敲出的数字不计入在内。
  3. 跳到末尾时无缝从头开始(循环查找),并可以多次循环。
  4. 若起始时 left > len(nums) 则无需跳数处理过程。

方法设计:

/**
 * @param nums 正整数数列,长度范围 [1, 10000]
 * @param jump 跳数,范围 [1, 10000]
 * @param left 幸存数量,范围 [0, 10000]
 * @return 幸存数之和
 */
int sumOfLeft(int[] nums, int jump, int left)

输入描述

第一行输入正整数数列

第二行输入跳数

第三行输入幸存数量

输出描述

输出幸存数之和

用例

输入1,2,3,4,5,6,7,8,9

4

3

输出13
说明从1(索引为0)开始起跳,中间跳过 4 个数字,因此依次删除 6,2,8,5,4,7。剩余1,3,9,返回和为13

题目解析

本题考试时为核心代码模式,非ACM模式,即无需自己解析输入数据。

本博客代码实现仍然以ACM模式处理,但是会将输入处理 与 算法逻辑 分开,大家只看算法逻辑即可。

题目用例删点过程如下:

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第1张

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第2张

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第3张

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第4张

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第5张

华为OD机试 - 求幸存数之和(Java & JS Python C C++),华为OD机试,求幸存数之和(多语言解决方案) 第6张

通过上面图示我们可以发现,被删除节点其实是作为起跳点,因此基于普通数组来操作的话,既要实现节点删除,又要实现基于删除点进行下次的起跳,这个逻辑是比较复杂的。

我的想法是构建一个循环链表来代替数组,关于循环链表的实现细节,请看代码实现以及注释。


2023.12.12

Python自定义循环链表的性能表现不佳,反而使用动态数组性能更好。

因此,加了一个动态数组解法。

JS算法源码

动态数组解法
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
// 输入处理
void (async function () {
  const nums = (await readline()).split(",").map(Number);
  const jump = parseInt(await readline());
  const left = parseInt(await readline());
  console.log(sumOfLeft(nums, jump, left));
})();
function sumOfLeft(nums, jump, left) {
  // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  let start = 1;
  // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  while (nums.length > left) {
    // 跳 jump 次
    start += jump;
    // 为了避免越界,新起跳点索引位置对剩余节点数取余
    start %= nums.length;
    nums.splice(start, 1);
  }
  return nums.reduce((a, b) => a + b, 0);
}
循环链表解法
const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
// 输入处理
void (async function () {
  const nums = (await readline()).split(",").map(Number);
  const jump = parseInt(await readline());
  const left = parseInt(await readline());
  console.log(sumOfLeft(nums, jump, left));
})();
// 循环链表的节点定义
class Node {
  constructor(val) {
    this.val = val;
    this.prev = null;
    this.next = null;
  }
}
// 循环链表定义
class CycleLink {
  constructor(nums) {
    this.head = null; // 私有属性,仅用于链接tail,完成循环
    this.tail = null; // 私有属性,仅用于链接head,完成循环
    this.cur = null; // 循环链表遍历指针
    this.size = 0; // 循环链表的节点数
    this.sum = 0; // 循环链表中所有节点值之和
    // 初始化循环链表
    for (let num of nums) {
      // 向循环链表中添加节点
      this.add(num);
    }
    // 将普通链表头尾相连,形成循环链表
    if (this.head != null) {
      this.head.prev = this.tail;
      this.tail.next = this.head;
      // 初始时循环链表的遍历指针指向头位值
      this.cur = this.head;
    }
  }
  add(val) {
    const node = new Node(val);
    if (this.size == 0) {
      this.head = node;
      this.tail = node;
    } else {
      this.tail.next = node;
      node.prev = this.tail;
      this.tail = node;
    }
    this.sum += val;
    this.size++;
  }
  // 删除循环链表cur指针指向的节点
  remove() {
    // 被删除节点的值从 循环链表和 中去除
    this.sum -= this.cur.val;
    // 循环链表节点数量-1
    this.size--;
    // 完成删除节点动作
    const prev = this.cur.prev;
    const next = this.cur.next;
    prev.next = next;
    next.prev = prev;
    this.cur.prev = null;
    this.cur.next = null;
    // 遍历指针指向被删除节点的下一个节点
    this.cur = next;
  }
  // 遍历下一个循环链表节点
  next() {
    this.cur = this.cur.next;
  }
}
function sumOfLeft(nums, jump, left) {
  const link = new CycleLink(nums);
  // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  // 这里我们从起跳点的下一个节点开始
  link.next();
  // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
  while (link.size > left) {
    // 跳 jump 次, 为了避免冗余转圈, 其实只需要跳 jump % link.size
    const zip_jump = jump % link.size;
    for (let i = 0; i  
 

Java算法源码

动态数组解法
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.stream.Collectors;
public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int[] nums = Arrays.stream(sc.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
    int jump = Integer.parseInt(sc.nextLine());
    int left = Integer.parseInt(sc.nextLine());
    System.out.println(new Main().sumOfLeft(nums, jump, left));
  }
  public int sumOfLeft(int[] nums, int jump, int left) {
    ArrayList list =
        (ArrayList) Arrays.stream(nums).boxed().collect(Collectors.toList());
    // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
    int start = 1;
    // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
    while (list.size() > left) {
      // 跳 jump 次
      start += jump;
      // 为了避免越界,新起跳点索引位置对剩余节点数取余
      start %= list.size();
      list.remove(start);
    }
    return list.stream().reduce(Integer::sum).orElse(0);
  }
}
循环链表解法
import java.util.Arrays;
import java.util.Scanner;
public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int[] nums = Arrays.stream(sc.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
    int jump = Integer.parseInt(sc.nextLine());
    int left = Integer.parseInt(sc.nextLine());
    System.out.println(new Main().sumOfLeft(nums, jump, left));
  }
  // 循环链表的节点定义
  static class Node {
    int val;
    Node prev;
    Node next;
    public Node(int val) {
      this.val = val;
    }
  }
  // 循环链表定义
  static class CycleLink {
    private Node head; // 私有属性,仅用于链接tail,完成循环
    private Node tail; // 私有属性,仅用于链接head,完成循环
    public Node cur; // 循环链表遍历指针
    public int size; // 循环链表的节点数
    public int sum; // 循环链表中所有节点值之和
    // 初始化循环链表
    public CycleLink(int[] nums) {
      // 向循环链表中添加节点
      for (int num : nums) {
        this.add(num);
      }
      // 将普通链表头尾相连,形成循环链表
      if (this.head != null) {
        this.head.prev = this.tail;
        this.tail.next = this.head;
        // 初始时循环链表的遍历指针指向头位值
        this.cur = this.head;
      }
    }
    private void add(int val) {
      Node node = new Node(val);
      if (this.size == 0) {
        this.head = node;
        this.tail = node;
      } else {
        this.tail.next = node;
        node.prev = this.tail;
        this.tail = node;
      }
      this.sum += val;
      this.size++;
    }
    // 删除循环链表cur指针指向的节点
    public void remove() {
      // 被删除节点的值从 循环链表和 中去除
      this.sum -= this.cur.val;
      // 循环链表节点数量-1
      this.size--;
      // 完成删除节点动作
      Node prev = this.cur.prev;
      Node next = this.cur.next;
      prev.next = next;
      next.prev = prev;
      this.cur.prev = null;
      this.cur.next = null;
      // 遍历指针指向被删除节点的下一个节点
      this.cur = next;
    }
    // 遍历下一个循环链表节点
    public void next() {
      this.cur = this.cur.next;
    }
  }
  public int sumOfLeft(int[] nums, int jump, int left) {
    CycleLink link = new CycleLink(nums);
    // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    // 这里我们从起跳点的下一个节点开始
    link.next();
    // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
    while (link.size > left) {
      // 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
      int zip_jump = jump % link.size;
      for (int i = 0; i  
 

Python算法源码

动态数组解法
# 输入获取
nums = list(map(int, input().split(",")))
jump = int(input())
left = int(input())
# 算法入口
def sumOfLeft(nums, jump, left):
    # 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    # 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    # 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
    start = 1
    # 如果剩余节点数 > 幸存数量,则还需要继续删除节点
    while len(nums) > left:
        # 跳 jump 次
        start += jump
        # 为了避免越界,新起跳点索引位置对剩余节点数取余
        start %= len(nums)
        nums.pop(start)
    return sum(nums)
# 算法调用
print(sumOfLeft(nums, jump, left))
循环链表解法
# 循环链表的节点定义
class Node:
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None
# 循环链表定义
class CycleLink:
    def __init__(self, nums):
        self.head = None  # 私有属性,仅用于链接tail,完成循环
        self.tail = None  # 私有属性,仅用于链接head,完成循环
        self.cur = None  # 循环链表遍历指针
        self.size = 0  # 循环链表的节点数
        self.sum = 0  # 循环链表中所有节点值之和
        # 初始化循环链表
        for num in nums:
            # 向循环链表中添加节点
            self.add(num)
        # 将普通链表头尾相连,形成循环链表
        if self.head is not None:
            self.head.prev = self.tail
            self.tail.next = self.head
            # 初始时循环链表的遍历指针指向头位值
            self.cur = self.head
    def add(self, val):
        node = Node(val)
        if self.size == 0:
            self.head = node
            self.tail = node
        else:
            self.tail.next = node
            node.prev = self.tail
            self.tail = node
        self.sum += val
        self.size += 1
    # 删除循环链表cur指针指向的节点
    def remove(self):
        # 被删除节点的值从 循环链表和 中去除
        self.sum -= self.cur.val
        # 循环链表节点数量-1
        self.size -= 1
        # 完成删除节点动作
        prev = self.cur.prev
        next = self.cur.next
        prev.next = next
        next.prev = prev
        self.cur.prev = None
        self.cur.next = None
        # 遍历指针指向被删除节点的下一个节点
        self.cur = next
    # 遍历下一个循环链表节点
    def next(self):
        self.cur = self.cur.next
# 输入获取
nums = list(map(int, input().split(",")))
jump = int(input())
left = int(input())
# 算法入口
def sumOfLeft(nums, jump, left):
    link = CycleLink(nums)
    # 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    # 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    # 这里我们从起跳点的下一个节点开始
    link.next()
    # 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
    while link.size > left:
        # 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
        zip_jump = jump % link.size
        for _ in range(zip_jump):
            link.next()
        # 删除当前接节点(被删除的节点其实是下一次的起跳点),这里link.remove方法删除节点后会自动跳到被删除节点的下一个节点,即:起跳点的下一个节点
        link.remove()
    return link.sum
# 算法调用
print(sumOfLeft(nums, jump, left))

C算法源码

动态数组解法
#include 
#include 
#define MAX_SIZE 10000
int sumOfLeft(int nums[], int nums_size, int jump, int left) {
    // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
    int start = 1;
    // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
    while (nums_size > left) {
        // 跳 jump 次
        start += jump;
        // 为了避免越界,新起跳点索引位置对剩余节点数取余
        start %= nums_size;
        for (int i = start; i  
循环链表解法
#include 
#include 
#define MAX_SIZE 10000
// 循环链表的节点定义
typedef struct Node {
    int val;
    struct Node *prev;
    struct Node *next;
} Node;
// 循环链表定义
typedef struct CycleLink {
    Node *head; // 私有属性,仅用于链接tail,完成循环
    Node *tail; // 私有属性,仅用于链接head,完成循环
    Node *cur; // 循环链表遍历指针
    int size; // 循环链表的节点数
    int sum; // 循环链表中所有节点值之和
} CycleLink;
void add_CycleLink(CycleLink *link, int val) {
    Node *node = (Node *) malloc(sizeof(Node));
    node->val = val;
    node->prev = NULL;
    node->next = NULL;
    if (link->size == 0) {
        link->head = node;
        link->tail = node;
    } else {
        link->tail->next = node;
        node->prev = link->tail;
        link->tail = node;
    }
    link->size++;
    link->sum += val;
}
// 初始化循环链表
CycleLink *new_CycleLink(int nums[], int nums_size) {
    CycleLink *link = (CycleLink *) malloc(sizeof(CycleLink));
    link->head = NULL;
    link->tail = NULL;
    link->cur = NULL;
    link->size = 0;
    link->sum = 0;
    // 向循环链表中添加节点
    for (int i = 0; i head != NULL) {
        link->head->prev = link->tail;
        link->tail->next = link->head;
        // 初始时循环链表的遍历指针指向头位值
        link->cur = link->head;
    }
    return link;
}
// 删除循环链表cur指针指向的节点
void remove_CycleLink(CycleLink *link) {
    // 循环链表节点数量-1
    link->size--;
    // 被删除节点的值从 循环链表和 中去除
    link->sum -= link->cur->val;
    // 完成删除节点动作
    Node *prev = link->cur->prev;
    Node *next = link->cur->next;
    prev->next = next;
    next->prev = prev;
    link->cur->prev = NULL;
    link->cur->next = NULL;
    // 遍历指针指向被删除节点的下一个节点
    link->cur = next;
}
// 遍历下一个循环链表节点
void next_CycleLink(CycleLink *link) {
    link->cur = link->cur->next;
}
int sumOfLeft(int nums[], int nums_size, int jump, int left) {
    CycleLink *link = new_CycleLink(nums, nums_size);
    // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    // 这里我们从起跳点的下一个节点开始
    next_CycleLink(link);
    // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
    while (link->size > left) {
        // 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
        int zip_jump = jump % link->size;
        for (int i = 0; i sum;
}
int main() {
    int nums[MAX_SIZE];
    int nums_size = 0;
    while (scanf("%d", &nums[nums_size++])) {
        if (getchar() != ',') break;
    }
    int jump;
    scanf("%d", &jump);
    int left;
    scanf("%d", &left);
    printf("%d\n", sumOfLeft(nums, nums_size, jump, left));
    return 0;
}

C++算法源码

动态数组解法
#include 
using namespace std;
int sumOfLeft(vector &nums, int jump, int left) {
    // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
    // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
    // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
    int start = 1;
    // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
    while (nums.size() > left) {
        // 跳 jump 次
        start += jump;
        // 为了避免越界,新起跳点索引位置对剩余节点数取余
        start %= nums.size();
        nums.erase(nums.begin() + start);
    }
    return accumulate(nums.begin(), nums.end(), 0);
}
int main() {
    vector nums;
    string s;
    cin >> s;
    stringstream ss(s);
    string token;
    while (getline(ss, token, ',')) {
        nums.emplace_back(stoi(token));
    }
    int jump, left;
    cin >> jump >> left;
    cout 

0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码