CSP-J教程——第二阶段第十二、十三课:排序与查找算法

课程目标

  • 掌握选择排序和冒泡排序的原理与实现
  • 理解顺序查找和二分查找的算法思想
  • 建立算法效率的初步概念
  • 学会分析算法的时间复杂度
  • 理解二分查找的重要性和应用场景
  • 培养算法思维和问题解决能力

第一部分:算法概念与效率分析(40分钟)

1.1 什么是算法?

算法定义: 解决特定问题的明确步骤序列

生活比喻:

  • 食谱:做菜的步骤序列
  • 乐谱:演奏音乐的指令序列
  • 说明书:组装家具的步骤指南

1.2 算法效率的重要性

为什么要关心效率?

cpp 复制代码
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;

// 低效的方法:重复计算
int inefficientSum(int n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
}

// 高效的方法:数学公式
int efficientSum(int n) {
    return n * (n + 1) / 2;
}

int main() {
    int n = 1000000;
    
    // 测试低效方法
    auto start1 = high_resolution_clock::now();
    int result1 = inefficientSum(n);
    auto end1 = high_resolution_clock::now();
    auto duration1 = duration_cast<microseconds>(end1 - start1);
    
    // 测试高效方法
    auto start2 = high_resolution_clock::now();
    int result2 = efficientSum(n);
    auto end2 = high_resolution_clock::now();
    auto duration2 = duration_cast<microseconds>(end2 - start2);
    
    cout << "计算结果: " << result1 << " = " << result2 << endl;
    cout << "低效方法耗时: " << duration1.count() << " 微秒" << endl;
    cout << "高效方法耗时: " << duration2.count() << " 微秒" << endl;
    cout << "效率提升: " << (double)duration1.count() / duration2.count() << " 倍" << endl;
    
    return 0;
}

1.3 时间复杂度概念

大O表示法:描述算法运行时间的增长趋势

时间复杂度 名称 例子
O(1) 常数时间 数组访问、数学运算
O(log n) 对数时间 二分查找
O(n) 线性时间 顺序查找、遍历数组
O(n²) 平方时间 选择排序、冒泡排序

第二部分:选择排序(80分钟)

2.1 选择排序算法思想

算法思想:

每次从未排序部分选择最小(或最大)元素,放到已排序部分的末尾

比喻: 像整理扑克牌,每次找出最小的牌放到左边

2.2 选择排序步骤详解

步骤:

  1. 从第一个元素开始,假设它是最小值
  2. 遍历剩余元素,找到真正的最小值
  3. 将最小值与第一个元素交换
  4. 对剩余未排序部分重复上述过程
cpp 复制代码
#include <iostream>
using namespace std;

// 选择排序函数
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;
            }
        }
        
        // 将找到的最小元素与第i个元素交换
        if (minIndex != i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
        
        // 显示每一轮排序的结果
        cout << "第" << i + 1 << "轮: ";
        for (int k = 0; k < n; k++) {
            cout << arr[k] << " ";
        }
        cout << endl;
    }
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    cout << "原始数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl << endl;
    
    selectionSort(arr, n);
    
    cout << "\n排序后数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    return 0;
}

2.3 选择排序可视化演示

排序过程:

plain 复制代码
初始: [64, 25, 12, 22, 11]

第1轮: 找到最小值11,与64交换
→ [11, 25, 12, 22, 64]

第2轮: 在剩余部分找到最小值12,与25交换  
→ [11, 12, 25, 22, 64]

第3轮: 找到最小值22,与25交换
→ [11, 12, 22, 25, 64]

第4轮: 已经有序,不需要交换
→ [11, 12, 22, 25, 64]

2.4 选择排序特点分析

优点:

  • 简单直观,容易理解
  • 不占用额外内存(原地排序)
  • 交换次数较少(最多n-1次交换)

缺点:

  • 时间复杂度总是O(n²)
  • 不稳定排序(可能改变相同元素的相对位置)

时间复杂度分析:

  • 比较次数:n(n-1)/2 = O(n²)
  • 交换次数:n-1 = O(n)

第三部分:冒泡排序(80分钟)

3.1 冒泡排序算法思想

算法思想:

重复遍历数组,比较相邻元素,如果顺序错误就交换,直到没有需要交换的元素

比喻: 像气泡在水中上浮,较大的元素慢慢"浮"到数组末尾

3.2 冒泡排序步骤详解

cpp 复制代码
#include <iostream>
using namespace std;

// 冒泡排序函数
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        // 标记本轮是否有交换
        bool swapped = false;
        
        // 最后i个元素已经有序,不需要比较
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换相邻元素
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        
        // 显示每一轮排序的结果
        cout << "第" << i + 1 << "轮: ";
        for (int k = 0; k < n; k++) {
            cout << arr[k] << " ";
        }
        cout << endl;
        
        // 如果本轮没有交换,说明已经有序,提前结束
        if (!swapped) {
            cout << "提前结束!数组已有序。" << endl;
            break;
        }
    }
}

int main() {
    int arr[] = {5, 1, 4, 2, 8};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    cout << "原始数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl << endl;
    
    bubbleSort(arr, n);
    
    cout << "\n排序后数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    return 0;
}

3.3 冒泡排序可视化演示

排序过程:

plain 复制代码
初始: [5, 1, 4, 2, 8]

第1轮:
5>1? 交换 → [1, 5, 4, 2, 8]
5>4? 交换 → [1, 4, 5, 2, 8]  
5>2? 交换 → [1, 4, 2, 5, 8]
5<8? 不交换 → [1, 4, 2, 5, 8]

第2轮:
1<4? 不交换 → [1, 4, 2, 5, 8]
4>2? 交换 → [1, 2, 4, 5, 8]
4<5? 不交换 → [1, 2, 4, 5, 8]

第3轮: 没有交换,提前结束

3.4 冒泡排序优化

cpp 复制代码
#include <iostream>
using namespace std;

// 优化的冒泡排序
void optimizedBubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        int lastSwapIndex = n - 1;  // 记录最后一次交换的位置
        
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
                lastSwapIndex = j;  // 更新最后交换位置
            }
        }
        
        cout << "第" << i + 1 << "轮: ";
        for (int k = 0; k < n; k++) {
            cout << arr[k] << " ";
        }
        cout << "(最后交换位置: " << lastSwapIndex << ")" << endl;
        
        // 下一轮只需要比较到lastSwapIndex
        if (!swapped) break;
    }
}

int main() {
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    cout << "原始数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl << endl;
    
    optimizedBubbleSort(arr, n);
    
    return 0;
}

3.5 冒泡排序特点分析

优点:

  • 实现简单,容易理解
  • 稳定排序(相同元素相对位置不变)
  • 原地排序,不占用额外空间

缺点:

  • 平均和最坏情况下时间复杂度为O(n²)
  • 交换次数较多

时间复杂度分析:

  • 最好情况(已有序):O(n)
  • 平均情况:O(n²)
  • 最坏情况(逆序):O(n²)

第四部分:顺序查找(40分钟)

4.1 顺序查找算法思想

算法思想:

从第一个元素开始,逐个比较,直到找到目标元素或遍历完所有元素

比喻: 像在书架上找书,从第一本开始一本本检查

4.2 顺序查找实现

cpp 复制代码
#include <iostream>
using namespace std;

// 顺序查找函数
int sequentialSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        cout << "检查位置 " << i << ": " << arr[i] << endl;
        if (arr[i] == target) {
            return i;  // 找到目标,返回索引
        }
    }
    return -1;  // 没有找到,返回-1
}

// 统计出现次数
int countOccurrences(int arr[], int n, int target) {
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            count++;
        }
    }
    return count;
}

int main() {
    int arr[] = {23, 45, 67, 12, 89, 45, 34, 56, 45, 78};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target;
    
    cout << "数组内容: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "请输入要查找的数字: ";
    cin >> target;
    
    cout << "\n=== 顺序查找过程 ===" << endl;
    int position = sequentialSearch(arr, n, target);
    
    if (position != -1) {
        cout << "\n找到了!数字 " << target << " 在位置: " << position << endl;
        
        // 统计出现次数
        int count = countOccurrences(arr, n, target);
        cout << "数字 " << target << " 在数组中出现了 " << count << " 次" << endl;
        
        // 找出所有出现位置
        cout << "所有出现位置: ";
        for (int i = 0; i < n; i++) {
            if (arr[i] == target) {
                cout << i << " ";
            }
        }
        cout << endl;
    } else {
        cout << "\n没有找到数字 " << target << endl;
    }
    
    return 0;
}

4.3 顺序查找特点分析

优点:

  • 实现简单,容易理解
  • 对数据没有要求(无序数据也可查找)
  • 可以找到所有出现位置

缺点:

  • 效率低,时间复杂度O(n)
  • 数据量大时查找速度慢

适用场景:

  • 数据量较小
  • 数据无序
  • 需要查找所有出现位置

第五部分:二分查找(重点,100分钟)

5.1 二分查找算法思想

算法思想:

在有序数组中,每次比较中间元素,根据比较结果排除一半的搜索范围

比喻: 猜数字游戏,每次猜中间数,根据提示排除一半可能性

5.2 二分查找前提条件

重要前提:

  • 数组必须是有序的(升序或降序)
  • 支持随机访问(数组结构)

5.3 二分查找实现(迭代版本)

cpp 复制代码
#include <iostream>
using namespace std;

// 二分查找函数(迭代版本)
int binarySearch(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
    int step = 0;
    
    while (left <= right) {
        step++;
        int mid = left + (right - left) / 2;  // 防止溢出
        
        cout << "第" << step << "步: ";
        cout << "left=" << left << ", right=" << right;
        cout << ", mid=" << mid << ", arr[mid]=" << arr[mid] << endl;
        
        if (arr[mid] == target) {
            cout << "找到了!位置: " << mid << endl;
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;  // 目标在右半部分
            cout << "  目标在右半部分,left = " << left << endl;
        } else {
            right = mid - 1;  // 目标在左半部分
            cout << "  目标在左半部分,right = " << right << endl;
        }
    }
    
    cout << "没有找到目标 " << target << endl;
    return -1;
}

int main() {
    int arr[] = {2, 5, 8, 12, 16, 23, 38, 45, 67, 89};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target;
    
    cout << "有序数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "请输入要查找的数字: ";
    cin >> target;
    
    cout << "\n=== 二分查找过程 ===" << endl;
    int result = binarySearch(arr, n, target);
    
    if (result != -1) {
        cout << "\n成功!在位置 " << result << " 找到 " << target << endl;
    } else {
        cout << "\n失败!没有找到 " << target << endl;
    }
    
    return 0;
}

5.4 二分查找实现(递归版本)

cpp 复制代码
#include <iostream>
using namespace std;

// 二分查找函数(递归版本)
int binarySearchRecursive(int arr[], int left, int right, int target, int depth) {
    // 缩进显示递归深度
    string indent(depth * 2, ' ');
    cout << indent << "递归深度 " << depth << ": ";
    cout << "left=" << left << ", right=" << right;
    
    if (left > right) {
        cout << " → 未找到" << endl;
        return -1;
    }
    
    int mid = left + (right - left) / 2;
    cout << ", mid=" << mid << ", arr[mid]=" << arr[mid] << endl;
    
    if (arr[mid] == target) {
        cout << indent << "找到目标!位置: " << mid << endl;
        return mid;
    } else if (arr[mid] < target) {
        cout << indent << "目标在右半部分" << endl;
        return binarySearchRecursive(arr, mid + 1, right, target, depth + 1);
    } else {
        cout << indent << "目标在左半部分" << endl;
        return binarySearchRecursive(arr, left, mid - 1, target, depth + 1);
    }
}

int main() {
    int arr[] = {2, 5, 8, 12, 16, 23, 38, 45, 67, 89};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target;
    
    cout << "有序数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "请输入要查找的数字: ";
    cin >> target;
    
    cout << "\n=== 递归二分查找过程 ===" << endl;
    int result = binarySearchRecursive(arr, 0, n - 1, target, 0);
    
    if (result != -1) {
        cout << "\n成功!在位置 " << result << " 找到 " << target << endl;
    } else {
        cout << "\n失败!没有找到 " << target << endl;
    }
    
    return 0;
}

5.5 二分查找可视化演示

查找过程示例(查找23):

plain 复制代码
数组: [2, 5, 8, 12, 16, 23, 38, 45, 67, 89]

第1步: left=0, right=9, mid=4, arr[mid]=16
  目标在右半部分,left=5

第2步: left=5, right=9, mid=7, arr[mid]=45  
  目标在左半部分,right=6

第3步: left=5, right=6, mid=5, arr[mid]=23
找到了!位置: 5

5.6 二分查找变种

cpp 复制代码
#include <iostream>
using namespace std;

// 查找第一个等于目标的位置
int findFirstOccurrence(int arr[], int n, int target) {
    int left = 0, right = n - 1;
    int result = -1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            result = mid;        // 记录位置
            right = mid - 1;     // 继续在左半部分查找
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return result;
}

// 查找最后一个等于目标的位置
int findLastOccurrence(int arr[], int n, int target) {
    int left = 0, right = n - 1;
    int result = -1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            result = mid;        // 记录位置
            left = mid + 1;      // 继续在右半部分查找
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return result;
}

int main() {
    int arr[] = {2, 5, 5, 5, 8, 12, 12, 16, 23, 23, 23};
    int n = sizeof(arr) / sizeof(arr[0]);
    int target;
    
    cout << "数组: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "请输入要查找的数字: ";
    cin >> target;
    
    int first = findFirstOccurrence(arr, n, target);
    int last = findLastOccurrence(arr, n, target);
    
    if (first != -1) {
        cout << "数字 " << target << " 第一次出现在位置: " << first << endl;
        cout << "数字 " << target << " 最后一次出现在位置: " << last << endl;
        cout << "总共出现次数: " << (last - first + 1) << endl;
    } else {
        cout << "没有找到数字 " << target << endl;
    }
    
    return 0;
}

5.7 二分查找特点分析

优点:

  • 效率极高,时间复杂度O(log n)
  • 数据量大时优势明显
  • 思想可以推广到其他问题

缺点:

  • 要求数据有序
  • 只适用于支持随机访问的数据结构

时间复杂度分析:

  • 每次将搜索范围减半
  • 最坏情况:O(log₂n)
  • 对于100万个元素,最多只需要20次比较!

第六部分:算法效率对比(40分钟)

6.1 排序算法对比

cpp 复制代码
#include <iostream>
#include <chrono>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace std::chrono;

// 生成随机数组
void generateRandomArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        arr[i] = rand() % 1000;
    }
}

// 复制数组
void copyArray(int source[], int dest[], int n) {
    for (int i = 0; i < n; i++) {
        dest[i] = source[i];
    }
}

// 测试选择排序性能
void testSelectionSort(int arr[], int n) {
    int* temp = new int[n];
    copyArray(arr, temp, n);
    
    auto start = high_resolution_clock::now();
    
    // 选择排序
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < n; j++) {
            if (temp[j] < temp[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            swap(temp[i], temp[minIndex]);
        }
    }
    
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(end - start);
    
    cout << "选择排序: " << duration.count() << " 微秒" << endl;
    delete[] temp;
}

// 测试冒泡排序性能
void testBubbleSort(int arr[], int n) {
    int* temp = new int[n];
    copyArray(arr, temp, n);
    
    auto start = high_resolution_clock::now();
    
    // 冒泡排序
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (temp[j] > temp[j + 1]) {
                swap(temp[j], temp[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
    
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(end - start);
    
    cout << "冒泡排序: " << duration.count() << " 微秒" << endl;
    delete[] temp;
}

int main() {
    srand(time(0));
    
    int sizes[] = {100, 500, 1000};
    
    for (int size : sizes) {
        int* arr = new int[size];
        generateRandomArray(arr, size);
        
        cout << "\n=== 数组大小: " << size << " ===" << endl;
        testSelectionSort(arr, size);
        testBubbleSort(arr, size);
        
        delete[] arr;
    }
    
    return 0;
}

6.2 查找算法对比

cpp 复制代码
#include <iostream>
#include <chrono>
#include <algorithm>
using namespace std;
using namespace std::chrono;

// 测试顺序查找性能
void testSequentialSearch(int arr[], int n, int target) {
    auto start = high_resolution_clock::now();
    
    bool found = false;
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            found = true;
            break;
        }
    }
    
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(end - start);
    
    cout << "顺序查找: " << duration.count() << " 微秒" << endl;
}

// 测试二分查找性能
void testBinarySearch(int arr[], int n, int target) {
    auto start = high_resolution_clock::now();
    
    int left = 0, right = n - 1;
    bool found = false;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) {
            found = true;
            break;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    auto end = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(end - start);
    
    cout << "二分查找: " << duration.count() << " 微秒" << endl;
}

int main() {
    int sizes[] = {1000, 10000, 100000};
    
    for (int size : sizes) {
        int* arr = new int[size];
        
        // 生成有序数组
        for (int i = 0; i < size; i++) {
            arr[i] = i * 2;  // 偶数序列
        }
        
        int target = size - 1;  // 查找最后一个元素
        
        cout << "\n=== 数组大小: " << size << " ===" << endl;
        testSequentialSearch(arr, size, target);
        testBinarySearch(arr, size, target);
        
        delete[] arr;
    }
    
    return 0;
}

第七部分:综合应用(60分钟)

7.1 学生成绩管理系统(排序+查找)

cpp 复制代码
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

const int MAX_STUDENTS = 100;

struct Student {
    int id;
    string name;
    int score;
};

// 显示学生列表
void displayStudents(Student students[], int count) {
    cout << left << setw(8) << "学号" << setw(15) << "姓名" << setw(8) << "成绩" << endl;
    cout << string(35, '-') << endl;
    for (int i = 0; i < count; i++) {
        cout << setw(8) << students[i].id 
             << setw(15) << students[i].name 
             << setw(8) << students[i].score << endl;
    }
}

// 按成绩降序排序(选择排序)
void sortByScore(Student students[], int count) {
    for (int i = 0; i < count - 1; i++) {
        int maxIndex = i;
        for (int j = i + 1; j < count; j++) {
            if (students[j].score > students[maxIndex].score) {
                maxIndex = j;
            }
        }
        if (maxIndex != i) {
            swap(students[i], students[maxIndex]);
        }
    }
}

// 按学号排序(冒泡排序)
void sortById(Student students[], int count) {
    for (int i = 0; i < count - 1; i++) {
        bool swapped = false;
        for (int j = 0; j < count - i - 1; j++) {
            if (students[j].id > students[j + 1].id) {
                swap(students[j], students[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

// 按学号二分查找
int binarySearchById(Student students[], int count, int targetId) {
    int left = 0, right = count - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (students[mid].id == targetId) {
            return mid;
        } else if (students[mid].id < targetId) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

// 按姓名顺序查找
int sequentialSearchByName(Student students[], int count, string targetName) {
    for (int i = 0; i < count; i++) {
        if (students[i].name == targetName) {
            return i;
        }
    }
    return -1;
}

int main() {
    Student students[MAX_STUDENTS];
    int studentCount = 0;
    int choice;
    
    // 添加示例数据
    students[studentCount++] = {1001, "张三", 85};
    students[studentCount++] = {1003, "李四", 92};
    students[studentCount++] = {1002, "王五", 78};
    students[studentCount++] = {1005, "赵六", 96};
    students[studentCount++] = {1004, "钱七", 88};
    
    do {
        cout << "\n=== 学生成绩管理系统 ===" << endl;
        cout << "1. 显示所有学生" << endl;
        cout << "2. 按成绩排序" << endl;
        cout << "3. 按学号排序" << endl;
        cout << "4. 按学号查找" << endl;
        cout << "5. 按姓名查找" << endl;
        cout << "0. 退出" << endl;
        cout << "请选择操作: ";
        cin >> choice;
        
        switch (choice) {
            case 1:
                displayStudents(students, studentCount);
                break;
                
            case 2:
                sortByScore(students, studentCount);
                cout << "按成绩排序完成!" << endl;
                displayStudents(students, studentCount);
                break;
                
            case 3:
                sortById(students, studentCount);
                cout << "按学号排序完成!" << endl;
                displayStudents(students, studentCount);
                break;
                
            case 4: {
                int targetId;
                cout << "请输入要查找的学号: ";
                cin >> targetId;
                
                // 先按学号排序
                sortById(students, studentCount);
                int index = binarySearchById(students, studentCount, targetId);
                
                if (index != -1) {
                    cout << "\n找到学生:" << endl;
                    cout << "学号: " << students[index].id << endl;
                    cout << "姓名: " << students[index].name << endl;
                    cout << "成绩: " << students[index].score << endl;
                } else {
                    cout << "没有找到学号为 " << targetId << " 的学生" << endl;
                }
                break;
            }
                
            case 5: {
                string targetName;
                cout << "请输入要查找的姓名: ";
                cin >> targetName;
                
                int index = sequentialSearchByName(students, studentCount, targetName);
                
                if (index != -1) {
                    cout << "\n找到学生:" << endl;
                    cout << "学号: " << students[index].id << endl;
                    cout << "姓名: " << students[index].name << endl;
                    cout << "成绩: " << students[index].score << endl;
                } else {
                    cout << "没有找到姓名为 " << targetName << " 的学生" << endl;
                }
                break;
            }
                
            case 0:
                cout << "感谢使用!" << endl;
                break;
                
            default:
                cout << "无效选择!" << endl;
        }
        
    } while (choice != 0);
    
    return 0;
}

7.2 猜数字游戏(二分查找思想)

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

// 人类猜数字(计算机出题)
void humanGuess() {
    srand(time(0));
    int secret = rand() % 100 + 1;
    int guess, attempts = 0;
    
    cout << "我想了一个1-100的数字,猜猜看!" << endl;
    
    do {
        attempts++;
        cout << "第" << attempts << "次猜测: ";
        cin >> guess;
        
        if (guess < secret) {
            cout << "太小了!" << endl;
        } else if (guess > secret) {
            cout << "太大了!" << endl;
        } else {
            cout << "恭喜!你在第" << attempts << "次猜中了!" << endl;
        }
    } while (guess != secret);
}

// 计算机猜数字(人类出题)
void computerGuess() {
    int low = 1, high = 100;
    int guess, attempts = 0;
    char response;
    
    cout << "请想一个1-100的数字,我会猜它!" << endl;
    cout << "我猜完后,请输入: " << endl;
    cout << "'s'表示太小,'b'表示太大,'c'表示正确" << endl;
    
    do {
        attempts++;
        guess = low + (high - low) / 2;  // 二分查找思想
        cout << "第" << attempts << "次猜测: " << guess << " → ";
        cin >> response;
        
        if (response == 's') {
            low = guess + 1;
            cout << "知道了,比" << guess << "大" << endl;
        } else if (response == 'b') {
            high = guess - 1;
            cout << "知道了,比" << guess << "小" << endl;
        } else if (response == 'c') {
            cout << "太好了!我在第" << attempts << "次猜中了!" << endl;
        } else {
            cout << "请输入 s/b/c!" << endl;
            attempts--;  // 无效输入不计次数
        }
    } while (response != 'c');
}

int main() {
    int choice;
    
    cout << "=== 猜数字游戏 ===" << endl;
    cout << "1. 人类猜数字" << endl;
    cout << "2. 计算机猜数字" << endl;
    cout << "请选择模式: ";
    cin >> choice;
    
    if (choice == 1) {
        humanGuess();
    } else if (choice == 2) {
        computerGuess();
    } else {
        cout << "无效选择!" << endl;
    }
    
    return 0;
}

练习与作业

基础练习(必做)

练习1:排序算法实现

  1. 实现选择排序,使其可以按降序排列
  2. 实现冒泡排序,添加优化(提前终止)
  3. 比较两种排序在相同数据上的性能

练习2:查找算法应用

  1. 在有序数组中实现二分查找
  2. 统计某个数字在有序数组中出现的次数
  3. 实现查找第一个和最后一个出现位置的功能

练习3:综合应用

创建一个图书管理系统:

  • 按书名排序(冒泡排序)
  • 按价格排序(选择排序)
  • 按ISBN查找(二分查找)
  • 按作者查找(顺序查找)

挑战练习(选做)

挑战1:排序算法可视化

创建一个图形化界面(使用字符图形),实时显示排序过程:

  • 用不同字符表示数组元素
  • 每步排序后显示数组状态
  • 比较不同排序算法的可视化效果

挑战2:三路快速排序

研究并实现三路快速排序的简单版本:

  • 将数组分为小于、等于、大于基准的三部分
  • 递归处理小于和大于部分
  • 分析其性能特点

挑战3:插值查找

实现插值查找算法:

  • 在有序数组中根据数值分布进行预测
  • 比较其与二分查找的性能
  • 分析适用场景

实验任务

任务1:算法性能测试

对不同规模的数组测试排序算法性能:

cpp 复制代码
// 测试数据规模:100, 500, 1000, 5000
// 记录每种算法在不同规模下的运行时间
// 绘制简单的性能曲线

任务2:边界条件测试

测试算法在各种边界情况下的表现:

  • 空数组
  • 单元素数组
  • 已排序数组
  • 逆序数组
  • 所有元素相同的数组

任务3:算法正确性验证

编写测试函数验证排序算法的正确性:

cpp 复制代码
bool isSorted(int arr[], int n) {
    // 检查数组是否已排序
}

void testSortAlgorithm() {
    // 生成测试数据
    // 调用排序算法
    // 验证排序结果
}

学习总结

今天学到了:

  • 选择排序:每次选择最小元素,时间复杂度O(n²)
  • 冒泡排序:相邻元素比较交换,时间复杂度O(n²)
  • 顺序查找:逐个比较查找,时间复杂度O(n)
  • 二分查找:对半缩小范围,时间复杂度O(log n)
  • 算法效率:时间复杂度的概念和重要性
  • 算法思维:分治思想在二分查找中的应用

关键突破:

  • 效率意识:认识到不同算法的性能差异
  • 分治思想:理解二分查找的"分而治之"策略
  • 算法选择:根据问题特点选择合适的算法
  • 优化思维:学会分析并改进算法性能

下一课预告:

下一节课我们将学习高精度运算,解决C++基本数据类型无法处理的大数运算问题,锻炼对数组的精细操作能力!


相关推荐
chao1898442 小时前
MATLAB与HFSS联合仿真
算法
fei_sun2 小时前
【总结】【OS】成组链接法
jvm·数据结构
月明长歌2 小时前
【码道初阶】牛客TSINGK110:二叉树遍历(较难)如何根据“扩展先序遍历”构建二叉树?
java·数据结构·算法
jqrbcts2 小时前
关于发那科机器人视觉补偿报警设置
人工智能·算法
_Li.2 小时前
机器学习-线性判别函数
人工智能·算法·机器学习
蒲小英2 小时前
算法-栈与队列
算法
代码游侠2 小时前
学习笔记——IPC(进程间通信)
linux·运维·网络·笔记·学习·算法
保持低旋律节奏2 小时前
数据结构——链表自实现
数据结构·链表
Nick_zcy2 小时前
基于Vue和Python的羽毛球拍智能推荐系统, 从“不会选羽毛球拍”到“选对拍”的一站式小工具
前端·vue.js·python·算法·推荐算法