C 语言实例 – 数组拆分与合并(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在 C 语言编程中,数组是存储和操作数据的核心工具之一。无论是处理用户输入、文件读写,还是实现算法逻辑,数组都扮演着关键角色。然而,当数组规模较大或数据结构复杂时,如何高效地拆分合并数组,就成为提升代码性能和可读性的重要技能。本文将通过实例代码分步讲解,帮助读者掌握数组拆分与合并的核心方法,同时结合实际场景,解析其在编程实践中的应用价值。


数组的基本概念与操作基础

数组的定义与初始化

在 C 语言中,数组是一段连续的内存空间,用于存储相同类型的数据。例如,定义一个整型数组 int arr[5] = {1, 2, 3, 4, 5};,表示分配 5 个 int 类型的连续存储单元,并初始化为 1 到 5 的数值。

数组的索引与遍历

数组元素通过索引(从 0 开始)访问,例如 arr[0] 对应第一个元素。遍历数组通常使用循环结构,例如:

for (int i = 0; i < 5; i++) {  
    printf("%d ", arr[i]);  
}  

输出结果为 1 2 3 4 5

数组的局限性

数组的大小在声明时固定,无法动态扩展或缩小。因此,当需要灵活调整数据规模时,拆分和合并操作就显得尤为重要。


数组拆分的三种方法与实例

方法一:按索引拆分

核心思想:根据指定的起始和结束索引,将原数组分割为多个子数组。
比喻:这如同将一本厚书拆分为多个章节,每个章节对应数组的一部分。

代码示例

#include <stdio.h>  

void split_by_index(int original[], int original_size, int start, int end, int result[]) {  
    int i;  
    for (i = 0; i <= end - start; i++) {  
        result[i] = original[start + i];  
    }  
}  

int main() {  
    int arr[] = {10, 20, 30, 40, 50, 60};  
    int split_arr[3];  

    split_by_index(arr, 6, 2, 4, split_arr);  

    printf("拆分后的数组元素:");  
    for (int i = 0; i < 3; i++) {  
        printf("%d ", split_arr[i]);  // 输出:30 40 50  
    }  
    return 0;  
}  

解析

  • 函数 split_by_index 接收原数组、大小、起始索引和结束索引,返回目标子数组。
  • 需注意目标数组的大小需足够容纳拆分后的元素,否则可能导致内存越界。

方法二:按元素值拆分

核心思想:根据元素的数值特征(如奇偶性、大小范围)将数组拆分为多个子数组。

案例:按奇偶性拆分数组

#include <stdio.h>  

void split_by_parity(int original[], int size, int *even, int *odd) {  
    int e = 0, o = 0;  
    for (int i = 0; i < size; i++) {  
        if (original[i] % 2 == 0) {  
            even[e++] = original[i];  
        } else {  
            odd[o++] = original[i];  
        }  
    }  
}  

int main() {  
    int arr[] = {1, 2, 3, 4, 5, 6};  
    int even[3], odd[3];  

    split_by_parity(arr, 6, even, odd);  

    printf("偶数数组:");  
    for (int i = 0; i < 3; i++) {  
        printf("%d ", even[i]);  // 输出:2 4 6  
    }  
    printf("\n奇数数组:");  
    for (int i = 0; i < 3; i++) {  
        printf("%d ", odd[i]);   // 输出:1 3 5  
    }  
    return 0;  
}  

解析

  • 通过判断元素是否为偶数,将原数组拆分为两个子数组。
  • 需预先为 evenodd 分配足够空间,或动态计算元素数量。

方法三:按条件拆分(动态内存分配)

当子数组的大小不确定时,可使用动态内存分配(malloc)实现更灵活的拆分。

案例:按元素值范围拆分

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

void split_by_condition(int original[], int size, int **result, int *result_sizes, int threshold) {  
    int count_low = 0, count_high = 0;  
    for (int i = 0; i < size; i++) {  
        if (original[i] < threshold) {  
            count_low++;  
        } else {  
            count_high++;  
        }  
    }  

    *result = (int *)malloc((count_low + count_high) * sizeof(int));  
    result_sizes[0] = count_low;  
    result_sizes[1] = count_high;  

    int low_ptr = 0, high_ptr = count_low;  // 高区起始位置  
    for (int i = 0; i < size; i++) {  
        if (original[i] < threshold) {  
            (*result)[low_ptr++] = original[i];  
        } else {  
            (*result)[high_ptr++] = original[i];  
        }  
    }  
}  

int main() {  
    int arr[] = {5, 3, 8, 1, 10, 2};  
    int size = 6;  
    int *result;  
    int result_sizes[2];  

    split_by_condition(arr, size, &result, result_sizes, 5);  

    printf("小于5的元素:");  
    for (int i = 0; i < result_sizes[0]; i++) {  
        printf("%d ", result[i]);  // 输出:3 1 2  
    }  
    printf("\n大于等于5的元素:");  
    for (int i = result_sizes[0]; i < result_sizes[0] + result_sizes[1]; i++) {  
        printf("%d ", result[i]);  // 输出:5 8 10  
    }  
    free(result);  
    return 0;  
}  

解析

  • 动态分配内存以适应不同条件下的元素数量。
  • 需手动释放内存(free(result)),避免内存泄漏。

数组合并的实现与优化

基础合并:简单拼接

将两个数组按顺序合并为一个新数组,无需排序或特殊逻辑。

代码示例

#include <stdio.h>  

void merge_arrays(int arr1[], int size1, int arr2[], int size2, int merged[]) {  
    int i;  
    for (i = 0; i < size1; i++) {  
        merged[i] = arr1[i];  
    }  
    for (int j = 0; j < size2; j++) {  
        merged[i++] = arr2[j];  
    }  
}  

int main() {  
    int arr1[] = {1, 2, 3};  
    int arr2[] = {4, 5, 6};  
    int merged[6];  

    merge_arrays(arr1, 3, arr2, 3, merged);  

    printf("合并后的数组:");  
    for (int i = 0; i < 6; i++) {  
        printf("%d ", merged[i]);  // 输出:1 2 3 4 5 6  
    }  
    return 0;  
}  

进阶合并:按条件去重

合并时过滤重复元素,例如将两个数组合并为无重复的集合。

代码示例

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

void merge_unique(int arr1[], int size1, int arr2[], int size2, int **merged, int *merged_size) {  
    int total = size1 + size2;  
    int *temp = (int *)malloc(total * sizeof(int));  

    int i;  
    for (i = 0; i < size1; i++) {  
        temp[i] = arr1[i];  
    }  
    for (int j = 0; j < size2; j++) {  
        temp[i++] = arr2[j];  
    }  

    // 排序后去重  
    qsort(temp, total, sizeof(int), compare);  
    int unique_count = 1;  
    for (int k = 1; k < total; k++) {  
        if (temp[k] != temp[k - 1]) {  
            temp[unique_count++] = temp[k];  
        }  
    }  

    *merged = (int *)malloc(unique_count * sizeof(int));  
    for (int m = 0; m < unique_count; m++) {  
        (*merged)[m] = temp[m];  
    }  
    *merged_size = unique_count;  
    free(temp);  
}  

int compare(const void *a, const void *b) {  
    return (*(int *)a - *(int *)b);  
}  

int main() {  
    int arr1[] = {1, 2, 3};  
    int arr2[] = {3, 4, 5};  
    int *merged;  
    int merged_size;  

    merge_unique(arr1, 3, arr2, 3, &merged, &merged_size);  

    printf("去重后的合并数组:");  
    for (int i = 0; i < merged_size; i++) {  
        printf("%d ", merged[i]);  // 输出:1 2 3 4 5  
    }  
    free(merged);  
    return 0;  
}  

解析

  • 先合并两个数组,再通过排序和遍历去重。
  • 动态内存分配需注意内存释放,避免泄漏。

实战案例:文件数据的拆分与合并

场景描述

假设需要将一个包含 1000 个数字的数组按每 200 个元素拆分存储到多个文件中,并在需要时合并这些文件数据。

代码实现

#include <stdio.h>  

#define ARRAY_SIZE 1000  
#define SPLIT_SIZE 200  

void save_to_files(int arr[], int size) {  
    for (int i = 0; i < size; i += SPLIT_SIZE) {  
        int chunk_size = (i + SPLIT_SIZE > size) ? (size - i) : SPLIT_SIZE;  
        char filename[20];  
        snprintf(filename, sizeof(filename), "data_%d.txt", i / SPLIT_SIZE);  

        FILE *file = fopen(filename, "w");  
        if (!file) {  
            perror("无法创建文件");  
            return;  
        }  

        for (int j = 0; j < chunk_size; j++) {  
            fprintf(file, "%d ", arr[i + j]);  
        }  
        fclose(file);  
    }  
}  

void merge_from_files(int merged[], int *merged_size) {  
    int file_count = ARRAY_SIZE / SPLIT_SIZE + (ARRAY_SIZE % SPLIT_SIZE != 0);  
    *merged_size = 0;  

    for (int i = 0; i < file_count; i++) {  
        char filename[20];  
        snprintf(filename, sizeof(filename), "data_%d.txt", i);  
        FILE *file = fopen(filename, "r");  
        if (!file) {  
            perror("无法读取文件");  
            continue;  
        }  

        int num;  
        while (fscanf(file, "%d", &num) != EOF) {  
            merged[(*merged_size)++] = num;  
        }  
        fclose(file);  
    }  
}  

int main() {  
    int original[ARRAY_SIZE];  
    // 初始化数组(此处省略具体赋值代码)  

    save_to_files(original, ARRAY_SIZE);  

    int merged[ARRAY_SIZE];  
    int merged_size = 0;  
    merge_from_files(merged, &merged_size);  

    printf("合并后的元素数量:%d\n", merged_size);  // 应输出 1000  
    return 0;  
}  

解析

  • 通过文件操作实现数据的持久化拆分与合并。
  • 需确保文件名生成逻辑与读取逻辑一致,避免遗漏或重复。

总结与扩展思考

通过本文的讲解,读者已掌握了 C 语言中数组拆分与合并的核心方法,包括按索引、元素值和动态条件拆分,以及基础合并与去重合并等技巧。这些技能在以下场景中尤为重要:

  • 数据分块处理:当数组规模较大时,分块操作可降低内存压力。
  • 多线程并行计算:将数组拆分后分配给不同线程处理,提升效率。
  • 文件存储优化:通过拆分数组到多个文件,避免单个文件过大。

进阶方向

  • 学习指针数组或二维数组,实现更复杂的分块逻辑。
  • 结合排序算法(如快速排序),在合并时实现有序合并。
  • 探索链表或动态数组(如 realloc)的使用,替代固定大小的数组。

掌握数组拆分与合并不仅是一项技术能力,更是理解 C 语言底层内存管理的重要途径。通过不断实践和优化,读者可以将这一技能灵活应用于实际开发中,提升代码的健壮性和性能。

最新发布