在程序中,经常需要对一批数据进行操作,例如,统计某个公司100个员工的平均工资。如果使用变量来存放这些数据,就需要定义100个变量,显然这样做很麻烦,而且很容易出错。这时,可以使用X[0]、X[1]、X[2]、…、X[99]表示这100个变量,并通过方括号中的数字来对这100个变量进行区分。
在程序设计中,使用X[0]、X[1]、X[2]、…、X[99]表示的一组具有相同数据类型的变量集合称为数组X,数组中的每一项称为数组的元素,每个元素都有对应的下标(n),用于表示元素在数组中的位置序号,该下标是从0开始的。
为了大家更好地理解数组,接下来,通过一张图来描述数组X[10]的元素分配情况。
从图中可以看出,数组X包含10个元素,并且这些元素是按照下标的顺序进行排列的。由于数组元素的下标是从0开始的,因此,数组X的最后一个元素为X[9]。
需要注意的是,根据数据的复杂度,数组下标的个数是不确定的。通常情况下,数组元素下标的个数也称为维数,根据维数的不同,可将数组分为一维数组、二维数组、三维数组、四维数组等。通常情况下,我们将二维及以上的数组称为多维数组。
数组的声明与变量的声明十分类似,只不过需要在变量名的后面添加一对方括号。如同下面,我们声明了一个可以包含 10 个整数的数组一样。
int intArray[10];
在这种形式下,你必须指明指明数组的大小,也就是方括号中的数值。或者你在初始化的时候使用大括号直接赋值,这样就不用直接指明数组的大小。
int intArray[]={1,2,3,4,5};
虽然没有指明数组的大小,但是其大小就是初始化的元素的数量。 C 语言中的数组一旦声明,其大小是不可以变化的。
在 C99 标准中,引入了一种新的方式来声明数组。就是使用变量。之前你可以使用常量来声明。但是不可以使用变量。这次。规则发生了变化。你可以用变量来声明。
int a=10, intArray[a];
int intArray[]={1,2,3,4,5};
int ages[3] = {4, 6, 9};
int intArray[10]={1,2,3,4,5};
int intArray[20]={1,2,3,4,5,[8]=8,9,10,11,[19]=19};
解析: 我们在这里声明了一个整数数组,可以容纳 20 个整数。前面 5 个位置,依次赋值 1、2、3、4、、5,这个时候我们跳过中间的 6 和 7 的位置,直接给第 8 个位置赋值为 8 ,然后后面的第 9 至 11 的位置依次赋值 9、10、11,最后再对第 19 个位置赋值为 19 。
int intArray[20]={0}; //那么会自动给20个空间都给一个默认值0
int nums[3];
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
如果定义数组后,没有初始化,数组中是有值的,是随机的垃圾数,所以如果想要正确使用数组应该要进行初始化
数组的使用和变量的使用基本一致,只不过你面对的是一个集合,而不是一个单一的元素。因此,你要访问其中的元素的话,就要使用变量名加索引位置的方式。
//定义数组并初始化
int ages[4] = {19, 22, 33, 13};
// 找到下标为0的元素, 赋值为 10
ages[0]=10;
// 取出下标为2的元素
int a = ages[2];
printf("a = %d", a);
int main() {
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
return 0;
}
因为数组在内存中占用的字节数取决于其存储的数据类型和数据的个数 ,数组所占用存储空间 = 一个元素所占用存储空间 * 元素个数(数组长度)所以计算数组长度可以使用如下方法 , 数组的长度 = 数组占用的总字节数 / 数组元素占用的字节数
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
printf("length = %d", length);//输出结果: 4
存储方式:
我们使用代码来看看数组内内部地址分配情况, 每个电脑可能都不一样
#include <stdio.h>
int main() {
int num=9;
printf("num = %p\n", &num); // cs = 000000000064FE1C
char cs[] = {'l', 'n', 'j'};
printf("cs = %p\n", &cs); // cs = 000000000064FE1D
printf("cs[0] = %p\n", &cs[0]); // cs[0] = 000000000064FE1D
printf("cs[1] = %p\n", &cs[1]); // cs[1] = 000000000064FE1E
printf("cs[2] = %p\n", &cs[2]); // cs[2] = 000000000064FE1F
int nums[] = {2, 6};
printf("nums = %p\n", &nums); // nums = 000000000064FE14
printf("nums[0] = %p\n", &nums[0]);// nums[0] = 000000000064FE14
printf("nums[1] = %p\n", &nums[1]);// nums[1] = 000000000064FE18
return 0;
}
数组越界导致的问题: 找错对象 , 程序崩溃
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:这句访问到了不属于cs2的内存,而是cs1的内存了
printf("cs1[0] = %d\n", cs1[0] ); //输出结果: 88
printf("cs1 = %p\n", &cs1); //cs1 = 000000000064FE1E E =15
printf("cs2 = %p\n", &cs2); //cs2 = 000000000064FE1B B=12+3=E(15)
printf("cs1[0] = %p\n", &cs1[0]); //cs1 = 000000000064FE1E
printf("cs2[3] = %p\n", &cs2[3]); //cs2 = 000000000064FE1E
十六进制满16进1 ,而A B C D E F 对应 11 , 12 , 13 , 14 , 15 , 16
在其他语言越界直接就停止执行了,但是在c语言中越界不会停止程序,而是操作越界后的内存,如果这个内存有人在用了,那么就完蛋了,可能就会出现未知的问题
数组可以作为函数的参数使用,数组用作函数参数有两种形式:
数组的元素作为函数实参,与同类型的简单变量作为实参一样,如果是基本数据类型, 那么形参的改变不影响实参
void change(int val){
val = 55;
}
int main(int argc, const char * argv[]){
int ages[3] = {1, 5, 8};
printf("ages[0] = %d\n", ages[0]);// 1
change(ages[0]); //数组作为实参传递
printf("ages[0] = %d", ages[0]);// 1
}
在C语言中,数组名除作为变量的标识符之外,数组名还代表了该数组在内存中的起始地址,因此,当数组名作函数参数时,实参与形参之间不是"值传递",而是"地址传递"实参数组名将该数组的起始地址传递给形参数组,两个数组共享一段内存单元, 系统不再为形参数组分配存储单元, 既然两个数组共享一段内存单元, 所以形参数组修改时,实参数组也同时被修改了
void change(int array[]){
array[0] = 88; //改变了传递进来数组的值
}
int main(int argc, const char * argv[]){
int ages[3] = {1, 5, 8};
printf("ages[0] = %d\n", ages[0]);// 1
change(ages);
printf("ages[0] = %d", ages[0]);// 88
}
数组名作函数参数的注意事项:
[]
里面只能写整型常量或者是返回整型常量的表达式或者变量int ages[3];
ages = {4, 6, 9}; // 报错
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
for (int i = length-1; i >= 0; i--) {
printf("ages[%d] = %d\n", i, ages[i]);
}
int a[10]={6,4,3,2,7,8,9,10,1,5};
int i,k,w;
for(i=0;i<9;i++)
{
for(k=0;k<9-i;k++)
{
if(a[k]>a[k+1])
{
w=a[k];
a[k]=a[k+1];
a[k+1]=w;
}
}
}
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
int arr[] = {1,2,3,5,67,8,9,33};
int min=arr[0],max=arr[0];
int len= sizeof(arr)/sizeof(int);
for(int i=0;i<len;i++){
if (min>arr[i]){
min = arr[i];
}
if (max<arr[i]){
max = arr[i];
}
}
printf("result: min = %d , max = %d \n",min,max);
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在 对一定范围内的整数排序 时,快于任何比较排序算法。
排序思路:
//待排序数组
int nums[5] = {3,5, 2, 0, 3};
// 用于排序数组,需要先找到最大值才行(数组长度=最大值+1)
int newNums[6] = {0};
// 计算待排序数组长度
int len = sizeof(nums) / sizeof(nums[0]);
// 遍历待排序数组
for (int i = 0; i < len; i++) {
// 取出待排序数组当前值
int index = nums[i];
// 将待排序数组当前值作为排序数组索引
// 将用于排序数组对应索引原有值+1
newNums[index] = newNums[index] + 1;
}
// 计算待排序数组长度
int len2 = sizeof(newNums) / sizeof(newNums[0]);
// 输出排序数组索引, 就是排序之后结果
for (int i = 0; i < len2; i++) {
for (int j = 0; j < newNums[i]; j++) {
printf("%i\n", i);
}
}
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
排序思路:
假设按照升序排序
#include <stdio.h>
// 交换两个元素的值, i/j需要交换的索引
void swapEle(int array[], int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 选择排序
void selectSort(int numbers[], int length) {
// 外循环为什么要-1? ,因为最后一位不用比较, 也没有下一位和它比较, 否则会出现错误访问
for (int i = 0; i < length; i++) {
for (int j = i; j < length - 1; j++) {
// 1.用当前元素和后续所有元素比较
if (numbers[i] > numbers[j + 1]) {
// 2.一旦发现当前元素大于后续元素那么就交换位置(从小到大排序)
swapEle(numbers, i, j + 1);
}
}
}
}
int main(int argc, const char *argv[]) {
int nums[5] = {3,5, 2, 0, 3};
int len=sizeof(nums) / sizeof(nums[0]);
selectSort(nums, len);
for (int j = 0; j <len; j++) {
printf("nums=%d\n", nums[j]);
}
return 0;
}
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
排序思路:
假设按照升序排序
#include <stdio.h>
// 交换两个元素的值, i/j需要交换的索引
void swapEle(int array[], int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
int main(int argc, const char *argv[]) {
int nums[5] = {3, 1, 2, 0, 3};
// 0.计算待排序数组长度
int len = sizeof(nums) / sizeof(nums[0]);
// 1.从第一个元素开始依次取出所有用于比较元素
for (int i = 1; i < len; i++) {
// 2.遍历取出前面元素进行比较
for (int j = i; j > 0; j--) {
// 3.如果前面一个元素大于当前元素,就交换位置
if (nums[j - 1] > nums[j]) {
swapEle(nums, j, j - 1);
} else {
break;
}
}
}
for (int j = 0; j <len; j++) {
printf("nums=%d\n", nums[j]);
}
return 0;
}
1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
排序思路:
// 交换两个元素的值, i/j需要交换的索引
void swapEle(int array[], int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
int main() {
// 待排序数组
int nums[5] = {3, 1, 2, 0, 3};
// 0.计算待排序数组 长度
int len = sizeof(nums) / sizeof(nums[0]);
// 2.计算步长
int gap = len / 2;
do {
// 1.从第一个元素开始依次取出所有用于比较元素
for (int i = gap; i < len; i++) {
// 2.遍历取出前面元素进行比较
int j = i;
while ((j - gap) >= 0) {
printf("%i > %i\n", nums[j - gap], nums[j]);
// 3.如果前面一个元素大于当前元素,就交 换位置
if (nums[j - gap] > nums[j]) {
swapEle(nums, j, j - gap);
} else {
break;
}
j--;
}
}
// 每个小数组排序完成, 重新计算步长
gap = gap / 2;
} while (gap >= 1);
for (int j = 0; j <len; j++) {
printf("nums=%d\n", nums[j]);
}
return 0;
}
快速排序是一个非常优秀且常用的排序算法,尤其是在大数据的排序应用中,最为常见。
虽然“快速”,但逻辑也是最复杂,最难理解。本算法采用分治的思想,涉及递归和函数调用,效率是极高的。到底什么是“分治”?所谓分治,就是以一个数为基准,将序列中的其他数往它两边“扔”。
以从小到大排序为例,比它小的都扔到左边,比它大的都扔到右边。然后两边分别重复这种操作,不停地分,直到每个分区的基准数的左边或右边都只剩一个数为止。此时排序完成。
实际上,它是对冒泡排序的一种改进,是冒泡的升级版。这种改进就体现在根据分割序列的基准数,跨越式的进行交换。正是由于这种跨
越式,使得元素移动的范围变大了,而不是像冒泡一样“一格一格”地“爬行”。So,效率提升 n 个档次。
下面的快速排序是最优的快速排序,去掉了多余的交换数据的步骤:
void MyQuickSort(int A[], int low, int high) {
int i = low;
int j = high;
int key = A[low];
if (low >= high){// 终止的条件
return;
}
while (low < high){// while结束表示比较了一轮
while (low < high && key <= A[high]){// 往前找
high--;
}
if (key > A[high]){// 交换
A[low] = A[high]; // 直接赋值,不用交换了
low++;
}
while (low < high && key >= A[low]){// 向后找
low++;
}
if (key < A[low]) {// 交换
A[high] = A[low]; // 直接赋值,不用交换了
high--;
}
}
/* 查找完一轮后key值归位,不用比较一次就交换一次 此时key左右分为两部分*/
A[low] = key;
MyQuickSort(A, i, low - 1); // 同样方式对分出来的左边部分排序
MyQuickSort(A, low + 1, j); // 同样方式对分出来的右边部分排序
}
int main(void) {
int i;
int A[] = {489, 45, -8, 48, -489, 18, 1, 0, 987, 0, 231, 14, 95, -78, -2, 0, 2500, 798, 32232, 48512, 465, 98};
int n = sizeof(A) / sizeof(A[0]); // 算一下数组长度
MyQuickSort(A, 0, n - 1); // n-1是最大元素的下标
printf("排序的结果为\n");
for (i = 0; i < n; i++) {
printf("%d\x20", A[i]);
}
printf("\n");
return 0;
}
在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败 ,可以说这种算法查询数据量越多速度就越快 ,
如果列表包含40亿个数字,最多需查询32次
有没有比二分查询(O(log n)))更快的算法?当然有: 哈希查找算法(O(1)) 但是这种算法就好比是门牌号直接就定位到你家里了, 可以实现无论多少数据最多一次查询,当然也会遇到到哈希冲突的问题,下面我们演示二分查询怎么写:
int findKey(int values[], int length, int key) { // 定义一个变量记录最小索引
int min = 0;
// 定义一个变量记录最大索引
int max = length - 1;
// 定义一个变量记录中间索引
int mid = (min + max) * 0.5;
while (min <= max) {
// 如果mid对应的值 大于 key, 那么max要变小
if (values[mid] > key) {
max = mid - 1;
// 如果mid对应的值 小于 key, 那么min要变
} else if (values[mid] < key) {
min = mid + 1;
} else {
return mid;
}
// 修改完min/max之后, 重新计算mid的值
mid = (min + max) * 0.5;
}
return -1;
}
int main(void) {
int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int len = sizeof(array) / sizeof(array[0]);
int index = findKey(array, len, 6);
printf("%d", index);
return 0;
}
实现思路:
将二进制、八进制、十进制、十六进制所有可能的字符都存入数组利用按位与运算符和右移依次取出当前进制对应位置的值利用取出的值到数组中查询当前位输出的结果将查询的结果存入一个新的数组, 当所有位都查询存储完毕, 新数组中的值就是对应进制的值, 代码实现:
void total(int num, int base, int offset) {
//1.定义表用于查询结果
char cs[] = {'0', '1','2', '3', '4','5','6','7', '8','9', 'a', 'b', 'c', 'd', 'e', 'f'};
//2.定义保存结果的数组
char rs[32];
//计算最大的角标位置
int length=sizeof(rs) / sizeof(char);
int pos=length;//8
while (num != 0) {
int index=num&base;
rs[--pos]=cs[index];
num=num >> offset;
}
for (int i=pos; i<length;i++){
printf("%c", rs[i]);
}
printf("\n");
}
void toBinary(int num) {
total(num, 1, 1);
}
void tooct(int num) {
total(num, 7, 3);
}
void toHex(int num) {
total(num, 15, 4);
}
int main() {
toBinary(9);
tooct(9);
toHex(15);
return 0;
}
所谓二维数组就是多个一维数组叠加,从而构成二维数组. 可以说二维数组是特殊的一维数组的集合。
它能让你完成一些仅仅依靠一维数组没有办法完成的事情。让很多事情完成的更为简单。,比如下面这些都是二维数组才能完成的
语法: data_type array_name[size1][size2];
列: int twodimen[4][3];
这里,4是行号,3是列号。
这种赋初值的方法比较直观,把每行看作一个元素,按行赋初值。
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
也可以只对部分元素赋初值,没有初始化的元素默认补充数据类型的默认值
int a[3][4] = { {5}, {9}};
也可以将所有数据写在一个花括号内,内部自动识别行(不推荐)
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
如果在定义数组时就初始化,行可以不指定,但列不能省,系统会自动推导(不推荐)
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};//等价
int b[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};//等价,但这种省略的写法几乎不用,因为可读性差
二维数组默认初始化
int a[3][4]={0}; //3行4列每个元素默认都是0
int a[][4]={0}; //默认为1行4列每个元素默认都是0
int arr[4][3] = { { 1,2,3 },{ 2,3,4 },{ 3,4,5 },{ 4,5,6 } };
int row= sizeof(arr)/sizeof(arr[0]);
int col=sizeof(arr[0])/sizeof(arr[0][0]);//列
printf("%d\n",row);//4
printf("%d\n",col);//3
遍历二维数组无非就是先取出二维数组中得一维数组, 然后再从一维数组中取出每个元素的值
#include <stdio.h>
int main() {
int arr[4][3] = { { 1,2,3 },{ 2,3,4 },{ 3,4,5 },{ 4,5,6 } };
int row= sizeof(arr)/sizeof(arr[0]);
int col=sizeof(arr[0])/sizeof(arr[0][0]);//列
for (int i = 0;i<row;i++) {
for (int j = 0;j<col;j++) {
printf("arr[%d] [%d] = %d \n", i, j, arr[i][j]);
}
}
return 0;
}
数组作为函数的形参是无法获取到数组的行长度的
void change(char cs[4][3]){
int row= sizeof(cs)/sizeof(cs[0]); //行
int col=sizeof(cs[0])/sizeof(cs[0][0]);//列
printf("%d\n",row);//2 行数不对
printf("%d\n",col);//3
}
int main() {
char cs[4][3] = {0};
int row= sizeof(cs)/sizeof(cs[0]); //行
int col=sizeof(cs[0])/sizeof(cs[0][0]);//列
printf("%d\n",row);//4
printf("%d\n",col);//3
change(cs);
return 0;
}
解决办法: 在函数外部将行数传递进函数里
二维数组作为形参错误写法
void test(char cs[2][]) // 错误写法
void test(char cs[2][3]) // 正确写法
void test(char cs[][3]) // 正确写法
值传递: 和一维数组差不多,传入的参数是基本类型是还是数组类型,如果是基本类型在函数中修改形参不会影响实参
#include <stdio.h>
void change(char ch){
ch = 'n';
}
int main() {
char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'}};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0][0]); //传入的是实际参数
printf("cs[0][0] = %c\n", cs[0][0]); // a
return 0;
}
地址传递 和一位数组一样, 传入的参数是基本类型还是数组类型,如果是数组类型在函数中修改形参会影响实参
#include <stdio.h>
void change(char ch[][3]){
ch[0][0] = 'n';
}
int main() {
char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'}};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs);//传进去是数组
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
#include <stdio.h>
void change(char ch[]) {
ch[0] = 'n';
}
int main() {
char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'}};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0]);//传进去是数组
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
点赞 -收藏-关注-便于以后复习和收到最新内容有其他问题在评论区讨论-或者私信我-收到会在第一时间回复感谢,配合,希望我的努力对你有帮助^_^免责声明:本文部分素材来源于网络,版权归原创者所有,如存在文章/图片/音视频等使用不当的情况,请随时私信联系我。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://huanmin.blog.csdn.net/article/details/125719014
内容来源于网络,如有侵权,请联系作者删除!