# C 语言与程序设计
# C 语言特点
1. 过程式语言:C语言是一种过程式编程语言,重点在于算法和逻辑的实现,以过程和函数为主要组织方式。
2. 简洁高效:C语言设计简洁,执行高效,适合系统编程和底层开发。
3. 指针:C语言提供了指针的概念,允许直接访问和操作内存地址,这在系统编程中很有用。
4. 低级别控制:C语言提供了对计算机底层的直接控制,包括对硬件的访问、内存管理等。
5. 面向过程:C语言是一种面向过程的语言,它将程序分解成一系列函数或过程,通过调用这些函数来完成任务。
- C++ 语言特点:
- 多范式编程:C++ 是一种多范式编程语言,支持面向对象编程、泛型编程和过程式编程。
- 面向对象:C++ 支持面向对象编程,提供类、继承、多态等面向对象的特性,使得代码更具可扩展性和可维护性。
- 标准模板库(STL):C++ 标准库提供了丰富的模板类和函数,称为 STL,包括容器、算法和迭代器等,可以大大提高开发效率。
- 异常处理:C++ 支持异常处理机制,允许在程序执行过程中抛出和捕获异常,提高了程序的健壮性。
- 运算符重载:C++ 允许运算符重载,使得用户自定义类型可以像内置类型一样使用运算符。
- 扩展性:C++ 提供了丰富的语言特性和库支持,可以进行底层编程,也可以用于开发大型、复杂的软件系统。
- 虽然 C 语言和 C 有许多共同点,但 C 作为 C 语言的超集,在功能上更加丰富,提供了更多的特性和工具,因此在实际应用中,开发者可以根据项目需求选择使用合适的语言。
# 计算机的解题过程
- 分析问题
- 在解决问题之前,需要完全理解问题的要求和约束条件。这可能需要对问题进行分析和拆解,以确保对问题的理解准确无误。
- 设计算法
- 一旦理解了问题,就需要设计一个算法来解决它。算法是一系列明确定义的步骤,用于解决特定问题或执行特定任务。在设计算法时,需要考虑效率、正确性和可维护性等因素。
- 编写程序
- 根据设计好的算法,编写相应的代码实现。编写的代码应该准确地反映算法的逻辑,并考虑到输入数据的边界情况和异常情况。
- 运行验证
- 编写完代码后,需要进行调试和测试以确保代码的正确性。这可能涉及在不同的输入数据上运行代码,并检查输出是否符合预期。如果发现错误,就需要进行调试,并修复代码中的问题。
- 优化和改进
- 一旦代码能够正确地解决问题,就可以考虑对代码进行优化和改进,以提高性能、减少资源消耗或使代码更易于理解和维护。优化可能涉及改进算法、优化数据结构或利用并行计算等技术。
- 文档和维护
- 最后,还需要编写文档来说明代码的功能、用法和限制。这有助于其他人理解和使用代码。此外,需要定期维护代码,以应对新的需求或发现的问题。
- PS: 这些步骤通常是迭代进行的,可能需要多次修改和调整代码,直到达到满意的解决方案。
# 算法的概念
- 算法是指解决问题或执行任务的一系列有序步骤。它是一种计算机程序的设计方法,通过确定性的、明确定义的步骤,将输入数据转换为输出结果。
# 算法的特性
- 有序性:算法由一系列明确的步骤组成,每个步骤都有特定的执行顺序。
- 确定性:算法对于给定的输入,在相同的条件下总是产生相同的输出,不受随机因素的影响。
- 可行性:算法是可行的,可以在有限的时间内完成执行。
- 输入输出:算法接受输入数据,并产生输出结果。
- 解决问题:算法用于解决特定的问题或执行特定的任务。
# 算法的表示
算法可以用多种方式表示,以便于理解、实现和交流。以下是几种常见的表示方法:
- 自然语言的描述
- 使用自然语言,如英语或中文,对算法进行文字描述。这种表示方法简单直观,适合于初步了解算法的工作原理。例如:
步骤1:从输入数组中找到最小元素。
步骤2:将最小元素放置在数组的第一个位置。
步骤3:在剩余的元素中继续重复步骤1和步骤2,直到整个数组排序完成。
- 传统流程图(Flowchart)
- 流程图是一种图形化的表示方法,用图形符号表示算法的执行流程和控制结构。流程图通常包括流程框(表示操作或处理)、连接线(表示流程的顺序)、判断框(表示条件判断)等元素。例如:
Start -> Initialize variable i to 0
-> Repeat until i reaches the length of array A
-> Find the index of the minimum element in the unsorted part of A
-> Swap the minimum element with the first element in the unsorted part of A
-> Increment i
-> End
- N-S 流程图
- 伪代码(Pseudocode)
- 伪代码是一种介于自然语言和实际编程语言之间的表示方法,它结合了编程语言的结构和自然语言的表达方式,以简单的方式描述算法的逻辑流程和步骤。例如:
procedure SelectionSort(A: list of sortable items)
n = length(A)
for i = 0 to n-1min_index = i
for j = i+1 to nif A[j] < A[min_index]
min_index = j
swap A[i] with A[min_index]
- 编程语言代码
- 算法可以直接用编程语言代码表示,这种表示方法最为具体和精确,可以直接用于实现算法。不同的编程语言可以用不同的语法来表示同一个算法。例如,使用 c 和 c++ 表示选择排序算法:
#include <stdio.h>// 选择排序函数void selection_sort(int A[], int n) {
// 遍历数组for (int i = 0; i < n; i++) {
// 找到未排序部分的最小元素的索引int min_index = i;
for (int j = i + 1; j < n; j++) {
if (A[j] < A[min_index]) {
min_index = j;
}}// 将最小元素与当前位置交换int temp = A[i];
A[i] = A[min_index];
A[min_index] = temp;
}}int main() {
int A[] = {64, 25, 12, 22, 11};
int n = sizeof(A) / sizeof(A[0]);
// 调用选择排序函数selection_sort(A, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", A[i]);
}printf("\n");
return 0;
}#include <iostream>// 选择排序函数void selection_sort(int A[], int n) {
// 遍历数组for (int i = 0; i < n; i++) {
// 找到未排序部分的最小元素的索引int min_index = i;
for (int j = i + 1; j < n; j++) {
if (A[j] < A[min_index]) {
min_index = j;
}}// 将最小元素与当前位置交换std::swap(A[i], A[min_index]);
}}int main() {
int A[] = {64, 25, 12, 22, 11};
int n = sizeof(A) / sizeof(A[0]);
// 调用选择排序函数selection_sort(A, n);
std::cout << "Sorted array: ";
for (int i = 0; i < n; i++) {
std::cout << A[i] << " ";
}std::cout << std::endl;
return 0;
}- 选择合适的表示方法取决于解释算法的目的、目标受众以及所处的环境和情境。
# 常用的算法介绍
枚举法
递推法
递归法
回溯法
贪婪法 (贪心算法)
- 最小生成树(Minimum Spanning Tree,MST):从图中选择边的子集,使得这些边连接了所有节点,且总权重最小。
- 最短路径(Shortest Path):寻找两个节点之间的最短路径,以最小化路径的总权重。
动态规划法
- 斐波那契数列(Fibonacci Sequence):通过将问题分解为更小的子问题,并保存子问题的解,来避免重复计算,从而提高效率。
- 最长公共子序列(Longest Common Subsequence,LCS):寻找两个序列中的最长子序列,使得它在两个原始序列中同时出现,且不一定连续。
排序算法:
- 冒泡排序(Bubble Sort):通过多次遍历数组,依次比较相邻的元素,并交换它们,将最大(或最小)的元素逐步 “冒泡” 到数组的顶端。
- 选择排序(Selection Sort):每次遍历数组,选择未排序部分的最小元素,并将其放置到已排序部分的末尾。
- 插入排序(Insertion Sort):将数组分为已排序和未排序两部分,每次将未排序部分的第一个元素插入到已排序部分的适当位置 3
- 快速排序(Quick Sort):选择一个基准元素,将数组分为小于基准元素和大于基准元素的两部分,然后递归地对这两部分进行排序。
搜索算法:
- 线性搜索(Linear Search):逐个检查数组中的元素,直到找到目标元素或遍历完整个数组。
- 二分搜索(Binary Search):仅适用于有序数组,通过反复将目标值与数组中间元素比较,来确定目标值在数组中的位置。
图算法
- 深度优先搜索(Depth-First Search,DFS):遍历图的节点,沿着路径尽可能深入,直到无法再继续为止,然后回溯并继续探索其他路径。
- 广度优先搜索(Breadth-First Search,BFS):从起始节点开始,逐层遍历图的节点,先访问所有与起始节点相邻的节点,然后依次访问它们的相邻节点。
- PS: 这些算法只是众多常用算法的一部分,它们应用广泛,可以用于解决各种不同类型的问题。
# 算法示例
- 题目: 假设一万个数组相同的数,查找出来,要求时间复杂度和空间复杂度最优
- 分析思路
- 理想情况下,我们可以通过对这些数组进行一次遍历来找到所有相同的值。以下是一种可能的方法,时间复杂度为 O (N),其中 N 是所有数组中元素的总数,空间复杂度也是 O (N),因为我们需要一个哈希集合来存储已经遇到的元素。
- 算法步骤
- 遍历每个数组,并将数组中的每个元素添加到一个哈希集合中。
- 如果遇到的元素已经在哈希集合中存在,则将其添加到结果集中。
- 编写代码
- Java 版本:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FindDuplicates {
public static List<Integer> findDuplicates(List<int[]> arrays) {
// 创建一个哈希集合来存储已经遇到的元素Map<Integer, Integer> seen = new HashMap<>();
// 创建一个结果列表来存储重复的元素List<Integer> duplicates = new ArrayList<>();
// 遍历每个数组for (int[] array : arrays) {
// 遍历数组中的每个元素for (int item : array) {
// 如果元素已经在哈希集合中存在,则将其添加到结果列表中if (seen.containsKey(item)) {
// 只有当元素第二次出现时才添加到结果列表中,避免重复添加if (seen.get(item) == 1) {
duplicates.add(item);
}} else {
// 如果元素第一次出现,则将其添加到哈希集合中seen.put(item, 1);
}}}return duplicates;
}public static void main(String[] args) {
// 示例输入List<int[]> arrays = new ArrayList<>();
arrays.add(new int[]{1, 2, 3, 4});
arrays.add(new int[]{3, 4, 5, 6});
arrays.add(new int[]{5, 6, 7, 8});
arrays.add(new int[]{1, 5, 9, 10});
// 查找重复的元素List<Integer> duplicates = findDuplicates(arrays);
System.out.println("Duplicates: " + duplicates);
}} - C++ 版本:
#include <iostream>#include <vector>#include <unordered_map>using namespace std;
vector<int> findDuplicates(vector<vector<int>>& arrays) {
// 创建一个哈希表来存储已经遇到的元素unordered_map<int, int> seen;
// 创建一个结果向量来存储重复的元素vector<int> duplicates;
// 遍历每个数组for (auto& array : arrays) {
// 遍历数组中的每个元素for (int item : array) {
// 如果元素已经在哈希表中存在,则将其添加到结果向量中if (seen.find(item) != seen.end()) {
// 只有当元素第二次出现时才添加到结果向量中,避免重复添加if (seen[item] == 1) {
duplicates.push_back(item);
}} else {
// 如果元素第一次出现,则将其添加到哈希表中seen[item] = 1;
}}}return duplicates;
}int main() {
// 示例输入vector<vector<int>> arrays = {
{1, 2, 3, 4},
{3, 4, 5, 6},
{5, 6, 7, 8},
{1, 5, 9, 10}
};
// 查找重复的元素vector<int> duplicates = findDuplicates(arrays);
cout << "Duplicates: ";
for (int item : duplicates) {
cout << item << " ";
}cout << endl;
return 0;
} - 结构化程序的设计基本思想
- 采用自顶向下,逐步求精的程序设计
- 任何程序只使用顺序、选择和循环这三种基本控制结构
# C 语言基本概念
简单的 C 语言程序
- 简单输出一个 hello world 字符串,表明向全世界宣布 "C 语言我来了!"
关键字和标识符
- 字符集
- 关键字
- 标识符
数据类型
- 基本类型
- 整数类型
- 浮点类型
- 字符类型
- 枚举类型
- 构造类型
- 指针类型
- 空类型 (void)
- 基本类型
常量和变量
- 整数常量
- 浮点数常量
- 字符常量
- 字符串常量
#define PI 3.14159 |
变量
- 变量声明
- 格式: 数据类型 变量名列表
- 变量初始化
- 变量声明
运算符和表达式
- 算术运算符
- 自增自减运算符
- 算术运算符 (实际运用中少,考试出题较多)
- 赋值运算符
- 其他运算符
- 逗号运算符
- 条件运算符
- 求字节运算符 (sizeof ())
数据类型转换
- 隐式转换
- 自动类型类型转换
- 赋值类型转换
- 显式转换
- 强制类型转换
- 隐式转换
# 程序控制结构
- 顺序结构
- 赋值
- =
- 数据输出
- putchar() , printf()
- 数据输入
- getchar() , scanf()
- 赋值
- 选择结构
- 关系运算符、关系表达式
- 逻辑运算符、逻辑表达式
- if, else, else if, switch
- 选择嵌套
- 循环结构
- while
- do while
- for
- 循环嵌套
# 数组和字符串
一维数组
冒泡排序
选择排序
二维数组
多维数组
# 指针
- 指针是表示计算机内存地址的数据类型,
- 指针就是地址,指针变量就是存储地址的变量
# 函数
定义 :C 语言和 C++ 都使用函数来组织和重用代码。函数是一段可重用的代码块,它可以接受输入(参数)、执行特定的任务,并返回结果。下面是它们的基本格式:
返回类型 函数名(参数列表) {
// 函数体// 可以包含声明、语句等return 返回值; // 返回结果,如果函数有返回类型的话
}- 返回类型(Return Type): 函数执行完任务后返回的数据类型。如果函数不返回任何值,则返回类型为 void。
- 函数名(Function Name): 函数的标识符,用于调用函数。
- 参数列表(Parameter List): 可选项,用于传递数据给函数。参数列表中可以包含零个或多个参数。
- 函数体(Function Body): 函数的具体实现,包含了需要执行的代码。
- 返回值(Return Value): 如果函数有返回类型,则使用 return 语句返回结果。
返回类型 函数名(参数列表) {
// 函数体// 可以包含声明、语句等return 返回值; // 返回结果,如果函数有返回类型的话
}C 函数的格式与 C 语言相似,但 C 增加了一些特性,例如函数重载、默认参数、函数模板等。
在 C++ 中,你还可以定义类成员函数,其格式为:
class 类名 {
public:
返回类型 函数名(参数列表) {
// 函数体// 可以包含声明、语句等return 返回值; // 返回结果,如果函数有返回类型的话
}};
- 这样的函数属于类的一部分,称为成员函数