当前位置:   article > 正文

华为OD机试 - 求幸存数之和(Java & JS & 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模式处理,但是会将输入处理 与 算法逻辑 分开,大家只看算法逻辑即可。

题目用例删点过程如下:

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

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


2023.12.12

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

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

JS算法源码

动态数组解法
  1. const rl = require("readline").createInterface({ input: process.stdin });
  2. var iter = rl[Symbol.asyncIterator]();
  3. const readline = async () => (await iter.next()).value;
  4. // 输入处理
  5. void (async function () {
  6. const nums = (await readline()).split(",").map(Number);
  7. const jump = parseInt(await readline());
  8. const left = parseInt(await readline());
  9. console.log(sumOfLeft(nums, jump, left));
  10. })();
  11. function sumOfLeft(nums, jump, left) {
  12. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  13. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  14. // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  15. let start = 1;
  16. // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  17. while (nums.length > left) {
  18. // 跳 jump 次
  19. start += jump;
  20. // 为了避免越界,新起跳点索引位置对剩余节点数取余
  21. start %= nums.length;
  22. nums.splice(start, 1);
  23. }
  24. return nums.reduce((a, b) => a + b, 0);
  25. }
循环链表解法
  1. const rl = require("readline").createInterface({ input: process.stdin });
  2. var iter = rl[Symbol.asyncIterator]();
  3. const readline = async () => (await iter.next()).value;
  4. // 输入处理
  5. void (async function () {
  6. const nums = (await readline()).split(",").map(Number);
  7. const jump = parseInt(await readline());
  8. const left = parseInt(await readline());
  9. console.log(sumOfLeft(nums, jump, left));
  10. })();
  11. // 循环链表的节点定义
  12. class Node {
  13. constructor(val) {
  14. this.val = val;
  15. this.prev = null;
  16. this.next = null;
  17. }
  18. }
  19. // 循环链表定义
  20. class CycleLink {
  21. constructor(nums) {
  22. this.head = null; // 私有属性,仅用于链接tail,完成循环
  23. this.tail = null; // 私有属性,仅用于链接head,完成循环
  24. this.cur = null; // 循环链表遍历指针
  25. this.size = 0; // 循环链表的节点数
  26. this.sum = 0; // 循环链表中所有节点值之和
  27. // 初始化循环链表
  28. for (let num of nums) {
  29. // 向循环链表中添加节点
  30. this.add(num);
  31. }
  32. // 将普通链表头尾相连,形成循环链表
  33. if (this.head != null) {
  34. this.head.prev = this.tail;
  35. this.tail.next = this.head;
  36. // 初始时循环链表的遍历指针指向头位值
  37. this.cur = this.head;
  38. }
  39. }
  40. add(val) {
  41. const node = new Node(val);
  42. if (this.size == 0) {
  43. this.head = node;
  44. this.tail = node;
  45. } else {
  46. this.tail.next = node;
  47. node.prev = this.tail;
  48. this.tail = node;
  49. }
  50. this.sum += val;
  51. this.size++;
  52. }
  53. // 删除循环链表cur指针指向的节点
  54. remove() {
  55. // 被删除节点的值从 循环链表和 中去除
  56. this.sum -= this.cur.val;
  57. // 循环链表节点数量-1
  58. this.size--;
  59. // 完成删除节点动作
  60. const prev = this.cur.prev;
  61. const next = this.cur.next;
  62. prev.next = next;
  63. next.prev = prev;
  64. this.cur.prev = null;
  65. this.cur.next = null;
  66. // 遍历指针指向被删除节点的下一个节点
  67. this.cur = next;
  68. }
  69. // 遍历下一个循环链表节点
  70. next() {
  71. this.cur = this.cur.next;
  72. }
  73. }
  74. function sumOfLeft(nums, jump, left) {
  75. const link = new CycleLink(nums);
  76. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  77. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  78. // 这里我们从起跳点的下一个节点开始
  79. link.next();
  80. // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
  81. while (link.size > left) {
  82. // 跳 jump 次, 为了避免冗余转圈, 其实只需要跳 jump % link.size
  83. const zip_jump = jump % link.size;
  84. for (let i = 0; i < zip_jump; i++) {
  85. link.next();
  86. }
  87. // 删除当前接节点(被删除的节点其实是下一次的起跳点),这里link.remove方法删除节点后会自动跳到被删除节点的下一个节点,即:起跳点的下一个节点
  88. link.remove();
  89. }
  90. return link.sum;
  91. }

Java算法源码

动态数组解法
  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.Scanner;
  4. import java.util.stream.Collectors;
  5. public class Main {
  6. public static void main(String[] args) {
  7. Scanner sc = new Scanner(System.in);
  8. int[] nums = Arrays.stream(sc.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
  9. int jump = Integer.parseInt(sc.nextLine());
  10. int left = Integer.parseInt(sc.nextLine());
  11. System.out.println(new Main().sumOfLeft(nums, jump, left));
  12. }
  13. public int sumOfLeft(int[] nums, int jump, int left) {
  14. ArrayList<Integer> list =
  15. (ArrayList<Integer>) Arrays.stream(nums).boxed().collect(Collectors.toList());
  16. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  17. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  18. // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  19. int start = 1;
  20. // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  21. while (list.size() > left) {
  22. // 跳 jump 次
  23. start += jump;
  24. // 为了避免越界,新起跳点索引位置对剩余节点数取余
  25. start %= list.size();
  26. list.remove(start);
  27. }
  28. return list.stream().reduce(Integer::sum).orElse(0);
  29. }
  30. }

循环链表解法
  1. import java.util.Arrays;
  2. import java.util.Scanner;
  3. public class Main {
  4. public static void main(String[] args) {
  5. Scanner sc = new Scanner(System.in);
  6. int[] nums = Arrays.stream(sc.nextLine().split(",")).mapToInt(Integer::parseInt).toArray();
  7. int jump = Integer.parseInt(sc.nextLine());
  8. int left = Integer.parseInt(sc.nextLine());
  9. System.out.println(new Main().sumOfLeft(nums, jump, left));
  10. }
  11. // 循环链表的节点定义
  12. static class Node {
  13. int val;
  14. Node prev;
  15. Node next;
  16. public Node(int val) {
  17. this.val = val;
  18. }
  19. }
  20. // 循环链表定义
  21. static class CycleLink {
  22. private Node head; // 私有属性,仅用于链接tail,完成循环
  23. private Node tail; // 私有属性,仅用于链接head,完成循环
  24. public Node cur; // 循环链表遍历指针
  25. public int size; // 循环链表的节点数
  26. public int sum; // 循环链表中所有节点值之和
  27. // 初始化循环链表
  28. public CycleLink(int[] nums) {
  29. // 向循环链表中添加节点
  30. for (int num : nums) {
  31. this.add(num);
  32. }
  33. // 将普通链表头尾相连,形成循环链表
  34. if (this.head != null) {
  35. this.head.prev = this.tail;
  36. this.tail.next = this.head;
  37. // 初始时循环链表的遍历指针指向头位值
  38. this.cur = this.head;
  39. }
  40. }
  41. private void add(int val) {
  42. Node node = new Node(val);
  43. if (this.size == 0) {
  44. this.head = node;
  45. this.tail = node;
  46. } else {
  47. this.tail.next = node;
  48. node.prev = this.tail;
  49. this.tail = node;
  50. }
  51. this.sum += val;
  52. this.size++;
  53. }
  54. // 删除循环链表cur指针指向的节点
  55. public void remove() {
  56. // 被删除节点的值从 循环链表和 中去除
  57. this.sum -= this.cur.val;
  58. // 循环链表节点数量-1
  59. this.size--;
  60. // 完成删除节点动作
  61. Node prev = this.cur.prev;
  62. Node next = this.cur.next;
  63. prev.next = next;
  64. next.prev = prev;
  65. this.cur.prev = null;
  66. this.cur.next = null;
  67. // 遍历指针指向被删除节点的下一个节点
  68. this.cur = next;
  69. }
  70. // 遍历下一个循环链表节点
  71. public void next() {
  72. this.cur = this.cur.next;
  73. }
  74. }
  75. public int sumOfLeft(int[] nums, int jump, int left) {
  76. CycleLink link = new CycleLink(nums);
  77. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  78. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  79. // 这里我们从起跳点的下一个节点开始
  80. link.next();
  81. // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
  82. while (link.size > left) {
  83. // 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
  84. int zip_jump = jump % link.size;
  85. for (int i = 0; i < zip_jump; i++) {
  86. link.next();
  87. }
  88. // 删除当前接节点(被删除的节点其实是下一次的起跳点),这里link.remove方法删除节点后会自动跳到被删除节点的下一个节点,即:起跳点的下一个节点
  89. link.remove();
  90. }
  91. return link.sum;
  92. }
  93. }

Python算法源码

动态数组解法
  1. # 输入获取
  2. nums = list(map(int, input().split(",")))
  3. jump = int(input())
  4. left = int(input())
  5. # 算法入口
  6. def sumOfLeft(nums, jump, left):
  7. # 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  8. # 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  9. # 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  10. start = 1
  11. # 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  12. while len(nums) > left:
  13. # 跳 jump 次
  14. start += jump
  15. # 为了避免越界,新起跳点索引位置对剩余节点数取余
  16. start %= len(nums)
  17. nums.pop(start)
  18. return sum(nums)
  19. # 算法调用
  20. print(sumOfLeft(nums, jump, left))
循环链表解法
  1. # 循环链表的节点定义
  2. class Node:
  3. def __init__(self, val):
  4. self.val = val
  5. self.prev = None
  6. self.next = None
  7. # 循环链表定义
  8. class CycleLink:
  9. def __init__(self, nums):
  10. self.head = None # 私有属性,仅用于链接tail,完成循环
  11. self.tail = None # 私有属性,仅用于链接head,完成循环
  12. self.cur = None # 循环链表遍历指针
  13. self.size = 0 # 循环链表的节点数
  14. self.sum = 0 # 循环链表中所有节点值之和
  15. # 初始化循环链表
  16. for num in nums:
  17. # 向循环链表中添加节点
  18. self.add(num)
  19. # 将普通链表头尾相连,形成循环链表
  20. if self.head is not None:
  21. self.head.prev = self.tail
  22. self.tail.next = self.head
  23. # 初始时循环链表的遍历指针指向头位值
  24. self.cur = self.head
  25. def add(self, val):
  26. node = Node(val)
  27. if self.size == 0:
  28. self.head = node
  29. self.tail = node
  30. else:
  31. self.tail.next = node
  32. node.prev = self.tail
  33. self.tail = node
  34. self.sum += val
  35. self.size += 1
  36. # 删除循环链表cur指针指向的节点
  37. def remove(self):
  38. # 被删除节点的值从 循环链表和 中去除
  39. self.sum -= self.cur.val
  40. # 循环链表节点数量-1
  41. self.size -= 1
  42. # 完成删除节点动作
  43. prev = self.cur.prev
  44. next = self.cur.next
  45. prev.next = next
  46. next.prev = prev
  47. self.cur.prev = None
  48. self.cur.next = None
  49. # 遍历指针指向被删除节点的下一个节点
  50. self.cur = next
  51. # 遍历下一个循环链表节点
  52. def next(self):
  53. self.cur = self.cur.next
  54. # 输入获取
  55. nums = list(map(int, input().split(",")))
  56. jump = int(input())
  57. left = int(input())
  58. # 算法入口
  59. def sumOfLeft(nums, jump, left):
  60. link = CycleLink(nums)
  61. # 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  62. # 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  63. # 这里我们从起跳点的下一个节点开始
  64. link.next()
  65. # 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
  66. while link.size > left:
  67. # 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
  68. zip_jump = jump % link.size
  69. for _ in range(zip_jump):
  70. link.next()
  71. # 删除当前接节点(被删除的节点其实是下一次的起跳点),这里link.remove方法删除节点后会自动跳到被删除节点的下一个节点,即:起跳点的下一个节点
  72. link.remove()
  73. return link.sum
  74. # 算法调用
  75. print(sumOfLeft(nums, jump, left))

C算法源码

动态数组解法
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_SIZE 10000
  4. int sumOfLeft(int nums[], int nums_size, int jump, int left) {
  5. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  6. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  7. // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  8. int start = 1;
  9. // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  10. while (nums_size > left) {
  11. // 跳 jump 次
  12. start += jump;
  13. // 为了避免越界,新起跳点索引位置对剩余节点数取余
  14. start %= nums_size;
  15. for (int i = start; i < nums_size - 1; i++) {
  16. nums[i] = nums[i + 1];
  17. }
  18. nums_size--;
  19. }
  20. int sum = 0;
  21. for (int i = 0; i < nums_size; i++) {
  22. sum += nums[i];
  23. }
  24. return sum;
  25. }
  26. int main() {
  27. int nums[MAX_SIZE];
  28. int nums_size = 0;
  29. while (scanf("%d", &nums[nums_size++])) {
  30. if (getchar() != ',') break;
  31. }
  32. int jump;
  33. scanf("%d", &jump);
  34. int left;
  35. scanf("%d", &left);
  36. printf("%d\n", sumOfLeft(nums, nums_size, jump, left));
  37. return 0;
  38. }
循环链表解法
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MAX_SIZE 10000
  4. // 循环链表的节点定义
  5. typedef struct Node {
  6. int val;
  7. struct Node *prev;
  8. struct Node *next;
  9. } Node;
  10. // 循环链表定义
  11. typedef struct CycleLink {
  12. Node *head; // 私有属性,仅用于链接tail,完成循环
  13. Node *tail; // 私有属性,仅用于链接head,完成循环
  14. Node *cur; // 循环链表遍历指针
  15. int size; // 循环链表的节点数
  16. int sum; // 循环链表中所有节点值之和
  17. } CycleLink;
  18. void add_CycleLink(CycleLink *link, int val) {
  19. Node *node = (Node *) malloc(sizeof(Node));
  20. node->val = val;
  21. node->prev = NULL;
  22. node->next = NULL;
  23. if (link->size == 0) {
  24. link->head = node;
  25. link->tail = node;
  26. } else {
  27. link->tail->next = node;
  28. node->prev = link->tail;
  29. link->tail = node;
  30. }
  31. link->size++;
  32. link->sum += val;
  33. }
  34. // 初始化循环链表
  35. CycleLink *new_CycleLink(int nums[], int nums_size) {
  36. CycleLink *link = (CycleLink *) malloc(sizeof(CycleLink));
  37. link->head = NULL;
  38. link->tail = NULL;
  39. link->cur = NULL;
  40. link->size = 0;
  41. link->sum = 0;
  42. // 向循环链表中添加节点
  43. for (int i = 0; i < nums_size; i++) {
  44. add_CycleLink(link, nums[i]);
  45. }
  46. // 将普通链表头尾相连,形成循环链表
  47. if (link->head != NULL) {
  48. link->head->prev = link->tail;
  49. link->tail->next = link->head;
  50. // 初始时循环链表的遍历指针指向头位值
  51. link->cur = link->head;
  52. }
  53. return link;
  54. }
  55. // 删除循环链表cur指针指向的节点
  56. void remove_CycleLink(CycleLink *link) {
  57. // 循环链表节点数量-1
  58. link->size--;
  59. // 被删除节点的值从 循环链表和 中去除
  60. link->sum -= link->cur->val;
  61. // 完成删除节点动作
  62. Node *prev = link->cur->prev;
  63. Node *next = link->cur->next;
  64. prev->next = next;
  65. next->prev = prev;
  66. link->cur->prev = NULL;
  67. link->cur->next = NULL;
  68. // 遍历指针指向被删除节点的下一个节点
  69. link->cur = next;
  70. }
  71. // 遍历下一个循环链表节点
  72. void next_CycleLink(CycleLink *link) {
  73. link->cur = link->cur->next;
  74. }
  75. int sumOfLeft(int nums[], int nums_size, int jump, int left) {
  76. CycleLink *link = new_CycleLink(nums, nums_size);
  77. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  78. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  79. // 这里我们从起跳点的下一个节点开始
  80. next_CycleLink(link);
  81. // 如果链表中剩余节点数 > 幸存数量,则还需要继续删除节点
  82. while (link->size > left) {
  83. // 跳 jump 次,为了避免冗余转圈, 其实只需要跳 jump % link.size
  84. int zip_jump = jump % link->size;
  85. for (int i = 0; i < zip_jump; i++) {
  86. next_CycleLink(link);
  87. }
  88. // 删除当前接节点(被删除的节点其实是下一次的起跳点),这里link.remove方法删除节点后会自动跳到被删除节点的下一个节点,即:起跳点的下一个节点
  89. remove_CycleLink(link);
  90. }
  91. return link->sum;
  92. }
  93. int main() {
  94. int nums[MAX_SIZE];
  95. int nums_size = 0;
  96. while (scanf("%d", &nums[nums_size++])) {
  97. if (getchar() != ',') break;
  98. }
  99. int jump;
  100. scanf("%d", &jump);
  101. int left;
  102. scanf("%d", &left);
  103. printf("%d\n", sumOfLeft(nums, nums_size, jump, left));
  104. return 0;
  105. }

C++算法源码

动态数组解法
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int sumOfLeft(vector<int> &nums, int jump, int left) {
  4. // 从起跳点开始的话,需要跳jump+1次,到达需要删除的节点
  5. // 从起跳点下一个节点开始的话,需要跳jump次,到达需要删除的节点
  6. // 这里我们从起跳点的下一个节点开始,初始时起跳点为索引0,因此下一个节点为索引1
  7. int start = 1;
  8. // 如果剩余节点数 > 幸存数量,则还需要继续删除节点
  9. while (nums.size() > left) {
  10. // 跳 jump 次
  11. start += jump;
  12. // 为了避免越界,新起跳点索引位置对剩余节点数取余
  13. start %= nums.size();
  14. nums.erase(nums.begin() + start);
  15. }
  16. return accumulate(nums.begin(), nums.end(), 0);
  17. }
  18. int main() {
  19. vector<int> nums;
  20. string s;
  21. cin >> s;
  22. stringstream ss(s);
  23. string token;
  24. while (getline(ss, token, ',')) {
  25. nums.emplace_back(stoi(token));
  26. }
  27. int jump, left;
  28. cin >> jump >> left;
  29. cout << sumOfLeft(nums, jump, left) << endl;
  30. return 0;
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/208807
推荐阅读
相关标签
  

闽ICP备14008679号