数据 · 13
修改列表
判断是否包含、移除、反转、交换、用同一个值填充、复制。
列表建好之后,还可以移除值、改变顺序。
下面的例子先把一组卡片复制一份,询问某个数在不在其中,在就把它移除,再移除最前面的卡片,反转列表,交换最前面的两张。
最后输出修改后的列表、事先复制的原列表,以及一个用 0 填充的新列表。
n = int(input())cards = list(map(int, input().split()))x = int(input())original = cards.copy() # 복사해 둬요if x in cards: print("yes") cards.remove(x) # 값으로 빼기: 처음 나오는 하나만else: print("no")cards.pop(0) # 자리로 빼기: 0번cards.reverse()cards[0], cards[1] = cards[1], cards[0] # 자리 바꾸기blank = [0] * len(cards) # 0으로 채운 새 목록print(*cards)print(*original)print(*blank)const lines = require("fs").readFileSync(0, "utf8").trim().split("\n");const cards = lines[1].trim().split(/\s+/).map(Number);const x = Number(lines[2]);const original = [...cards]; // 복사해 둬요if (cards.includes(x)) { console.log("yes"); cards.splice(cards.indexOf(x), 1); // 값으로 빼기: 자리를 찾아서 하나만} else { console.log("no");}cards.splice(0, 1); // 자리로 빼기: 0번cards.reverse();[cards[0], cards[1]] = [cards[1], cards[0]]; // 자리 바꾸기const blank = new Array(cards.length).fill(0); // 0으로 채운 새 목록console.log(cards.join(" "));console.log(original.join(" "));console.log(blank.join(" "));import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").trim().split("\n");const cards: number[] = lines[1].trim().split(/\s+/).map(Number);const x: number = Number(lines[2]);const original: number[] = [...cards]; // 복사해 둬요if (cards.includes(x)) { console.log("yes"); cards.splice(cards.indexOf(x), 1); // 값으로 빼기: 자리를 찾아서 하나만} else { console.log("no");}cards.splice(0, 1); // 자리로 빼기: 0번cards.reverse();[cards[0], cards[1]] = [cards[1], cards[0]]; // 자리 바꾸기const blank: number[] = new Array<number>(cards.length).fill(0); // 0으로 채운 새 목록console.log(cards.join(" "));console.log(original.join(" "));console.log(blank.join(" "));import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Scanner; public class Main { static void show(List<Integer> list) { StringBuilder line = new StringBuilder(); for (int i = 0; i < list.size(); i++) line.append(i > 0 ? " " : "").append(list.get(i)); System.out.println(line); } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); List<Integer> cards = new ArrayList<>(); for (int i = 0; i < n; i++) cards.add(sc.nextInt()); int x = sc.nextInt(); List<Integer> original = new ArrayList<>(cards); // 복사해 둬요 if (cards.contains(x)) { System.out.println("yes"); cards.remove(Integer.valueOf(x)); // 값으로 빼기: 처음 나오는 하나만 } else { System.out.println("no"); } cards.remove(0); // 자리로 빼기: 0번 Collections.reverse(cards); Collections.swap(cards, 0, 1); // 자리 바꾸기 List<Integer> blank = new ArrayList<>(Collections.nCopies(cards.size(), 0)); // 0으로 채운 새 목록 show(cards); show(original); show(blank); }}using System;using System.Collections.Generic;using System.Linq; class Program{ static void Main() { int n = int.Parse(Console.ReadLine()); List<int> cards = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList(); int x = int.Parse(Console.ReadLine()); List<int> original = new List<int>(cards); // 복사해 둬요 if (cards.Contains(x)) { Console.WriteLine("yes"); cards.Remove(x); // 값으로 빼기: 처음 나오는 하나만 } else { Console.WriteLine("no"); } cards.RemoveAt(0); // 자리로 빼기: 0번 cards.Reverse(); (cards[0], cards[1]) = (cards[1], cards[0]); // 자리 바꾸기 List<int> blank = Enumerable.Repeat(0, cards.Count).ToList(); // 0으로 채운 새 목록 Console.WriteLine(string.Join(" ", cards)); Console.WriteLine(string.Join(" ", original)); Console.WriteLine(string.Join(" ", blank)); }}#include <algorithm>#include <iostream>#include <vector> void show(const std::vector<int>& list) { for (size_t i = 0; i < list.size(); i++) std::cout << (i > 0 ? " " : "") << list[i]; std::cout << '\n';} int main() { int n, x; std::cin >> n; std::vector<int> cards(n); for (int& c : cards) std::cin >> c; std::cin >> x; std::vector<int> original = cards; // 복사해 둬요(vector는 대입하면 복사) auto it = std::find(cards.begin(), cards.end(), x); if (it != cards.end()) { std::cout << "yes\n"; cards.erase(it); // 값으로 빼기: 찾은 자리 하나만 } else { std::cout << "no\n"; } cards.erase(cards.begin()); // 자리로 빼기: 0번 std::reverse(cards.begin(), cards.end()); std::swap(cards[0], cards[1]); // 자리 바꾸기 std::vector<int> blank(cards.size(), 0); // 0으로 채운 새 목록 show(cards); show(original); show(blank);}#include <stdio.h>#include <string.h> void show(const int *list, int n) { for (int i = 0; i < n; i++) printf(i > 0 ? " %d" : "%d", list[i]); printf("\n");} // 자리로 빼기: 뒤의 값들을 한 칸씩 당기고, 줄어든 개수를 돌려줘요int remove_at(int *list, int n, int at) { for (int i = at; i + 1 < n; i++) list[i] = list[i + 1]; return n - 1;} int main(void) { int n, x, cards[1000], original[1000], blank[1000]; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &cards[i]); scanf("%d", &x); int m = n; memcpy(original, cards, n * sizeof cards[0]); // 복사해 둬요 int at = -1; for (int i = 0; i < n && at < 0; i++) if (cards[i] == x) at = i; // 있나: 처음 나오는 자리 찾기 if (at >= 0) { printf("yes\n"); n = remove_at(cards, n, at); // 값으로 빼기 } else { printf("no\n"); } n = remove_at(cards, n, 0); // 자리로 빼기: 0번 for (int i = 0, j = n - 1; i < j; i++, j--) { // 뒤집기: 양 끝에서 맞바꿔요 int t = cards[i]; cards[i] = cards[j]; cards[j] = t; } int t = cards[0]; cards[0] = cards[1]; cards[1] = t; // 자리 바꾸기 for (int i = 0; i < n; i++) blank[i] = 0; // 0으로 채운 새 목록 show(cards, n); show(original, m); show(blank, n); return 0;}n = 控制台.读行().转数字();cards = 控制台.读数字();x = 控制台.读行().转数字();original = cards.子列表(0, cards.数量); // 복사해 둬요如果 (cards.包含(x)){ 控制台.输出("yes"); cards.移除(x); // 값으로 빼기: 처음 나오는 하나만}否则{ 控制台.输出("no");}cards.按位置移除(0); // 자리로 빼기: 0번cards.反转();t = cards[0]; // 자리 바꾸기cards[0] = cards[1];cards[1] = t;blank = 新列表(cards.数量, 0); // 0으로 채운 새 목록控制台.输出(cards);控制台.输出(original);控制台.输出(blank);5 30 10 40 10 50 10
yes 10 50 40 30 10 40 10 50 0 0 0
要点
- 判断是否包含的回答是真或假。
不用循环逐个比较,一行就能写出来(C 要用循环查找)。 - 移除有两种。
按位置移除去掉第 i 格,按值移除只去掉这个值第一次出现的那一格。
无论哪种,后面的值都会向前挪一格。
要移除的值不存在时会怎样,各语言不同。 - 反转不会生成新列表,而是把这个列表本身的顺序倒过来。
- 交换:交换两格的值时,如果先写
a[0] = a[1],a[0]原来的值就没了。
要先把其中一个放进临时变量,或者使用一次交换两者的写法。 - 用同一个值填充:只给出大小和初始值来建列表,比如含有 n 个 0 的列表。
- 复制:第 08 课说过,
b = a只是给同一个列表再起一个名字。
想保留原列表,要在修改之前另外复制一份。
在 Python、JavaScript、Java、C#、Craykon 中,这种复制只复制一层:列表里装着列表的网格(第 12 课)复制之后各行仍然是共用的,要把每一行分别复制。
各语言的写法
| Python | x in a。按值 a.remove(x)(不存在则 ValueError),按位置 a.pop(i) 或 del a[i]。a.reverse()。a[0], a[1] = a[1], a[0]。[0] * n。复制用 a.copy() |
|---|---|
| JavaScript | a.includes(x)。按位置 a.splice(i, 1)。没有按值移除的函数,要先用 a.indexOf(x) 找到位置再 splice。a.reverse()。[a[0], a[1]] = [a[1], a[0]]。new Array(n).fill(0)。复制用 [...a] |
| TypeScript | 和 JavaScript 一样 |
| Java | 在 List<Integer> 上用 a.contains(x)。按位置 a.remove(i),按值 a.remove(Integer.valueOf(x))(不存在则返回 false)。Collections.reverse(a)、Collections.swap(a, 0, 1)。new ArrayList<>(Collections.nCopies(n, 0))。复制用 new ArrayList<>(a)。数组 new int[n] 一开始就全是 0 |
| C# | 在 List<int> 上用 a.Contains(x)。按值 a.Remove(x)(不存在则返回 false),按位置 a.RemoveAt(i)。a.Reverse()。(a[0], a[1]) = (a[1], a[0])。Enumerable.Repeat(0, n).ToList()。复制用 new List<int>(a) |
| C++ | std::find(a.begin(), a.end(), x) 不等于 a.end() 就表示存在。找到的位置用 a.erase(it) 移除,第 i 格用 a.erase(a.begin() + i)。std::reverse(a.begin(), a.end())、std::swap(a[0], a[1])。std::vector<int> b(n, 0)。b = a 本身就是复制 |
| C | 数组大小固定,也没有这些函数。 用循环查找;移除时把后面的值逐个向前挪一格,再自己把个数减一。 反转是从两端向中间交换,交换用临时变量 t。复制用 memcpy(b, a, n * sizeof a[0]) — b = a 是编译错误 |
| Craykon | a.包含(x)。按值 a.移除(x)(不存在则返回 false),按位置 a.按位置移除(i)。a.反转()。交换用临时变量。 新列表(n, 0)。复制用 a.子列表(0, a.数量) |
常见错误
想移除值 1,结果第 1 格被移除了Java
List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
a.remove(1);
System.out.println(a);→ [1, 3] — 传入 int 时是按位置移除。第 1 格的 2 不见了
要按值移除,写 a.remove(Integer.valueOf(1)) → [2, 3]。
对 [10, 20, 30] 写 a.remove(10),会去找第 10 格,以 IndexOutOfBoundsException 停止。
一边遍历列表一边从中移除Python · JavaScript · TypeScript · Java · C#
a = [1, 1, 2, 3]
for v in a:
if v == 1:
a.remove(v)
print(a)→ Python 输出 [1, 2, 3] — 后一个值挪进了被移除的那一格,所以第二个 1 被跳过了。JavaScript 的 for...of 结果相同。Java 以 ConcurrentModificationException 停止,C# 以 InvalidOperationException 停止。只有 Craykon 的 遍历 是照着开始时拍下的列表快照来走的,所以两个 1 都被移除,得到 2 3
只把要保留的值收集成新列表,或者用专门的函数:Python a = [v for v in a if v != 1],JavaScript a = a.filter(v => v !== 1),Java a.removeIf(v -> v == 1),C# a.RemoveAll(v => v == 1)。
移除不存在的值,结果最后一个值被移除了JavaScript · TypeScript
const a = [1, 2, 3];
a.splice(a.indexOf(9), 1);
console.log(a);→ [ 1, 2 ] — 值不存在时 indexOf 给出 -1,而 splice(-1, 1) 移除的是最后一格
先确认是否存在:const i = a.indexOf(9); if (i !== -1) a.splice(i, 1);
反转是修改列表本身的命令,所以有的语言不能把它的结果拿来用。
Python 中写
b = a.reverse(),b 是 None。需要一个反转后的新列表时,Python 用
a[::-1],JavaScript 用先复制再反转的 [...a].reverse()。另外,C 中的
int *b = a; 不是复制,而是指向同一个数组的另一个名字:执行 b[0] = 9 后,a[0] 也变成 9。