前提概要

小智是数字世界的管理员,负责帮居民把数字存进仓库,还要帮忙查找、删除、替换数字。可刚上任就撞上第一个难题——仓库容量不好定,谁也不知道到底要存多少数字。容量定小了,存不下;定大了,又白白浪费空间。

幸好,小智之前就已经学习过这几篇教程,心里有了底,让我们开始吧!

功能设计

#include <stdio.h>
#include <stdlib.h>

int upSize(int size) {
    if (size == 0) {
        size++;
    }
    return size;
}

int main() {
    // 使用数组存储数据
    system("chcp 65001");
    int size = 0; // 已存入的数据总数 作为下次存储数字的下标
    int length = 4; // 当前仓库的总容量
    int size0;
    int *values = (int *)malloc(length * sizeof(int));
    while (1) {
        printf("=== 小智的数字仓库 ===\n");
        printf("=== 201: 存入单个数字 \n");
        printf("=== 202: 存入多个数字 \n");
        printf("=== 203: 根据位置查找数字 \n");
        printf("=== 204: 根据位置修改数字 \n");
        printf("=== 205: 根据位置删除数字 \n");
        printf("=== 206: 查看仓库中所有数字 \n");
        printf("=== 207: 删除指定的所有相同数字 \n");
        printf("=== 208: 对仓库中的所有数字排序 \n");
        printf("=== 209: 退出仓库 \n");
        printf("请输入功能编号: \n");
        size0=upSize(size);
        int cid = 0;
        scanf("%d", &cid);
        if (cid == 201) {
            printf("请输入一个数字:\n");
            int num = 0;
            scanf("%d", &num);
            values = addData(&size,&length,values,num);
        }
        else if (cid == 202) {
            printf("请输入要存入数字总数:\n");
            int n = 0;
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                printf("请输入第%d个数字:\n",i+1);
                int num = 0;
                scanf("%d", &num);
                values = addData(&size,&length,values,num);
            }
        }
        else if (cid == 203) {
            findData(size0,values);
        }
        else if (cid == 204) {
            modifyData(size0,values);
        }
        else if (cid == 205) {
            removeData(size0,&size,values);
        }
        else if (cid == 206) {
            displayData(size,values);
        }
        else if (cid == 207) {
            removeAll(&size,values);
        }
        else if (cid == 208) {
            printf("输入1正序(从小到大),输入2倒序(从大到小),其它数字取消操作:\n");
            int n;
            scanf("%d", &n);
            if (n == 1) {
                sortData1(size,values);
            }
            else if (n == 2) {
                sortData2(size,values);
            }
            else if (cid == 209) {
                printf("感谢使用小智数字仓库");
                break;
            }
        }
    }
}

先来认识几个出场的关键变量吧:

  1. size 是访问下标,指向最后一个已存数据的位置,增删数据时它会跟着变化。
  2. size0 是输入上限,超出这个值的输入统统无效。
  3. length 是数组的总容量,和 size 长得有点像,但职责完全不同,可别混淆哦。
  4. values 就是我们的数字仓库本体啦。

所有设计好的功能,都能在菜单里一眼看到并直接选择。

另外,这个程序采用函数封装的方式来组织功能,这样能让主函数保持简洁、可读性更强。

添加与扩容

int *addData(int *size,int *length,int values[],int num){
    //先判断是否是存满了
    if (*size == *length) {
        // 需要扩容
        int newLen = *length * 2;
        int *newValues = (int *)malloc(newLen * sizeof(int)); // 新数组
        // 迁移数据
        for (int i = 0; i < *length; i++) {
            newValues[i] = values[i];
        }
        // 更新数据
        *length = newLen;
        // 更新旧数组名存储的地址
        values = newValues;
        printf("扩容完成,当前的总容量升级为:%d\n", *length);
    }
    values[*size] = num;
    *size = *size + 1;
    return values;
}

「扩容」并不是独立的功能,而是藏在添加数据流程里的一个判断分支:只有当仓库真的满员了、size 追上了 length,才会临时触发扩容。
整个流程可以拆成两步:

  1. 添加数据:把 num 放到 values[*size] 这个位置,存完之后让 *size 加 1,相当于把「已存数量」往后推一格。
  2. 扩容:如果存之前发现仓库已经满了,就先申请一块容量翻倍的新仓库(length * 2),把旧仓库里的数字全部搬过去,再更新 length,并让 values 指向新仓库,最后继续执行第 1 步完成存储。

简单说就是:先看看还有没有位置,没位置就换个大仓库,再把数字稳稳放进去。

打印全部

void displayData(int size,int values[]) {
    printf("小智数字仓库当前数据:[");
    for (int i = 0; i < size; i++) {
        printf("%d,", values[i]);
    }
    printf("]\n");
}

这里有个小细节:遍历时用的是 i < size,而不是 i < length。因为 size 才代表仓库里真正存了多少数字,而 length 表示仓库的总容量。前面扩容判断里之所以写 *size == *length,是因为只有在仓库刚好满员的那一刻,两者才相等;正常情况下,size 是小于 length 的。

输入判定

用户输入什么,可不能全盘相信,所以这里得多留个心眼,加一道「输入判定」。推荐使用 do-while 结构,这样就算用户输错了也无妨,循环会贴心地再问一遍,直到输入合规为止,完全不会中途罢工退出。

int checkIndex(int size0) {
    int index;
    do {
        printf("请输入一个编号(0~%d):\n",size0-1);
        int index;
        scanf("%d", &index);
        if (index < 0 || index > size0-1) {
            printf("输入的编号不存在,请重新输入!\n");
        }
        else {
            return index;
        }
    } while (1);
}

删查改

查找和修改算是比较轻松的任务:直接顺着下标找过去,找到位置就能读或改。删除就稍微有点讲究了,先别急着往下翻,你可以试着看看下面的程序,能不能自己看出点门道。

void removeData(int size0,int *size,int *values) {
    int n=checkIndex(size0),oldNum;
    oldNum = values[n];
    for (int i = n; i <= *size-n; i++) {
        values[i] = values[i+1];
    }
    *size = *size - 1;
    printf("数字仓库已删除%d号位储存的数字(%d):\n",n,oldNum);
}

void findData(int size0,int values[]) {
    int n=checkIndex(size0);
    printf("数字仓库的%d号位储存的数字是:%d\n",n,values[n]);
}

void modifyData(int size0,int *values) {
    int num,oldNum,n=checkIndex(size0);
    printf("请输入一个新数字(原位置的数字是%d):\n",values[n]);
    scanf("%d", &num);
    oldNum = values[n];
    values[n] = num;
    printf("数字仓库的%d号位储存的数字由%d改为%d:\n",n,oldNum,values[n]);
}

可以注意到,删除的巧妙之处在于:不能留一个空位在那里,得把被删除数字后面的每个数字依次向前挪一格。程序里用的正是这种「覆盖」方法,前一个位置被后一个数字顶上去,仓库就重新变得整整齐齐。

细心的你可能已经发现啦:删除、查找、修改这三个功能,都整齐地加上了刚才的输入判定。无论是查找、修改还是删除,只要位置不合规,都会先被 do-while 温柔地拦下,直到拿到合法编号为止。

排序功能

排序这里选用的是最经典的冒泡排序:相邻两个数字两两比较,谁更大谁就往后挪,像气泡一样一点一点浮到正确的位置。sortData1 负责从小到大正序排列,sortData2 则是它的“镜像版”,把大小关系一翻转,立刻变成从大到小的倒序。至于选择排序、插入排序这些其他路子,代码也完全可以换成它们,本文就先不展开啦。

void sortData1(int size,int *values) {
    int d;
    for (int j = 0; j < size-1; j++) {
        for (int i = 0; i < size-1; i++) {
            if (values[i] > values[i+1]) {
                d = values[i];
                values[i] = values[i+1];
                values[i+1] = d;
            }
        }
    }
}
void sortData2(int size,int *values) {
    int d;
    for (int j = 0; j < size-1; j++) {
        for (int i = 0; i < size-1; i++) {
            if (values[i] < values[i+1]) {
                d = values[i];
                values[i] = values[i+1];
                values[i+1] = d;
            }
        }
    }
}

删掉全部指定数字

这个功能有点像仓库里的一次“大扫除”:用户随便输入一个数字,程序就要把仓库里所有跟它一模一样的数字统统清理掉,一个都不留。你可以别着急看答案,自己先想想。

void removeAll(int *size,int *values) {
    int num,move=0;
    printf("请输入一个数字:\n");
    scanf("%d", &num);
    for (int i = 0; i < *size; i++) {
        if (values[i] == num) {
            move++;
        }
        else {
            values[i-move] = values[i];
        }
    }
    *size=*size-move;
    printf("数字仓库已删除所有的%d\n",num);
}

本程序采用的方法是边扫描边覆盖:move 就像一个小账本,负责记录已经碰到了多少个要删除的数字。遍历时,遇到要删除的数字就让 move 加 1;遇到要保留的数字,就把它整体“前移” move 格,刚好补上前面空出来的位置。一轮走完后,所有不要的数字都被要保留的数字顶掉了,最后把 size 减去 move,仓库就完成了瘦身,干净又整齐。

相比反复调用“删一个数字”再搬来搬去,这里只从头到尾扫了一遍,思路更轻巧高效。

完整源码

恭喜你完成了“数字仓库”项目!

#include <stdio.h>
#include <stdlib.h>

int upSize(int size) {
    if (size == 0) {
        size++;
    }
    return size;
}
int checkIndex(int size0) {
    int index;
    do {
        printf("请输入一个编号(0~%d):\n",size0-1);
        int index;
        scanf("%d", &index);
        if (index < 0 || index > size0-1) {
            printf("输入的编号不存在,请重新输入!\n");
        }
        else {
            return index;
        }
    } while (1);
}
int *addData(int *size,int *length,int values[],int num){
    //先判断是否是存满了
    if (*size == *length) {
        // 需要扩容
        int newLen = *length * 2;
        int *newValues = (int *)malloc(newLen * sizeof(int)); // 新数组
        // 迁移数据
        for (int i = 0; i < *length; i++) {
            newValues[i] = values[i];
        }
        // 更新数据
        *length = newLen;
        // 更新旧数组名存储的地址
        values = newValues;
        printf("扩容完成,当前的总容量升级为:%d\n", *length);
    }
    values[*size] = num;
    *size = *size + 1;
    return values;
}
void findData(int size0,int values[]) {
    int n=checkIndex(size0);
    printf("数字仓库的%d号位储存的数字是:%d\n",n,values[n]);
}
void modifyData(int size0,int *values) {
    int num,oldNum,n=checkIndex(size0);
    printf("请输入一个新数字(原位置的数字是%d):\n",values[n]);
    scanf("%d", &num);
    oldNum = values[n];
    values[n] = num;
    printf("数字仓库的%d号位储存的数字由%d改为%d:\n",n,oldNum,values[n]);
}
void removeData(int size0,int *size,int *values) {
    int n=checkIndex(size0),oldNum;
    oldNum = values[n];
    for (int i = n; i <= *size-n; i++) {
        values[i] = values[i+1];
    }
    *size = *size - 1;
    printf("数字仓库已删除%d号位储存的数字(%d):\n",n,oldNum);
}
void removeAll(int *size,int *values) {
    int num,move=0;
    printf("请输入一个数字:\n");
    scanf("%d", &num);
    for (int i = 0; i < *size; i++) {
        if (values[i] == num) {
            move++;
        }
        else {
            values[i-move] = values[i];
        }
    }
    *size=*size-move;
    printf("数字仓库已删除所有的%d\n",num);
}
void sortData1(int size,int *values) {
    int d;
    for (int j = 0; j < size-1; j++) {
        for (int i = 0; i < size-1; i++) {
            if (values[i] > values[i+1]) {
                d = values[i];
                values[i] = values[i+1];
                values[i+1] = d;
            }
        }
    }
}
void sortData2(int size,int *values) {
    int d;
    for (int j = 0; j < size-1; j++) {
        for (int i = 0; i < size-1; i++) {
            if (values[i] < values[i+1]) {
                d = values[i];
                values[i] = values[i+1];
                values[i+1] = d;
            }
        }
    }
}
void displayData(int size,int values[]) {
    printf("小智数字仓库当前数据:[");
    for (int i = 0; i < size; i++) {
        printf("%d,", values[i]);
    }
    printf("]\n");
}

int main() {
    // 使用数组存储数据
    system("chcp 65001");
    int size = 0; // 已存入的数据总数 作为下次存储数字的下标
    int length = 4; // 当前仓库的总容量
    int size0;
    int *values = (int *)malloc(length * sizeof(int));
    while (1) {
        printf("=== 小智的数字仓库 ===\n");
        printf("=== 201: 存入单个数字 \n");
        printf("=== 202: 存入多个数字 \n");
        printf("=== 203: 根据位置查找数字 \n");
        printf("=== 204: 根据位置修改数字 \n");
        printf("=== 205: 根据位置删除数字 \n");
        printf("=== 206: 查看仓库中所有数字 \n");
        printf("=== 207: 删除指定的所有相同数字 \n");
        printf("=== 208: 对仓库中的所有数字排序 \n");
        printf("=== 209: 退出仓库 \n");
        printf("请输入功能编号: \n");
        size0=upSize(size);
        int cid = 0;
        scanf("%d", &cid);
        if (cid == 201) {
            printf("请输入一个数字:\n");
            int num = 0;
            scanf("%d", &num);
            values = addData(&size,&length,values,num);
        }
        else if (cid == 202) {
            printf("请输入要存入数字总数:\n");
            int n = 0;
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                printf("请输入第%d个数字:\n",i+1);
                int num = 0;
                scanf("%d", &num);
                values = addData(&size,&length,values,num);
            }
        }
        else if (cid == 203) {
            findData(size0,values);
        }
        else if (cid == 204) {
            modifyData(size0,values);
        }
        else if (cid == 205) {
            removeData(size0,&size,values);
        }
        else if (cid == 206) {
            displayData(size,values);
        }
        else if (cid == 207) {
            removeAll(&size,values);
        }
        else if (cid == 208) {
            printf("输入1正序(从小到大),输入2倒序(从大到小),其它数字取消操作:\n");
            int n;
            scanf("%d", &n);
            if (n == 1) {
                sortData1(size,values);
            }
            else if (n == 2) {
                sortData2(size,values);
            }
            else if (cid == 209) {
                printf("感谢使用小智数字仓库");
                break;
            }
        }
    }
}
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐