Skip to content

可运行版本 · C++

每个文件均可独立编译运行(g++ 文件名.cpp -o out && ./out),带 main 测试用例。

文件一览

序号内容文件
01线性表01_线性表.cpp
02栈与队列02_栈与队列.cpp
03二叉树03_二叉树.cpp
04排序算法04_排序算法.cpp
05串匹配与查找05_串匹配与查找.cpp
06图算法06_图算法.cpp

01 线性表

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— 线性表 (可运行 C++ 版)
 *   文件: 01_线性表.cpp
 *   编译: g++ 01_线性表.cpp -o test && ./test
 * ============================================================
 *
 *   覆盖内容:
 *     1. 顺序表: 初始化、插入、删除
 *     2. 单链表: 尾插法建表、链表逆置
 *     3. ★ 2010 真题: 循环左移 (三次逆置法)
 *     4. ★ 2011 真题: 两有序数组求中位数
 *     5. ★ 2013 真题: 摩尔投票法求主元素
 *
 *   C++ 特性: 使用引用 & 传参,简化指针操作
 * ============================================================
 */

#include <cstdio>
#include <cstdlib>

#define MaxSize 50
typedef int ElemType;


/* ========== 一、顺序表 ========== */

typedef struct {
    ElemType data[MaxSize];
    int length;
} SqList;

/* 初始化 */
void InitList(SqList &L) {
    L.length = 0;
}

/* 插入: 在第 i 个位置(1-based)插入元素 e */
bool ListInsert(SqList &L, int i, ElemType e) {
    if (i < 1 || i > L.length + 1 || L.length >= MaxSize)
        return false;

    for (int j = L.length; j >= i; j--)   // 后移腾位
        L.data[j] = L.data[j - 1];

    L.data[i - 1] = e;   // 位序 i 对应下标 i-1
    L.length++;
    return true;
}

/* 删除: 删除第 i 个位置的元素, 用 e 带回 */
bool ListDelete(SqList &L, int i, ElemType &e) {
    if (i < 1 || i > L.length)
        return false;

    e = L.data[i - 1];
    for (int j = i; j < L.length; j++)    // 前移填充
        L.data[j - 1] = L.data[j];

    L.length--;
    return true;
}

/* 打印顺序表 */
void PrintSqList(SqList L) {
    printf("[ ");
    for (int i = 0; i < L.length; i++)
        printf("%d ", L.data[i]);
    printf("]\n");
}


/* ========== 二、单链表 ========== */

typedef struct LNode {
    ElemType data;
    struct LNode *next;
} LNode, *LinkList;

/* 尾插法建表: 数据顺序与输入一致 */
LinkList CreateList_Tail(int arr[], int n) {
    LinkList L = (LinkList)malloc(sizeof(LNode));  // 创建头结点
    L->next = NULL;
    LNode *r = L;  // r 始终指向表尾

    for (int i = 0; i < n; i++) {
        LNode *s = (LNode *)malloc(sizeof(LNode));
        s->data = arr[i];
        r->next = s;   // 新结点接到表尾
        r = s;          // r 后移
    }
    r->next = NULL;     // 尾结点 next 置空
    return L;
}

/* 链表原地逆置: 头插法逆置, O(n)/O(1) */
void ReverseList(LinkList &L) {
    LNode *p = L->next;   // p: 工作指针
    LNode *r;              // r: 保存 p 的后继
    L->next = NULL;        // 头结点断开,变空表

    while (p != NULL) {
        r = p->next;       // 1. 保存后继
        p->next = L->next; // 2. 头插: p 指向当前第一个
        L->next = p;       // 3. 头结点指向 p
        p = r;             // 4. 前进到下一个
    }
}

/* 打印链表 */
void PrintList(LinkList L) {
    LNode *p = L->next;
    printf("Head->");
    while (p) {
        printf("%d->", p->data);
        p = p->next;
    }
    printf("NULL\n");
}


/* ========== 三、真题算法 ========== */

/*
 * ★ 2010 真题: 数组循环左移 p 位
 *   三次逆置法, 时间 O(n), 空间 O(1)
 */
void Reverse(int a[], int left, int right) {
    while (left < right) {
        int temp = a[left];
        a[left] = a[right];
        a[right] = temp;
        left++;
        right--;
    }
}

void LeftShift(int a[], int n, int p) {
    p %= n;                    // 防止 p >= n
    Reverse(a, 0, p - 1);     // 步骤1: 逆置前 p 个
    Reverse(a, p, n - 1);     // 步骤2: 逆置后 n-p 个
    Reverse(a, 0, n - 1);     // 步骤3: 整体逆置
}

/*
 * ★ 2011 真题: 两等长有序数组求中位数
 *   双指针归并计数, O(n)
 */
int FindMedian(int A[], int B[], int n) {
    int ia = 0, ib = 0;    // 两个数组的指针
    int last = 0;           // 记录每次选中的较小值

    for (int count = 0; count < n; count++) {
        if (ia < n && (ib >= n || A[ia] <= B[ib]))
            last = A[ia++];   // A 的当前元素较小(或 B 已用完)
        else
            last = B[ib++];   // B 的当前元素较小(或 A 已用完)
    }
    return last;  // 第 n 个就是中位数
}

/*
 * ★ 2013 真题: 摩尔投票法找主元素
 *   第一遍: 候选者计数, 归零换人
 *   第二遍: 验证候选者出现次数是否 > n/2
 */
int FindMainElement(int A[], int n) {
    int candidate = A[0];
    int count = 1;

    // 第一遍: 寻找候选者
    for (int i = 1; i < n; i++) {
        if (A[i] == candidate) {
            count++;
        } else {
            count--;
            if (count == 0) {
                candidate = A[i];
                count = 1;
            }
        }
    }

    // 第二遍: 验证
    count = 0;
    for (int i = 0; i < n; i++)
        if (A[i] == candidate) count++;

    return (count > n / 2) ? candidate : -1;
}


/* ========== main 演示 ========== */
int main() {
    printf("===== 线性表 C++ 可运行版 =====\n\n");

    /* --- 顺序表 --- */
    SqList L;
    InitList(L);
    ListInsert(L, 1, 10);
    ListInsert(L, 2, 20);
    ListInsert(L, 3, 30);
    printf("顺序表: ");
    PrintSqList(L);

    /* --- 单链表 --- */
    int d[] = {3, 1, 4, 1, 5};
    LinkList LL = CreateList_Tail(d, 5);
    printf("链表:   ");
    PrintList(LL);
    ReverseList(LL);
    printf("逆置:   ");
    PrintList(LL);

    /* --- 2010: 循环左移 --- */
    printf("\n--- 2010 真题 ---\n");
    int arr[] = {0, 1, 2, 3, 4, 5, 6, 7};
    LeftShift(arr, 8, 3);
    printf("循环左移3: ");
    for (int i = 0; i < 8; i++) printf("%d ", arr[i]);
    printf("\n");

    /* --- 2011: 中位数 --- */
    printf("\n--- 2011 真题 ---\n");
    int A[] = {1, 3, 5, 7, 9};
    int B[] = {2, 4, 6, 8, 10};
    printf("中位数: %d\n", FindMedian(A, B, 5));

    /* --- 2013: 主元素 --- */
    printf("\n--- 2013 真题 ---\n");
    int C[] = {0, 5, 5, 3, 5, 1, 5, 5};
    printf("主元素: %d\n", FindMainElement(C, 8));

    return 0;
}

02 栈与队列

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— 栈与队列 (可运行 C++ 版)
 *   文件: 02_栈与队列.cpp
 *   编译: g++ 02_栈与队列.cpp -o test && ./test
 * ============================================================
 *
 *   覆盖内容:
 *     1. 顺序栈: 初始化/进栈/出栈/判空
 *     2. 循环队列 (牺牲一个单元法)
 *     3. ★ 括号匹配算法
 *     4. ★ 后缀表达式求值
 * ============================================================
 */

#include <cstdio>

#define MaxSize 50
typedef int ElemType;


/* ========== 一、顺序栈 ========== */

typedef struct {
    ElemType data[MaxSize];
    int top;   // 栈顶指针, 初始 -1
} SqStack;

void InitStack(SqStack &S) {
    S.top = -1;
}

bool StackEmpty(SqStack S) {
    return S.top == -1;
}

/* 进栈: 先移指针再存数据 */
bool Push(SqStack &S, ElemType x) {
    if (S.top == MaxSize - 1)
        return false;          // 栈满
    S.data[++S.top] = x;
    return true;
}

/* 出栈: 先取数据再移指针 */
bool Pop(SqStack &S, ElemType &x) {
    if (S.top == -1)
        return false;          // 栈空
    x = S.data[S.top--];
    return true;
}


/* ========== 二、循环队列 (牺牲一个单元法) ========== */

typedef struct {
    ElemType data[MaxSize];
    int front, rear;
} SqQueue;

void InitQueue(SqQueue &Q) {
    Q.front = Q.rear = 0;
}

bool QueueEmpty(SqQueue Q) {
    return Q.front == Q.rear;
}

/* 入队: rear 的下一个是 front 就满了 */
bool EnQueue(SqQueue &Q, ElemType x) {
    if ((Q.rear + 1) % MaxSize == Q.front)
        return false;  // 队满
    Q.data[Q.rear] = x;
    Q.rear = (Q.rear + 1) % MaxSize;
    return true;
}

/* 出队: 从 front 端取 */
bool DeQueue(SqQueue &Q, ElemType &x) {
    if (Q.front == Q.rear)
        return false;  // 队空
    x = Q.data[Q.front];
    Q.front = (Q.front + 1) % MaxSize;
    return true;
}


/* ========== 三、栈的应用 ========== */

/*
 * ★ 括号匹配
 *   左括号入栈, 右括号检查栈顶是否对称
 *   最终栈必须为空
 */
bool BracketMatch(const char *str) {
    SqStack S;
    InitStack(S);

    for (int i = 0; str[i] != '\0'; i++) {
        char ch = str[i];

        // 遇到左括号: 入栈
        if (ch == '(' || ch == '[' || ch == '{') {
            Push(S, ch);
        }
        // 遇到右括号: 检查配对
        else if (ch == ')' || ch == ']' || ch == '}') {
            if (StackEmpty(S))
                return false;       // 没有左括号可配

            ElemType top;
            Pop(S, top);

            if (ch == ')' && top != '(') return false;
            if (ch == ']' && top != '[') return false;
            if (ch == '}' && top != '{') return false;
        }
    }
    return StackEmpty(S);  // 栈空则全部匹配
}

/*
 * ★ 后缀表达式求值 (操作数为个位数, 简化演示)
 *   规则: 数字入栈, 遇运算符弹两个计算后压回
 *   注意: 先弹出的是右操作数!
 */
int EvalPostfix(const char *expr) {
    SqStack S;
    InitStack(S);
    ElemType a, b, result;

    for (int i = 0; expr[i] != '\0'; i++) {
        if (expr[i] >= '0' && expr[i] <= '9') {
            Push(S, expr[i] - '0');   // 数字字符转整数入栈
        } else if (expr[i] == ' ') {
            continue;                  // 跳过空格
        } else {
            Pop(S, b);   // 先弹出的是右操作数
            Pop(S, a);   // 再弹出左操作数

            switch (expr[i]) {
                case '+': result = a + b; break;
                case '-': result = a - b; break;
                case '*': result = a * b; break;
                case '/': result = a / b; break;
                default:  result = 0;
            }
            Push(S, result);
        }
    }
    Pop(S, result);
    return result;
}


/* ========== main 演示 ========== */
int main() {
    printf("===== 栈与队列 C++ 可运行版 =====\n\n");

    /* --- 顺序栈基础 --- */
    printf("--- 顺序栈 ---\n");
    SqStack S;
    InitStack(S);
    Push(S, 10);
    Push(S, 20);
    Push(S, 30);
    ElemType val;
    Pop(S, val);
    printf("弹出: %d\n", val);   // 30

    /* --- 循环队列 --- */
    printf("\n--- 循环队列 ---\n");
    SqQueue Q;
    InitQueue(Q);
    EnQueue(Q, 1);
    EnQueue(Q, 2);
    EnQueue(Q, 3);
    DeQueue(Q, val);
    printf("出队: %d\n", val);   // 1

    /* --- 括号匹配 --- */
    printf("\n--- 括号匹配 ---\n");
    printf("\"(a+b)*[c-d]\" 匹配? %s\n",
           BracketMatch("(a+b)*[c-d]") ? "是" : "否");
    printf("\"(a+b]*c\"     匹配? %s\n",
           BracketMatch("(a+b]*c") ? "是" : "否");

    /* --- 后缀表达式 --- */
    printf("\n--- 后缀表达式求值 ---\n");
    printf("\"34+5*\" = %d (即(3+4)*5)\n", EvalPostfix("34+5*"));
    printf("\"512+4*+3-\" = %d (即5+(1+2)*4-3)\n", EvalPostfix("512+4*+3-"));

    return 0;
}

03 二叉树

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— 二叉树 (可运行 C++ 版)
 *   文件: 03_二叉树.cpp
 *   编译: g++ 03_二叉树.cpp -o test && ./test
 * ============================================================
 *
 *   覆盖内容:
 *     1. 二叉链表存储与手工建树
 *     2. 先序/中序/后序 递归遍历
 *     3. ★ 中序非递归遍历 (栈实现)
 *     4. ★ 层序遍历 (队列实现, BFS 原型)
 *     5. 求深度 / 统计叶子
 *     6. ★ 2014 真题: 求 WPL (带权路径长度)
 *
 *   测试树:
 *          1
 *        /   \
 *       2     3
 *      / \   / \
 *     4   5 6   7
 * ============================================================
 */

#include <cstdio>
#include <cstdlib>

typedef int ElemType;

/* ---- 二叉链表定义 ---- */
typedef struct BiTNode {
    ElemType data;
    struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

/* 创建新结点 */
BiTNode *NewNode(ElemType val) {
    BiTNode *p = (BiTNode *)malloc(sizeof(BiTNode));
    p->data = val;
    p->lchild = p->rchild = NULL;
    return p;
}


/* ========== 一、递归遍历 ========== */

void PreOrder(BiTree T) {
    if (T != NULL) {
        printf("%d ", T->data);     // 先访问根
        PreOrder(T->lchild);        // 再左
        PreOrder(T->rchild);        // 再右
    }
}

void InOrder(BiTree T) {
    if (T != NULL) {
        InOrder(T->lchild);         // 先左
        printf("%d ", T->data);     // 再根
        InOrder(T->rchild);         // 再右
    }
}

void PostOrder(BiTree T) {
    if (T != NULL) {
        PostOrder(T->lchild);       // 先左
        PostOrder(T->rchild);       // 再右
        printf("%d ", T->data);     // 最后根
    }
}


/* ========== 二、中序非递归 (栈实现, 考研大题重点) ========== */
/*
 * 思路:
 *   1. 从当前结点一路向左走到底, 沿途入栈
 *   2. 走到空了, 弹栈访问
 *   3. 转向右子树, 重复步骤 1
 */
void InOrderNonRecursive(BiTree T) {
    BiTNode *stack[50];
    int top = -1;
    BiTNode *p = T;

    while (p != NULL || top != -1) {
        if (p != NULL) {
            stack[++top] = p;       // 入栈
            p = p->lchild;         // 一路向左
        } else {
            p = stack[top--];      // 弹栈
            printf("%d ", p->data); // 访问
            p = p->rchild;         // 转向右子树
        }
    }
}


/* ========== 三、层序遍历 (队列实现) ========== */
void LevelOrder(BiTree T) {
    if (T == NULL) return;

    BiTNode *queue[50];
    int front = 0, rear = 0;

    queue[rear++] = T;  // 根入队

    while (front != rear) {
        BiTNode *p = queue[front++];    // 出队
        printf("%d ", p->data);          // 访问

        if (p->lchild != NULL)
            queue[rear++] = p->lchild;   // 左孩子入队
        if (p->rchild != NULL)
            queue[rear++] = p->rchild;   // 右孩子入队
    }
}


/* ========== 四、常用操作 ========== */

/* 求深度: max(左深, 右深) + 1 */
int TreeDepth(BiTree T) {
    if (T == NULL) return 0;
    int ld = TreeDepth(T->lchild);
    int rd = TreeDepth(T->rchild);
    return (ld > rd ? ld : rd) + 1;
}

/* 统计叶子: 左右孩子都空就是叶子 */
int CountLeaves(BiTree T) {
    if (T == NULL) return 0;
    if (T->lchild == NULL && T->rchild == NULL)
        return 1;
    return CountLeaves(T->lchild) + CountLeaves(T->rchild);
}


/* ========== 五、2014 真题: 求 WPL ========== */
/*
 * WPL = 所有叶子的 (权值 × 深度) 之和
 * 叶子结点: 返回 weight * depth
 * 非叶子: 递归左右求和
 */
int CalcWPL(BiTree T, int depth) {
    if (T == NULL) return 0;
    if (T->lchild == NULL && T->rchild == NULL)
        return T->data * depth;       // 叶子: 权值 × 深度
    return CalcWPL(T->lchild, depth + 1)
         + CalcWPL(T->rchild, depth + 1);
}


/* ========== main 演示 ========== */
int main() {
    printf("===== 二叉树 C++ 可运行版 =====\n\n");

    /* 手工建树 */
    BiTNode *n1 = NewNode(1), *n2 = NewNode(2), *n3 = NewNode(3);
    BiTNode *n4 = NewNode(4), *n5 = NewNode(5);
    BiTNode *n6 = NewNode(6), *n7 = NewNode(7);
    n1->lchild = n2;  n1->rchild = n3;
    n2->lchild = n4;  n2->rchild = n5;
    n3->lchild = n6;  n3->rchild = n7;

    printf("先序: ");       PreOrder(n1);            printf("\n");
    printf("中序: ");       InOrder(n1);             printf("\n");
    printf("后序: ");       PostOrder(n1);           printf("\n");
    printf("中序非递归: "); InOrderNonRecursive(n1); printf("\n");
    printf("层序: ");       LevelOrder(n1);          printf("\n");

    printf("\n深度 = %d\n", TreeDepth(n1));
    printf("叶子数 = %d\n", CountLeaves(n1));
    printf("WPL = %d\n", CalcWPL(n1, 0));

    return 0;
}

04 排序算法

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— 八大排序 (可运行 C++ 版)
 *   文件: 04_排序算法.cpp
 *   编译: g++ 04_排序算法.cpp -o test && ./test
 * ============================================================
 *
 *   包含全部 8 种考纲排序算法:
 *     1. 直接插入排序          5. 快速排序
 *     2. 折半插入排序          6. 简单选择排序
 *     3. 希尔排序              7. 堆排序 (下标从1)
 *     4. 冒泡排序              8. 归并排序
 * ============================================================
 */

#include <cstdio>

/* 工具函数 */
void PrintArr(int A[], int n) {
    for (int i = 0; i < n; i++)
        printf("%d ", A[i]);
    printf("\n");
}

void CopyArr(int src[], int dst[], int n) {
    for (int i = 0; i < n; i++)
        dst[i] = src[i];
}

void swap(int &a, int &b) {
    int t = a; a = b; b = t;
}


/* ===========================================
 *  1. 直接插入排序
 *     稳定, 最好 O(n), 最坏/平均 O(n^2)
 * =========================================== */
void InsertSort(int A[], int n) {
    for (int i = 1; i < n; i++) {
        int temp = A[i];
        int j;
        for (j = i - 1; j >= 0 && temp < A[j]; j--)
            A[j + 1] = A[j];   // 比 temp 大的后移
        A[j + 1] = temp;        // 插入到正确位置
    }
}


/* ===========================================
 *  2. 折半插入排序
 *     稳定, 比较次数减少但移动不变, O(n^2)
 * =========================================== */
void BinaryInsertSort(int A[], int n) {
    for (int i = 1; i < n; i++) {
        int temp = A[i];
        int low = 0, high = i - 1;

        // 折半查找插入位置
        while (low <= high) {
            int mid = (low + high) / 2;
            if (A[mid] > temp)
                high = mid - 1;
            else
                low = mid + 1;
        }
        // high+1 就是插入位置, 后移 [high+1, i-1]
        for (int j = i - 1; j >= high + 1; j--)
            A[j + 1] = A[j];
        A[high + 1] = temp;
    }
}


/* ===========================================
 *  3. 希尔排序
 *     不稳定, 约 O(n^1.3)
 * =========================================== */
void ShellSort(int A[], int n) {
    for (int dk = n / 2; dk >= 1; dk /= 2) {       // 增量递减
        for (int i = dk; i < n; i++) {               // 对每组做插排
            if (A[i] < A[i - dk]) {
                int temp = A[i];
                int j;
                for (j = i - dk; j >= 0 && temp < A[j]; j -= dk)
                    A[j + dk] = A[j];
                A[j + dk] = temp;
            }
        }
    }
}


/* ===========================================
 *  4. 冒泡排序
 *     稳定, 最好 O(n), 最坏 O(n^2)
 * =========================================== */
void BubbleSort(int A[], int n) {
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;
        for (int j = n - 1; j > i; j--) {
            if (A[j - 1] > A[j]) {
                swap(A[j - 1], A[j]);
                swapped = true;
            }
        }
        if (!swapped) return;   // 本趟无交换, 已有序
    }
}


/* ===========================================
 *  5. 快速排序
 *     不稳定, 平均 O(nlogn), 最坏 O(n^2)
 * =========================================== */
int Partition(int A[], int low, int high) {
    int pivot = A[low];             // 取第一个作为枢轴

    while (low < high) {
        while (low < high && A[high] >= pivot)
            high--;                  // 从右找小的
        A[low] = A[high];

        while (low < high && A[low] <= pivot)
            low++;                   // 从左找大的
        A[high] = A[low];
    }
    A[low] = pivot;                  // 枢轴就位
    return low;
}

void QuickSort(int A[], int low, int high) {
    if (low < high) {
        int pos = Partition(A, low, high);
        QuickSort(A, low, pos - 1);   // 左半递归
        QuickSort(A, pos + 1, high);  // 右半递归
    }
}


/* ===========================================
 *  6. 简单选择排序
 *     不稳定, 时间始终 O(n^2)
 * =========================================== */
void SelectSort(int A[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int min = i;
        for (int j = i + 1; j < n; j++)
            if (A[j] < A[min]) min = j;
        if (min != i)
            swap(A[i], A[min]);
    }
}


/* ===========================================
 *  7. 堆排序 (大顶堆, 下标从 1, A[0] 作暂存)
 *     不稳定, O(nlogn), 空间 O(1)
 * =========================================== */
void AdjustDown(int A[], int k, int len) {
    A[0] = A[k];                     // 暂存要调整的结点
    for (int i = 2 * k; i <= len; i *= 2) {
        if (i < len && A[i] < A[i + 1])
            i++;                      // 找较大的孩子
        if (A[0] >= A[i])
            break;                    // 到位
        A[k] = A[i];                  // 大孩子上浮
        k = i;
    }
    A[k] = A[0];                      // 放到最终位置
}

void HeapSort(int A[], int len) {
    // 建初始大顶堆
    for (int i = len / 2; i > 0; i--)
        AdjustDown(A, i, len);

    // 排序: 不断交换堆顶与末尾, 再调整
    for (int i = len; i > 1; i--) {
        swap(A[1], A[i]);             // 堆顶与末尾交换
        AdjustDown(A, 1, i - 1);     // 调整剩余
    }
}


/* ===========================================
 *  8. 归并排序
 *     稳定, O(nlogn), 空间 O(n)
 * =========================================== */
int B_merge[50];   // 辅助数组

void Merge(int A[], int low, int mid, int high) {
    // 复制到辅助数组
    for (int k = low; k <= high; k++)
        B_merge[k] = A[k];

    // 归并: 两路合一
    int i = low, j = mid + 1, k = low;
    while (i <= mid && j <= high) {
        if (B_merge[i] <= B_merge[j])
            A[k++] = B_merge[i++];
        else
            A[k++] = B_merge[j++];
    }
    while (i <= mid)  A[k++] = B_merge[i++];
    while (j <= high) A[k++] = B_merge[j++];
}

void MergeSort(int A[], int low, int high) {
    if (low < high) {
        int mid = (low + high) / 2;
        MergeSort(A, low, mid);       // 排左半
        MergeSort(A, mid + 1, high);  // 排右半
        Merge(A, low, mid, high);     // 合并
    }
}


/* ========== main: 全部排序对比 ========== */
int main() {
    printf("===== 八大排序 C++ 可运行版 =====\n");
    int original[] = {49, 38, 65, 97, 76, 13, 27, 49};
    int n = 8, A[8];

    printf("原始数组: ");
    PrintArr(original, n);
    printf("\n");

    CopyArr(original, A, n); InsertSort(A, n);
    printf("直接插入: "); PrintArr(A, n);

    CopyArr(original, A, n); BinaryInsertSort(A, n);
    printf("折半插入: "); PrintArr(A, n);

    CopyArr(original, A, n); ShellSort(A, n);
    printf("希尔排序: "); PrintArr(A, n);

    CopyArr(original, A, n); BubbleSort(A, n);
    printf("冒泡排序: "); PrintArr(A, n);

    CopyArr(original, A, n); QuickSort(A, 0, n - 1);
    printf("快速排序: "); PrintArr(A, n);

    CopyArr(original, A, n); SelectSort(A, n);
    printf("选择排序: "); PrintArr(A, n);

    CopyArr(original, A, n); MergeSort(A, 0, n - 1);
    printf("归并排序: "); PrintArr(A, n);

    /* 堆排序: A[0] 暂存, 数据从 A[1] 开始 */
    printf("\n--- 堆排序 (下标从1) ---\n");
    int H[] = {0, 49, 38, 65, 97, 76, 13, 27, 49};
    printf("排序前: ");
    for (int i = 1; i <= 8; i++) printf("%d ", H[i]);
    printf("\n");
    HeapSort(H, 8);
    printf("排序后: ");
    for (int i = 1; i <= 8; i++) printf("%d ", H[i]);
    printf("\n");

    return 0;
}

05 串匹配与查找

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— KMP 与查找 (可运行 C++ 版)
 *   文件: 05_串匹配与查找.cpp
 *   编译: g++ 05_串匹配与查找.cpp -o test && ./test
 * ============================================================
 *
 *   覆盖内容:
 *     1. ★ KMP: get_next 求 next 数组 (1-based, 教材风格)
 *     2. ★ KMP: get_nextval 求 nextval 数组
 *     3. ★ KMP: Index_KMP 主匹配函数
 *     4. 折半查找 (非递归)
 * ============================================================
 */

#include <cstdio>
#include <cstring>


/* ========== KMP 算法 (1-based, 教材风格) ========== */

/*
 * get_next: 求模式串 T 的 next 数组
 *
 * next[j] 含义: 当 T[j] 与主串失配时, j 应回退到 next[j]
 * 手算提示: next[1]=0, next[2]=1, 从第3位开始找最长公共前后缀+1
 */
void get_next(char T[], int Tlen, int next[]) {
    int i = 1, j = 0;
    next[1] = 0;

    while (i < Tlen) {
        if (j == 0 || T[i] == T[j]) {
            ++i;
            ++j;
            next[i] = j;
        } else {
            j = next[j];   // 回退
        }
    }
}

/*
 * get_nextval: next 的优化版
 *
 * 当 T[i] == T[next[i]] 时, 回退后仍会失配
 * 所以直接继承 nextval[next[i]]
 */
void get_nextval(char T[], int Tlen, int nextval[]) {
    int i = 1, j = 0;
    nextval[1] = 0;

    while (i < Tlen) {
        if (j == 0 || T[i] == T[j]) {
            ++i;
            ++j;
            if (T[i] != T[j])
                nextval[i] = j;            // 不等, 正常赋值
            else
                nextval[i] = nextval[j];   // 相等, 继承优化
        } else {
            j = nextval[j];
        }
    }
}

/*
 * KMP 主匹配: 在 S 中查找 T 第一次出现的位置 (1-based)
 * 主串不回溯, 模式串利用 next 跳转
 */
int Index_KMP(char S[], int Slen, char T[], int Tlen, int next[]) {
    int i = 1, j = 1;

    while (i <= Slen && j <= Tlen) {
        if (j == 0 || S[i] == T[j]) {
            ++i;
            ++j;
        } else {
            j = next[j];   // 模式串回退
        }
    }

    if (j > Tlen)
        return i - Tlen;    // 匹配成功, 返回起始位置
    else
        return 0;           // 未找到
}


/* ========== 折半查找 ========== */

/*
 * 有序数组中查找 key, 找到返回下标, 找不到返回 -1
 * 时间 O(logn)
 */
int BinarySearch(int A[], int n, int key) {
    int low = 0, high = n - 1;

    while (low <= high) {
        int mid = (low + high) / 2;
        if (A[mid] == key)
            return mid;
        else if (A[mid] > key)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return -1;  // 未找到
}


/* ========== main 演示 ========== */
int main() {
    printf("===== KMP 与查找 C++ 可运行版 =====\n\n");

    /* --- KMP 演示 ---
     * 教材风格: 下标从 1 开始, ch[0] 用 '#' 占位
     */
    char S[] = "#aababaabaabcac";   // 主串, 14 个有效字符
    char T[] = "#abaabcac";         // 模式串, 8 个有效字符
    int Slen = 14, Tlen = 8;
    int next[20], nextval[20];

    get_next(T, Tlen, next);
    get_nextval(T, Tlen, nextval);

    printf("模式串 T = \"abaabcac\"\n");
    printf("位序:    ");
    for (int i = 1; i <= Tlen; i++) printf("%d ", i);
    printf("\n字符:    ");
    for (int i = 1; i <= Tlen; i++) printf("%c ", T[i]);
    printf("\nnext:    ");
    for (int i = 1; i <= Tlen; i++) printf("%d ", next[i]);
    printf("\nnextval: ");
    for (int i = 1; i <= Tlen; i++) printf("%d ", nextval[i]);
    printf("\n\n");

    int pos = Index_KMP(S, Slen, T, Tlen, next);
    printf("主串 S = \"aababaabaabcac\"\n");
    printf("匹配位置 = %d\n\n", pos);

    /* --- 折半查找 --- */
    printf("--- 折半查找 ---\n");
    int arr[] = {7, 10, 13, 16, 19, 29, 32, 33, 37, 41, 43};
    int n = 11;
    printf("有序数组: ");
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    printf("\n");

    printf("查找 33 -> 下标 %d\n", BinarySearch(arr, n, 33));
    printf("查找 20 -> %s\n",
           BinarySearch(arr, n, 20) == -1 ? "未找到" : "找到");

    return 0;
}

06 图算法

cpp
/*
 * ============================================================
 *   408 考研数据结构 —— 图算法 (可运行 C++ 版)
 *   文件: 06_图算法.cpp
 *   编译: g++ 06_图算法.cpp -o test && ./test
 * ============================================================
 *
 *   覆盖内容:
 *     1. 邻接矩阵存储图
 *     2. ★ DFS 深度优先遍历 (递归)
 *     3. ★ BFS 广度优先遍历 (队列)
 *     4. ★ Dijkstra 最短路径
 *     5. ★ 拓扑排序
 * ============================================================
 */

#include <cstdio>
#include <cstdlib>

#define MaxV 20
#define INF 999999

/* ========== 邻接矩阵存储 ========== */
typedef struct {
    int vex[MaxV];
    int Edge[MaxV][MaxV];
    int vexnum, arcnum;
} MGraph;

bool visited[MaxV];   // 访问标记数组

/* 初始化图 */
void InitGraph(MGraph &G, int n) {
    G.vexnum = n;
    G.arcnum = 0;
    for (int i = 0; i < n; i++) {
        G.vex[i] = i;
        for (int j = 0; j < n; j++)
            G.Edge[i][j] = (i == j) ? 0 : INF;
    }
}

/* 添加无向边 */
void AddEdge(MGraph &G, int u, int v, int w) {
    G.Edge[u][v] = w;
    G.Edge[v][u] = w;
    G.arcnum++;
}


/* ========== DFS 深度优先遍历 ========== */
/*
 * 递归: 访问 v, 标记, 遍历邻居递归
 */
void DFS(MGraph &G, int v) {
    printf("%d ", v);
    visited[v] = true;

    for (int w = 0; w < G.vexnum; w++) {
        if (G.Edge[v][w] != 0 && G.Edge[v][w] != INF && !visited[w])
            DFS(G, w);
    }
}

void DFSTraverse(MGraph &G) {
    for (int i = 0; i < G.vexnum; i++)
        visited[i] = false;
    for (int i = 0; i < G.vexnum; i++)
        if (!visited[i]) DFS(G, i);
}


/* ========== BFS 广度优先遍历 ========== */
/*
 * 队列: 源点入队标记, 出队遍历邻居, 未访问的入队
 */
void BFS(MGraph &G, int v) {
    int queue[MaxV];
    int front = 0, rear = 0;

    printf("%d ", v);
    visited[v] = true;
    queue[rear++] = v;

    while (front != rear) {
        int u = queue[front++];   // 出队

        for (int w = 0; w < G.vexnum; w++) {
            if (G.Edge[u][w] != 0 && G.Edge[u][w] != INF && !visited[w]) {
                printf("%d ", w);
                visited[w] = true;
                queue[rear++] = w;
            }
        }
    }
}

void BFSTraverse(MGraph &G) {
    for (int i = 0; i < G.vexnum; i++)
        visited[i] = false;
    for (int i = 0; i < G.vexnum; i++)
        if (!visited[i]) BFS(G, i);
}


/* ========== Dijkstra 最短路径 ========== */
/*
 * 从源点 src 到其余各点的最短距离
 * dist[i]: 源到 i 的当前最短距离
 * path[i]: i 的前驱 (用于输出路径)
 * fin[i]:  i 是否已确定最短路
 */
void Dijkstra(MGraph &G, int src) {
    int dist[MaxV], path[MaxV];
    bool fin[MaxV];

    // 初始化
    for (int i = 0; i < G.vexnum; i++) {
        dist[i] = G.Edge[src][i];
        fin[i] = false;
        path[i] = (dist[i] < INF) ? src : -1;
    }
    dist[src] = 0;
    fin[src] = true;

    // 循环 n-1 次
    for (int i = 0; i < G.vexnum - 1; i++) {
        // 找未确定的最近顶点
        int minDist = INF, u = -1;
        for (int j = 0; j < G.vexnum; j++) {
            if (!fin[j] && dist[j] < minDist) {
                minDist = dist[j];
                u = j;
            }
        }
        if (u == -1) break;   // 不连通
        fin[u] = true;

        // 松弛操作: 用 u 更新其邻居
        for (int j = 0; j < G.vexnum; j++) {
            if (!fin[j] && G.Edge[u][j] < INF
                && dist[u] + G.Edge[u][j] < dist[j]) {
                dist[j] = dist[u] + G.Edge[u][j];
                path[j] = u;
            }
        }
    }

    // 输出结果
    printf("Dijkstra (源点 %d):\n", src);
    for (int i = 0; i < G.vexnum; i++) {
        if (dist[i] >= INF)
            printf("  到 %d: 不可达\n", i);
        else
            printf("  到 %d: 距离 = %d, 前驱 = %d\n", i, dist[i], path[i]);
    }
}


/* ========== 拓扑排序 ========== */
/*
 * 1. 统计入度
 * 2. 入度为 0 的入栈
 * 3. 弹出→输出→邻居入度-1→新入度0则入栈
 * 4. 若输出数 < 顶点数, 有环
 */
int TopologicalSort(MGraph &G) {
    int indegree[MaxV] = {0};
    int stack[MaxV], top = -1;
    int count = 0;

    // 统计入度
    for (int j = 0; j < G.vexnum; j++)
        for (int i = 0; i < G.vexnum; i++)
            if (G.Edge[i][j] != 0 && G.Edge[i][j] != INF)
                indegree[j]++;

    // 入度为 0 的入栈
    for (int i = 0; i < G.vexnum; i++)
        if (indegree[i] == 0)
            stack[++top] = i;

    printf("拓扑序列: ");
    while (top != -1) {
        int v = stack[top--];   // 出栈
        printf("%d ", v);
        count++;

        for (int j = 0; j < G.vexnum; j++) {
            if (G.Edge[v][j] != 0 && G.Edge[v][j] != INF) {
                indegree[j]--;
                if (indegree[j] == 0)
                    stack[++top] = j;
            }
        }
    }
    printf("\n");

    if (count < G.vexnum) {
        printf("存在环, 拓扑排序失败!\n");
        return 0;
    }
    return 1;
}


/* ========== main 演示 ========== */
int main() {
    printf("===== 图算法 C++ 可运行版 =====\n\n");

    /* 无向图:
     *   0---1---3
     *   |   |
     *   2---4
     */
    MGraph G;
    InitGraph(G, 5);
    AddEdge(G, 0, 1, 1);
    AddEdge(G, 0, 2, 1);
    AddEdge(G, 1, 3, 1);
    AddEdge(G, 1, 4, 1);
    AddEdge(G, 2, 4, 1);

    printf("DFS: ");
    DFSTraverse(G);
    printf("\n");
    printf("BFS: ");
    BFSTraverse(G);
    printf("\n\n");

    /* 带权有向图:
     *   0--(1)-->1--(3)-->3
     *   |        |
     *  (6)      (2)
     *   v        v
     *   2--(1)-->4
     */
    MGraph G2;
    InitGraph(G2, 5);
    G2.Edge[0][1] = 1;
    G2.Edge[0][2] = 6;
    G2.Edge[1][3] = 3;
    G2.Edge[1][4] = 2;
    G2.Edge[2][4] = 1;

    Dijkstra(G2, 0);
    printf("\n");

    printf("--- 拓扑排序 ---\n");
    TopologicalSort(G2);

    return 0;
}

Released under the MIT License.