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;
}
解析:
- 通过判断元素是否为偶数,将原数组拆分为两个子数组。
- 需预先为
even
和odd
分配足够空间,或动态计算元素数量。
方法三:按条件拆分(动态内存分配)
当子数组的大小不确定时,可使用动态内存分配(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 语言底层内存管理的重要途径。通过不断实践和优化,读者可以将这一技能灵活应用于实际开发中,提升代码的健壮性和性能。