在C语言中数组是一种基本的数据结构,用于存储相同类型的数据元素的集合,数组中的每个数据元素都可以通过索引(或下标)来访问。
索引通常是从0开始的整数,在C语言中,数组可以是任何数据类型,包括整型、浮点型、字符型等。
一维数组
一维数组是最简单的数组形式,它只有一个维度,一维数组可以用来存储一系列的值,例如整数序列或字符序列。
#include <stdio.h>
int main()
{
// 定义整数数组,数组内包含五个整数
int arr[5] = {
1, 2, 3, 4, 5};
int *ptr = arr;
int i;
// 定义字符数组,数组内包含五个字符
char arr2[5] = {
'a', 'b', 'c', 'd', 'e'};
// 输出整数数组
printf("整数数组:\n");
for (i = 0; i < 5; i++)
{
printf("%d \n", *ptr);
ptr++;
}
// 输出字符数组
printf("字符数组:\n");
for (i = 0; i < 5; i++)
{
printf("%c \n", *(arr2 + i));
}
return 0;
}
运行结果如下:
二维数组
二维数组可以看作是“数组的数组”,即数组的每个元素也是一个数组,二维数组常用于表示矩阵或表格数据。
#include <stdio.h>
int main()
{
int arr[3][3]; // 声明一个3x3的数组
// 输入数组
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("为数组%d输入一个值[%d][%d]: ", i+1, i, j);
scanf("%d", &arr[i][j]);
}
}
// 打印数组
for (int i = 0; i < 3; i++)
{
printf("数组%d的内容:\n", i+1);
for (int j = 0; j < 3; j++)
{
printf("\t");
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
运行结果如下
字符数组
字符数组用于存储字符串。在C语言中,字符串是通过字符数组实现的,其中数组的最后一个元素是'\0'
(空字符),表示字符串的结束。
#include <stdio.h>
int main()
{
char array[10]; // 声明一个字符数组
printf("输入一串字符: ");
scanf("%s", array);
printf("输入字符为: %s\n", array);
return 0;
}
运行结果如下:
如果输入的字数数量超出设定限制,会产生以下报错:
多维数组
多维数组是数组的数组的数组…等等。虽然常见的多维数组是二维数组,但C语言也支持更高维度的数组。
#include <stdio.h>
int main() {
int array[3][3][3];
int value;
printf("请输入3x3x3三维数组的元素:\n");
// 使用三层嵌套循环接受用户输入
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
printf("请输入元素[%d][%d][%d]: ", i, j, k);
scanf("%d", &value);
array[i][j][k] = value;
}
}
}
printf("您输入的三维数组是:\n");
// 使用三层嵌套循环打印数组
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
printf("%d ", array[i][j][k]);
}
printf("\n");
}
printf("\n");
}
return 0;
}
代码运行结果如下:
数组的排序方法
数组排序是将数组中的元素按照某种顺序排列的过程,虽然在C语言中没有内置的排序函数,但可以使用各种算法来排序数组,例如冒泡排序、选择排序、插入排序、快速排序等。这些排序算法各有特点,适用于不同的场景和数据集,在选择排序算法时,需要考虑数据的特性、排序的稳定性、时间复杂度和空间复杂度等因素。
这里我先用Python生成了100个1~1000的正整数并保存到CSV文件中,随后使用使用排序方法来对数组进行排序
import csv
import random
random_numbers = [random.randint(1, 1000) for _ in range(100)]
with open('test.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Random Number'])
writer.writerows([[num] for num in random_numbers])
冒泡排序(Bubble Sort)
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,随后该数列排序完成。这个算法的名字由来是因为越小或越大的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序适用于数据量较小的情况,因为其平均和最坏情况下的时间复杂度都是O(n^2)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 定义冒泡排序函数
void bubbleSort(int arr[], int n) {
int i, j, temp;
for (i = 0; i < n-1; i++) {
for (j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
bubbleSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
打开排序好的CSV文件,可以看到排序成功:
选择排序(Selection Sort)
选择排序是一种简单直观的排序算法,它的工作原理是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾,以此类推,直到所有元素均排序完毕。
同样适用于数据量较小的情况,因为其时间复杂度也是O(n^2)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 定义选择排序函数
void selectionSort(int arr[], int n) {
int i, j, min_idx, temp;
for (i = 0; i < n-1; i++) {
min_idx = i;
for (j = i+1; j < n; j++) {
if (arr[j] < arr[min_idx]) {
min_idx = j;
}
}
if (min_idx != i) {
temp = arr[i];
arr[i] = arr[min_idx];
arr[min_idx] = temp;
}
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
selectionSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
插入排序(Insertion Sort)
插入排序的算法通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,找到排序的位置后,需要将已排序元素逐步向后挪位,为新元素提供插入空间。
适用于数据量较小或基本有序的情况,时间复杂度在最好情况下是O(n),最坏情况下是O(n^2)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 定义插入排序函数
void insertionSort(int arr[], int n) {
int i, key, j;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
// 将arr[i]插入到已排序的序列arr[0...i-1]中的正确位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
insertionSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
快速排序(Quick Sort)
快速排序在平均状况下,排序n个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。快速排序使用分治法(Divide and Conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
适用于数据量较大的情况,时间复杂度在平均和最坏情况下分别是O(nlogn)和O(n^2)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 定义快速排序函数
int partition(int arr[], int low, int high);
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
// 递归地对划分后的两个子数组进行快速排序
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// 用于快速排序的划分函数
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 选择最后一个元素作为基准
int i = (low - 1); // 指向较小元素的索引
for (int j = low; j <= high - 1; j++) {
// 如果当前元素小于或等于基准
if (arr[j] <= pivot) {
i++; // 增加较小元素的索引
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 交换arr[i + 1]和arr[high]
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1); // 返回基准的正确位置
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
quickSort(numbers, 0, i - 1);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
归并排序(Merge Sort):
归并排序是一种分治算法。它将数组分成两半,对每一半进行递归排序,然后将排序好的两半合并在一起,归并排序在最好、最坏和平均情况下的时间复杂度都是O(n log n)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 归并排序的辅助函数,用于合并两个已排序的子数组
void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
// 创建临时数组
int L[n1], R[n2];
// 拷贝数据到临时数组
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
// 合并临时数组到原数组
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
// 拷贝L[]的剩余元素
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
// 拷贝R[]的剩余元素
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
// 归并排序函数
void mergeSort(int arr[], int l, int r) {
if (l < r) {
// 找到中间点
int m = l + (r - l) / 2;
// 递归排序两半
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
// 合并已排序的两半
merge(arr, l, m, r);
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
mergeSort(numbers, 0, i - 1);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
堆排序(Heap Sort):
堆排序利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于或大于它的父节点,堆排序在最好、最坏和平均情况下的时间复杂度都是O(n log n)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 用于调整堆的函数
void heapify(int arr[], int n, int i) {
int largest = i; // 初始化最大元素为根
int l = 2 * i + 1; // 左子节点
int r = 2 * i + 2; // 右子节点
// 如果左子节点大于根节点时更新最大元素为左子节点
if (l < n && arr[l] > arr[largest])
largest = l;
// 如果右子节点是目前最大的数值时更新最大元素为右子节点
if (r < n && arr[r] > arr[largest])
largest = r;
// 如果最大元素不是根节点时交换根和最大元素
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// 递归地调整受影响的子树
heapify(arr, n, largest);
}
}
// 堆排序函数
void heapSort(int arr[], int n) {
// 从最后一个非叶子节点开始,为每个节点执行heapify
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// 一个个从堆顶取出元素
for (int i = n - 1; i >= 0; i--) {
// 移动当前根到数组的末尾
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// 调用heapify在减少的堆上
heapify(arr, i, 0);
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
heapSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
希尔排序(Shell Sort):
希尔排序是插入排序的一种更高效的改进版本。它通过将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。希尔排序的时间复杂度与所选取的增量序列有很大关系。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
// 希尔排序函数
void shellSort(int arr[], int n) {
// 初始步长
for (int gap = n / 2; gap > 0; gap /= 2) {
// 对每个子列表进行插入排序
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j;
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = temp;
}
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
shellSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
计数排序(Counting Sort):
计数排序是一种非比较型排序算法,它的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。计数排序在数据范围不是很大的时候非常高效,但是它的空间复杂度较高,且不适用于数据范围很大的情况。
适用于数据范围不大的情况,时间复杂度是O(n+k),其中k是数据的范围。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
#define MAX_VALUE 10000 // 假设数值的最大值为10000
// 计数排序函数
void countingSort(int arr[], int n) {
int output[n]; // 输出数组
int count[MAX_VALUE + 1]; // 计数数组,大小为最大值+1
int i, j;
// 初始化计数数组
for (i = 0; i <= MAX_VALUE; i++) {
count[i] = 0;
}
// 计算每个元素的个数
for (i = 0; i < n; i++) {
count[arr[i]]++;
}
// 更改count[i],现在包含实际位置信息
for (i = 1; i <= MAX_VALUE; i++) {
count[i] += count[i - 1];
}
// 构建输出数组
for (i = n - 1; i >= 0; i--) {
output[count[arr[i]] - 1] = arr[i];
count[arr[i]]--;
}
// 将排序后的数据复制回原数组
for (i = 0; i < n; i++) {
arr[i] = output[i];
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
countingSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}
桶排序(Bucket Sort):
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序的时间复杂度为O(n+k),其中n为待排序记录数,k为桶的数量。
适用于数据分布均匀的情况,时间复杂度在平均和最坏情况下分别是O(n+k)和O(n^2),其中k是桶的数量。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_NUMBERS 100 // 定义读取100个数值
#define MAX_VALUE 10000 // 假设数值的最大值为10000
#define BUCKET_SIZE 10 // 每个桶的大小
// 桶排序函数
void bucketSort(int arr[], int n) {
int i, j;
int bucketCount = (int)ceil((double)MAX_VALUE / BUCKET_SIZE) + 1; // 计算桶的数量
int buckets[bucketCount][BUCKET_SIZE]; // 创建桶数组
int bucketElements[bucketCount]; // 记录每个桶中的元素数量
// 初始化桶
for (i = 0; i < bucketCount; i++) {
bucketElements[i] = 0;
for (j = 0; j < BUCKET_SIZE; j++) {
buckets[i][j] = 0;
}
}
// 将数组元素分配到各个桶中
for (i = 0; i < n; i++) {
int bucketIndex = (int)floor((double)arr[i] / BUCKET_SIZE);
buckets[bucketIndex][bucketElements[bucketIndex]] = arr[i];
bucketElements[bucketIndex]++;
}
// 对每个桶进行排序,这里简单使用插入排序
for (i = 0; i < bucketCount; i++) {
for (j = 1; j < bucketElements[i]; j++) {
int temp = buckets[i][j];
int k = j - 1;
while (k >= 0 && buckets[i][k] > temp) {
buckets[i][k + 1] = buckets[i][k];
k--;
}
buckets[i][k + 1] = temp;
}
}
// 合并桶到原数组
int index = 0;
for (i = 0; i < bucketCount; i++) {
for (j = 0; j < bucketElements[i]; j++) {
arr[index++] = buckets[i][j];
}
}
}
int main() {
FILE *fp, *fp_out;
int numbers[MAX_NUMBERS];
int i = 0;
// 打开python生成的csv文件
fp = fopen("test.csv", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取数据
while (fscanf(fp, "%*[^0-9]%d", &numbers[i]) != EOF && i < MAX_NUMBERS) {
i++;
}
fclose(fp);
// 开始排序
bucketSort(numbers, i);
// 写入新的CSV文件
fp_out = fopen("OjbK.csv", "w");
if (fp_out == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp_out, "Sorted Number\n");
for (int j = 0; j < i; j++) {
fprintf(fp_out, "%d\n", numbers[j]);
}
fclose(fp_out);
return 0;
}