CSP-J的考生们注意啦:火热题型〔I〕

CSP-J考题大纲

  • 一、选择题
    • 1. 计算机历史
      • 1.1 计算机历史人物
        • 1.1.1 艾伦 • 图灵
        • 1.1.2 莫西利和艾克特
        • 1.1.3 冯 • 诺伊曼
      • 1.2 计算机领域奖项
        • 1.2.1 图灵奖
        • 1.2.2 诺贝尔物理学奖
        • 1.2.3 约翰 • 冯 • 诺伊曼奖
    • 2. 计算机常识
      • 2.1 摩尔定律
      • 2.2 量子计算机
      • 2.3 二进制“和”与“或”
      • 2.4 面向对象
        • 2.4.1 面向对象的概念
        • 2.4.2 C++具有面向对象特性
        • 2.4.3 面向对象的程序设计语言
    • 3. C++常识
      • 3.1 数据结构
        • 3.1.1 栈(Stack)
        • 3.1.2 队列(Queue)
        • 3.1.3 二叉树(Binary Tree)
        • 3.1.4 链表(Linked List)
        • 3.1.5 堆(Heap)
        • 3.1.6 散列表(Hash Table)
        • 3.1.7 图(Graph)
        • 3.1.8 树(Tree)
        • 3.1.9 集合(Set)
        • 3.1.10 哈希集合(HashSet)
        • 3.1.11 哈希映射(HashMap)
        • 3.1.12 图的遍历算法
        • 3.1.13 树的遍历算法
      • 3.2 常见算法
        • 3.2.1 冒泡排序
        • 3.2.2 插入排序
        • 3.2.3 选择排序
        • 3.2.4 快速排序
        • 3.2.5 归并排序
        • 3.2.6 顺序查找
        • 3.2.7 二分查找
        • 3.2.8 插值查找
        • 3.2.9 哈希查找
        • 3.2.10 二叉查找树
  • 二、程序阅读题
    • 1. 程序问答
      • 1.1 明确程序效果
        • 1.1.1 列举每个变量值
        • 1.1.2 画流程图
        • 1.1.3 不断简化
          • 1.1.3.1 去除固定值的得出
          • 1.1.3.2 合并部分程序
      • 1.2 逆向思考
    • 2. 程序填空
      • 2.1 一堆[]先分解
      • 2.2 已知值先转换

备注: 所有的星都表示易考指数。

一、选择题

1. 计算机历史

1.1 计算机历史人物

1.1.1 艾伦 • 图灵

成就:

  1. 提出了一种抽象的计算模型图灵机【★★★☆☆】
  2. 提出了关于机器思维的问题【★☆☆☆☆】
  3. 人工智能之父【★★★★☆】

影响:
设立图灵奖,专门奖励为计算机事业作出重要贡献的个人【★★★★★】

1.1.2 莫西利和艾克特

成就:
成功研制出世界上第一台同通用计算机【★★★☆☆】

1.1.3 冯 • 诺伊曼

成就:

  1. 起草了“存储程序通用电子计算机方案”(EDVAC)【★★☆☆☆】
  2. 提出了冯 • 诺伊曼结构【★☆☆☆☆】
  3. 提出了利用存储程序运行计算机【★☆☆☆☆】
  4. 提出了采用二进制编码代替十进制【★★☆☆☆】
  5. 计算机之父【★★★★★】

影响:
成就中2.3.4这三大思想至今仍然为电子计算机设计者所遵循

1.2 计算机领域奖项

1.2.1 图灵奖

目的:
奖励为计算机事业作出重要贡献的个人【★★☆☆☆】


特点:

  1. 计算机界的诺贝尔奖【★☆☆☆☆】
  2. 计算机界最负盛名、最崇高的奖项【★★★★☆】
1.2.2 诺贝尔物理学奖

目的:
奖励计算机领域的突出贡献者【★★☆☆☆】


获奖事例:

  • 晶体管的发明【★★☆☆☆】
  • 大容量存储硬盘的发明(巨磁电阻)【★☆☆☆☆】
  • 光纤的发明【★☆☆☆☆】
  • 集成电路的发明【★★☆☆☆】
1.2.3 约翰 • 冯 • 诺伊曼奖

目的:
奖励计算机科学家和技术上具有杰出成就的科学家【★★☆☆☆】

2. 计算机常识

2.1 摩尔定律

提出者:
戈登 • 摩尔【★☆☆☆☆】


提出时间:
1965年【★☆☆☆☆】


称为:
计算机第一定律【★★★☆☆】


定律内容:

  • 集成电路芯片上所集成的电路的数目,每隔 18 18 18 个月就翻一番;【★★★☆☆】
  • 微处理器的性能每隔 18 18 18 个月提高一倍,而价格下降一半;【★★★☆☆】
  • 1 1 1 美元所能买到的计算机的性能,每隔 18 18 18 个月翻两番。【★★☆☆☆】

2.2 量子计算机

研制者:
谷歌【★☆☆☆☆】


研制(成功)时间:
2019 2019 2019 10 10 10 月【★☆☆☆☆】


研制结果:
研制出名为 Sycamore 53 53 53 位量子芯片,芯片已成功实现“量子优越性”。【★★☆☆☆】

2.3 二进制“和”与“或”

在编程中, ∨ ∨ ∧ ∧ 分别代表逻辑或(OR)和逻辑与(AND)操作符。它们是用来在布尔表达式中进行逻辑运算的。
逻辑或操作符( ∨ ∨ )表示只要两个操作数中至少一个为 true,整个表达式的结果就为 true。例如,表达式 ( x ∨ y x ∨ y xy) 的结果在 x x x y y y 中至少有一个为 true 时为 true,否则为 false
逻辑与操作符( ∧ ∧ )表示只有两个操作数都为 true,整个表达式的结果才为 true。例如,表达式 ( x ∧ y x ∧ y xy) 的结果在 x x x y y y 都为 true 时为 true,否则为 false
这些逻辑运算符通常用于控制流程中的条件判断和布尔表达式的求值。在编程语言中,常见的逻辑运算符是 “||” 和 “&&”,它们分别对应逻辑或和逻辑与操作符。

2.4 面向对象

2.4.1 面向对象的概念

面向对象的概念是一种软件开发方法论,其核心思想是将系统中的数据和操作数据的方法封装在一起,通过对象之间的交互来实现程序的功能。【★★☆☆☆】面向对象的特点包括封装、继承和多态。【★★★★☆】

2.4.2 C++具有面向对象特性

C++是一种支持面向对象编程的编程语言,它在语言层面上提供了丰富的面向对象特性。下面是C++的一些面向对象特性:

  1. 封装:通过类定义来实现封装,类将数据和操作封装在一起,通过访问修饰符(publicprivateprotected)控制对类成员的访问权限。封装提供了数据的隐藏和保护,使得对象的使用者只能通过类的公共接口访问和操作对象的数据。
  2. 继承:C++支持单继承和多继承。继承允许一个类通过继承另一个类来获得其属性和方法,并且可以通过派生类对继承的成员进行覆盖或扩展。继承提供了代码的重用性,可以通过定义基类来实现通用的行为,然后通过派生类来扩展或定制特定的行为。
  3. 多态:C++通过虚函数和运行时动态类型识别(RTTI)实现多态性。多态允许不同类型的对象根据其具体类型调用相同的方法,实现方法的动态绑定。这提供了灵活性和可扩展性,可以处理具有不同行为的对象集合,并且可以在运行时根据对象的实际类型来决定调用哪个方法。
2.4.3 面向对象的程序设计语言

• 简介
除了C++,还有一些专门面向对象的编程语言,如JavaC#Csharp)等。这些语言在语法和语义上更加贴近面向对象的概念,提供了更丰富的面向对象特性。比如,JavaC#都支持自动垃圾回收机制,简化了内存管理的工作;它们还提供接口(interface)机制,允许定义规范和多继承等。

• 优点
面向对象的编程模型具有很多优点,例如可重用性、可扩展性和可维护性等。通过面向对象的方法,可以将系统分解为若干个相对独立的对象,每个对象都有自己的状态和行为,从而简化了系统的设计和实现过程。同时,面向对象的编程模型也使得代码更加易于理解和维护,有助于提高开发效率和软件质量。

3. C++常识

3.1 数据结构

3.1.1 栈(Stack)

栈是一种具有后进先出(LIFO)特性的数据结构。实现了两个基本操作:压栈(将元素放入栈顶)和出栈(将栈顶元素取出)。

在C++中,可以使用std::stack作为栈的实现。

3.1.2 队列(Queue)

队列是一种具有先进先出(FIFO)特性的数据结构。实现了两个基本操作:入队(将元素放入队尾)和出队(将队头元素取出)。

在C++中,可以使用std::queue作为队列的实现。

3.1.3 二叉树(Binary Tree)

二叉树是一种每个节点最多有两个子节点的树状结构。其中一个子节点称为左子节点,另一个称为右子节点。

可以使用指针或者数组等方式来实现二叉树。常见的二叉树操作包括插入节点、删除节点、查找节点等。

3.1.4 链表(Linked List)

链表是由节点按顺序连接而成的线性数据结构。每个节点包含一个数据元素和一个指向下一个节点的指针。

在C++中,可以使用指针或者标准库中的std::list来实现链表。

3.1.5 堆(Heap)

堆是一种特殊的树形结构,常用于实现优先级队列。堆分为最大堆和最小堆两种。

C++标准库中的std::priority_queue提供了堆的实现,可以根据自定义的比较函数来实现最大堆或最小堆。

3.1.6 散列表(Hash Table)

散列表是根据关键字直接访问存储位置的数据结构,实现了键值对的映射。散列表通过哈希函数将关键字映射到存储位置。

C++标准库中的std::unordered_mapstd::unordered_set提供了散列表的实现。

3.1.7 图(Graph)

图是由节点和边构成的网络结构。图可以分为有向图和无向图,可以使用邻接矩阵或邻接表来表示。

常见的图算法包括深度优先搜索(DFS)、广度优先搜索(BFS)、最短路径算法等。

3.1.8 树(Tree)

树是一种非线性的数据结构,以分层的方式存储数据。树的每个节点可以有多个子节点。

常见的树结构包括二叉树、二叉搜索树、平衡二叉树、红黑树等。

3.1.9 集合(Set)

集合是一种无序且元素不重复的数据结构。集合中的元素没有顺序,可以进行插入、删除和查找操作。

C++标准库中的std::setstd::unordered_set提供了集合的实现。

3.1.10 哈希集合(HashSet)

哈希集合是基于散列表实现的集合数据结构。哈希集合利用哈希函数将元素存储在散列表中。

C++标准库中的std::unordered_set提供了哈希集合的实现。

3.1.11 哈希映射(HashMap)

哈希映射是基于散列表实现的键值对映射数据结构。哈希映射利用哈希函数将键映射到散列表中的存储位置。

C++标准库中的std::unordered_map提供了哈希映射的实现。

3.1.12 图的遍历算法

图的遍历算法用于访问图中的所有节点。常见的图的遍历算法包括深度优先搜索(DFS)和广度优先搜索(BFS)。

DFS从起始节点开始,沿着一条路径访问图的节点,直到无法继续前进,然后回退到上一个节点继续探索其他路径。

BFS从起始节点开始,先访问起始节点的所有邻接节点,然后逐层访问其他节点,直到访问完所有节点。

3.1.13 树的遍历算法

树的遍历算法用于访问树中的所有节点。常见的树的遍历算法包括前序遍历、中序遍历和后序遍历。

前序遍历先访问根节点,然后按照左子树、右子树的顺序遍历节点。

中序遍历按照左子树、根节点、右子树的顺序遍历节点。

后序遍历按照左子树、右子树、根节点的顺序遍历节点。

3.2 常见算法

3.2.1 冒泡排序
void bubbleSort(int arr[], int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        for (int j = 0; j < n - i - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.2 插入排序
void insertionSort(int arr[], int n)
{
    for (int i = 1; i < n; i++)
    {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key)
        {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.3 选择排序
void selectionSort(int arr[], int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int minIndex = i;
        for (int j = i + 1; j < n; j++)
        {
            if (arr[j] < arr[minIndex])
            {
                minIndex = j;
            }
        }
        swap(arr[i], arr[minIndex]);
    }
}

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.4 快速排序
int partition(int arr[], int low, int high)
{
    int pivot = arr[high];
    int i = low - 1;
    for (int j = low; j < high; j++)
    {
        if (arr[j] < pivot)
        {
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return (i + 1);
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)(平均情况), O ( n 2 ) O(n^2) O(n2)(最坏情况)
空间复杂度: O ( l o g n ) O(logn) O(logn)(平均情况), O ( n ) O(n) O(n)(最坏情况)

3.2.5 归并排序
void merge(int arr[], int left, int mid, int right)
{
    int n1 = mid - left + 1;
    int n2 = right - mid;
    int L[n1], R[n2];
    for (int i = 0; i < n1; i++)
    {
        L[i] = arr[left + i];
    }
    for (int j = 0; j < n2; j++)
    {
        R[j] = arr[mid + 1 + j];
    }
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2)
    {
        if (L[i] <= R[j])
        {
            arr[k] = L[i];
            i++;
        }
        else
        {
            arr[k] = R[j];
            j++;
        }
        k++;
    }
    while (i < n1)
    {
        arr[k] = L[i];
        i++;
        k++;
    }
    while (j < n2)
    {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int arr[], int left, int right)
{
    if (left < right)
    {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度: O ( n ) O(n) O(n)

3.2.6 顺序查找
int sequentialSearch(int arr[], int n, int target)
{
    for (int i = 0; i < n; i++)
    {
        if (arr[i] == target)
        {
            return i;
        }
    }
    return -1;
}

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.7 二分查找
int binarySearch(int arr[], int n, int target)
{
    int left = 0, right = n - 1;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target)
        {
            return mid;
        }
        else if (arr[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
    return -1;
}

时间复杂度: O ( l o g n ) O(logn) O(logn)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.8 插值查找
int interpolationSearch(int arr[], int n, int target)
{
    int left = 0, right = n - 1;
    while (left <= right && target >= arr[left] && target <= arr[right])
    {
        int pos = left + (target - arr[left]) * (right - left) / (arr[right] - arr[left]);
        if (arr[pos] == target)
        {
            return pos;
        }
        else if (arr[pos] < target)
        {
            left = pos + 1;
        }
        else
        {
            right = pos - 1;
        }
    }
    return -1;
}

时间复杂度:平均情况下 O ( l o g l o g n ) O(loglogn) O(loglogn),最坏情况下 O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

3.2.9 哈希查找
int hashSearch(int arr[], int n, int target)
{
    unordered_map<int, int> hashTable;
    for (int i = 0; i < n; i++)
    {
        hashTable[arr[i]] = i;
    }
    if (hashTable.find(target) != hashTable.end())
    {
        return hashTable[target];
    }
    return -1;
}

时间复杂度:平均情况下 O ( 1 ) O(1) O(1),最坏情况下 O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

3.2.10 二叉查找树
struct TreeNode
{
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

TreeNode* insert(TreeNode* root, int val)
{
    if (root == NULL)
    {
        return new TreeNode(val);
    }
    if (val < root->val)
    {
        root->left = insert(root->left, val);
    }
    else if (val > root->val)
    {
        root->right = insert(root->right, val);
    }
    return root;
}

bool search(TreeNode* root, int val)
{
    if (root == NULL)
    {
        return false;
    }
    if (root->val == val)
    {
        return true;
    }
    else if (val < root->val)
    {
        return search(root->left, val);
    }
    else
    {
        return search(root->right, val);
    }
}

时间复杂度:平均情况下 O ( l o g n ) O(logn) O(logn),最坏情况下 O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

二、程序阅读题

1. 程序问答

1.1 明确程序效果

1.1.1 列举每个变量值

例如 2020 2020 2020CSP-J 真题:

#include
#include
using namespace std;

char encoder[26]={'C','S','P',0};
char decoder[26];

string st;

int main(){
    int k=0;
    for(int i=0;i<26;++i)
        if (encoder[i]!=0)++k;
    for(char x='A';x<='Z';++x){
        bool flag=true;
        for(int i=0;i<26;++i)
            if(encoder[i]==x){
                flag=false;
                break;
            }
            if(flag){
            encoder[k]=x;
            ++k;
            }
    }
    for(int i=0;i<26;++i)
        decoder[encoder[i]-'A']=i+'A';
    cin>>st;
    for(int i=0;i<st.length();++i)
        st[i]=decoder[st[i]-'A'];
    cout<<st;
    return 0;
}

问题: 如果输入UTP, 则程序会输出什么?
通过表格列举所有变量、数组中改动的值这个方式:

循环变量的值 k flag encoder[k] decoder st
0 1 0 ‘\0’ / /
1 2 0 ‘\0’ / /
2 3 0 ‘\0’ / /
‘A’, 0 4 true ‘A’ / /
‘B’, 0 5 true ‘A’ / /
‘C’, 0 6 true ‘A’ / /
‘Z’, 0 28 true ‘A’ / /
‘A’, 1 29 false ‘C’ / /
‘B’, 1 29 true ‘C’ / /
‘C’, 1 29 true ‘C’ / /
‘Z’, 1 29 true ‘C’ / /
‘A’, 2 29 true ‘C’ / /
‘B’, 2 29 true ‘C’ / /
‘C’, 2 29 false ‘C’ / /
‘Z’, 2 29 true ‘C’ / /
‘U’ /
‘T’ /
‘P’ /

程序将输出:CSP。这个程序看似复杂,但是经过列举之后,我们就能发现它还是很容易的。

1.1.2 画流程图
Created with Raphaël 2.3.0 开始 k = 0 i = 0 encoder[i] ≠ 0 j = 0 encoder[j] = x encoder[k] = x k++ i < 26 decoder[encoder[i] - 'A'] = i + 'A' 读取输入字符串 解码 输出解码后的字符串 结束 yes no yes no yes no
1.1.3 不断简化
1.1.3.1 去除固定值的得出
#include 
using namespace std;

char encoder[26] = {'C','S','P',0};
char decoder[26];

string st;

int main() {
    int k = 3; // k一定是3
    for (char x = 'A'; x <= 'Z'; ++x) {
        bool flag = true;
        for (int i = 0; i < k; ++i) {
            if (encoder[i] == x) {
                flag = false;
                break;
            }
        }
        if (flag) {
            encoder[k] = x;
            ++k;
        }
    }
    for (int i = 0; i < 26; ++i) {
        decoder[encoder[i] - 'A'] = i + 'A';
    }
    cin >> st;
    for (int i = 0; i < st.length(); ++i) {
        st[i] = decoder[st[i] - 'A'];
    }
    cout << st;
    return 0;
1.1.3.2 合并部分程序
#include 
using namespace std;

string st;
char encoder[26] = {'C', 'S', 'P', 0};
char decoder[26];

int main() {
    int k = 3;
    for (char x = 'A'; x <= 'Z'; ++x) {
        bool flag = true;
        for (int i = 0; i < k; ++i) {
            if (encoder[i] == x) {
                flag = false;
                break;
            }
        }
        if (flag) encoder[k++] = x; // 合并程序,下标直接变成k自增
    }
    for (int i = 0; i < 26; ++i) decoder[encoder[i] - 'A'] = i + 'A';
    cin >> st;
    for (char &c : st) c = decoder[c - 'A'];
    cout << st;
    return 0;
}

1.2 逆向思考

如果输入TTT:

  • 编码器中的字符C对应解码器中的字符0。
  • 编码器中的字符S对应解码器中的字符1。
  • 编码器中的字符P对应解码器中的字符2。
  • 其他字符按顺序添加到编码器中,并且对应的索引值作为解码器中的字符。

因此,输入TTT的解码结果为022。

编码器将会是:

{'C', 'S', 'P', 0, 'A', 'B', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'Q', 'R', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}

解码器将会是:

{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}

如果输入是TTT,输出应该是AAA

2. 程序填空

2.1 一堆[]先分解

例如:

cnt[a[k]]++;

首先,先不看a[k]

cnt[***]++;

这样,我们就知道,程序的目的是将cnt[]中某一个元素自增。然后再看a[k]

index = a[k];
cnt[index]++;

这样就清楚许多了。

2.2 已知值先转换

例如:

int w[6] = {1, 1};
for (int n = 2; n <= 5; n++)
{
    w[n]=w[n-1]+w[n-2];
}

通过列举:

n w[n] w[n-1] w[n-2
2 0 1 1
2 2 1 1
3 0 2 1
3 3 2 1
4 0 3 2
4 5 3 2
5 0 5 3
5 8 5 3

因此, w[]数组应该为:

{1, 1, 2, 3, 5, 8}

你可能感兴趣的:(CSP-J新题型整理,C/C++,算法,数据结构)