您的位置:首页 > 编程语言 > C语言/C++

C语言学习笔记分享之数组(冒泡排序)

2015-10-01 16:24 344 查看
//------------------整型变量修饰符

修饰符(int short long long long signed unsigned)

所有修饰符都是用来修整形

int 4

short %hd 2

long %ld 8

long long 8(32位)

用来改变一个整形变量的存数空间

用来改变整形变量的符号

signed 表示是有符号的(默认的就是有符号数)

unsigned 表示是一个无符号的数 (正数的取值范围会扩大一倍)
%u

//--------------------char类型数据存储

存储原理

char a='a' ----->取出'a'的ASCII码值,97,然后转换2进制,存储在一个字节中

注意事项

1)当把一个字符赋值给一个char类型变量,那么系统首先查这个字符所对应的ASCII码,然后把

这个ASCII值放到变量中

2)根据变量中存储的ASCII值,去查ASCII表中对应字符,然后把这个字符打印控制台上,整形和 字符型可以互相转换。

3)char z = '我'; char字节,一个中文字符占3字节(unicode表),所有char不可以存储中文

4)不支持多个字符,多个字符是字符串

char two = 'ac'; printf("%c\n",two);

//------------------1)输入一个小写字母,要求转换成大写输出

A 65 a 97 差了32

//------------------数组的基本概念及分类

把具有

相同类型

的若干

变量



有序

的形式组织起来。这些按序排列的

同类数据元素

的集合

称为

数组

在C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。

数组的几个名词

1)数组:一组具有相同数据类型的数据的有序的集合

2)数组元素:构成数组的数据。数组中的每一个数组元素具有相同的名称,不同的下标,可以作 为单个变量使用,所以也称为下标变量。

3)数组的下标:是数组元素的位置的一个索引或指示。

4)数组的维数:数组元素下标的个数。根据数组的维数可以将数组分为一维、二维、三维、多维 数组。

存储



类型不同划分

数值数组 字符数组 指针数组 结构数组

按维度分类

//----------------------一维数组

如果一个数组的所有元素都不是数组,那么该数组称为一维数组

定义

类型说明符 数组名[常量表达式];



//---------------------注意点

1) 数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相

同的。第一个数组元素的地址是数组所占内存块的首地址

2) 数组名的书写规则应符合标识符的书写规定。 命名规则:

1、只能由字母、数字、下划线组成

2、不能数字开头

3、不能与关键字重名

4、严格区分大小写

命名规范: 1、起一个有意义名字

2、驼峰标示

3) 数组名不能与其它变量名相同。

//注意点

4) 方括号中常量表达式表示数组元素的个数,如a[5]表示数组a有5个元素。但是其下标从0开始 计算。

5) 不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式。

c99不支持使用变量作为元素个数

6) 允许在同一个类型说明中,说明多个数组和多个变量。

//--------------------数组的初始化

类型说明符 数组名[常量表达式] = { 值, 值......值 };

其中在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔。

//--------------数组初始化的注意事项

1)指定元素的个数的同时,对所有的元素进行显式的初始化

int nums[5] = {1,2,3,4,5};

2)指定数组的元素个数,对数组进行部分显式初始化 定义的同时对数组进行初始化,没有显式初始化的元素,

那么系统会自动将其初始化为0

int num[5] = {1} ---> 1,0,0,0,0

3)不指定元素个数,定义的同时初始化,它是根据大括号中的元素的个数来确定数组的元素 个数

int nums[] = {1,2,3,5,6};

4)指定元素个数,同时给指定元素进行初始化

int nums[5] = {[4] = 3,[1]
= 2};

给数组赋值的方法除了用赋值语句对数组元素逐个赋值外

还可采用初始化赋值和动态赋值的方法

使用变量定义的数组不可以定义数组的同时对数组进行初始化

int a = 6;

int nums[a];

先定义后初始化

数组没有初始化,数组里面是垃圾数

数组如果部分初始化,剩下的会自动初始化为0,所以是0

先定义后初始化,后初始化的只会对部分元素初始化,其余依旧是垃圾数

int a[10];

a[1] = 10;

a[5]? //垃圾数

//------------------------------数组的引用

数组名[下标]

数组元素通常也称为下标变量。必须先定义数组,才能使用下标变量。

int a[3] = {1,2,3};

a[0] = 1;

a[1] = 2;

a[2] = 3;

a[3]?

正序输出

for (int i=0; i<10;
i++) {

printf("%d\t",a[i]); }

逆序输出

for (int j=9; j>=0;
j--) {

printf("%d\t",a[j]); }

数组的遍历 ---> 初始化

for (int i=0;i<len; i++) {

printf("请输入数组的第%d个值\n",i+1);

scanf("%d",&a[i]);

}

数组长度

定义数组

遍历



//----------------------------一维数组的存储方式

存储方式:

1)计算机会给数组分配一块连续的存储空间

2)数组名代表数组的首地址

从首地址位置,依次存入数组的第1个、第2个....、第n个元素

3)每个元素占用相同的字节数(取决于数组类型)

4)并且数组中元素之间的地址是连续。

一维数组的地址

在内存中,内存从大到小进行寻址,为数组分配了存储空间后,数组的元素自然的从上往下排列 存储,整个数组的地址为首元素的地址。

2)数组名存放的是数组的首地址

数组的首地址:数组的第一个元素首地址(第一个元素的第一个字节地址)

//-----------------------------计算数组长度

:sizeof(数组名) --> 总字节数

数组的长度 = 数组占用的总字节数 / 数组元素占用的字节数

//-----------------------------数组的越界问题

总结:

一个长度为n的数组,最大下标为n-1, 下标范围:0~n-1

1、约错了对象(访问自己不该访问存储空间)

2、程序崩溃

//-------------------------找最大值

定义变量

遍历存入数组

假设max = a[0]

比较

if(a[i]>max) max=a[i];



//-------------------------数组元素作为函数参数

数组用作函数参数有两种形式:

一种是把数组元素(下标变量)作为实参使用;

一种是把数组名作为函数的形参和实参

数组元素作函数实参,就是把数组元素的值传送给形参

数组名作为函数参数,就是把数组的地址传递过去

1)用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组

2)在C语言中,数组名除作为变量的标识符之外,数组名还代表了该数组在内存中的起始地址, 因此,当数组名作函数参数时,实参与形参之间不是"值传递",而是"地址传递",实参数组名将 该数组的起始地址传递给形参数组,两个数组共享一段内存单元,编译系统不再为形参数组分配存储单元。

3) 在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实 参。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。



注意点

1形参数组和实参数组的类型必须一致,否则将引起错误。

2形参数组和实参数组的长度可以不相同

3在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的个数。

4多维数组也可以作为函数的参数。在函数定义时对形参数组可以指定每一维的长度,也可省去 第一维的长度。

sizeof(地址) --> 地址都是占8个字节

//------------------冒泡排序

int a[5] = {1,7,5,3,2};

大数下沉,小数上浮

1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。

2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。

在这一点,最后的元素应该会是最大的数。

3)针对所有的元素重复以上的步骤,除了最后一个。

4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

for (int i=0; i<len-1;
i++)

//每趟排序都会确定一个数,所以需要再循环len-i次,但因为每次都是 //相邻的两个数进行比较,为了a[j+1]不越界,让j循环到len-i-1时停止。

for (int j=0; j<len-i-1;
j++) {



//------------------冒泡排序

外层 len - 1 里层 j < len - i - 1

大数下沉

1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。

2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。

在这一点,最后的元素应该会是最大的数。

3)针对所有的元素重复以上的步骤,除了最后一个。

4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

for (int i=0; i<len-1;
i++)

//每趟排序都会确定一个数,所以需要再循环len-i次,但因为每次都是 //相邻的两个数进行比较,为了a[j+1]不越界,让j循环到len-i-1时停止。

for (int j=0; j<len-i-1;
j++) {

//若arr[j]>arr[j+1] 若前一位比后一位元素大,则交换顺序

if (arr[j]>arr[j+1]) {

int temp = arr[j];

arr[j] = arr[j+1];

arr[j+1] = temp;

}

//------------------选择排序思想

外层 len-1 里层 j=i+1
j<len

int a[10]={23,12,4,67,20,100,21,45,3,28};

假设a[0]为最小,分别与a[1]
a[2] ... 比较, 在比较过程中,如果有元素的值比a[0]小,交换值

第一种方法

//每一趟都是拿着一个元素与后面其他元素进行比较,找出最小值

void selectSort1(int array[],int len){

// 1、确定需排序趟数

for (int i = 0 ; i < len - 1;
i++) { // 2、每一趟怎么处理

for (int j = i + 1; j < len; j++) {

if (array[i] > array[j]) {

int temp = array[i];

array[i] = array[j];

array[j] = temp;

}

}

}

}

第二种方法

void selectSort2(int arr[],int len){

int temp;

int min;

for (int i=0; i<len - 1;
i++) {

//每次假设第arr[i]是最小值 min = i;

min = i;

for (int j=i+1; j<len; j++) {

//找到最小的那个元素的下标

if (arr[j]<arr[min]) min=j;

}

//如果没有找到比arr[i]小的,就不用交换 if(i!=
min){

//将第i个元素和最小的元素交换 temp=arr[i];

temp = arr[i];

arr[i]=arr[min];

arr[min] = temp; }

}

//打印数组

for (int i=0; i<len; i++) {

printf("%d\t",arr[i]); }

}

//----- 冒泡:

外层for (int i=0;
i<len-1; i++)

内层for (int j=0;
j<len-i-1; j++)

//----- 选择排序:

外层for (int i=0;
i<len - 1; i++)

内层for (int j=i+1;
j<len; j++)

//------------------折半查找思想

思路: 先划分空间,再在其空间查找,取中间点,中间点往左,元素的值一定比中间值小,右边的值一定比中间值大.

判断 关键点 if (key>arr[mid]) {low = mid+1;}
//右半区查找

if (key<arr[mid]){high = mid - 1;} //左半区查找

找 12

low high

0 1 2 3 4
5 6 7 8
9

// 3 4
12 20
21 23
28 45
67 100

--------------------------------------------

1 low mid high

2 low mid high

3 low high

mid = (low + high) / 2 = 4;

a[4] = 21 > 12 --> high = mid - 1
---> 4 - 1 = 3

mid = (low + high)/2 = 1;

a[1] = 4 < 12 --> low = mid + 1 --> 1+1 = 2

mid = (2+3)/2 --> 2

a[2] = 12 == 12

//折半查找

int searchItem(int arr[],int len,int key){

//先要定义变量

int low=0,high=len-1,mid;

//循环

while (low<=high) {

// 计算 mid的位置

mid = (low+high)/2;

printf("mid = %d",mid);

// 判断 key a[mid],右半区查找

if (key>arr[mid]) {

// key > a[mid] low = mid +1;

low = mid+1;

}else if (key<arr[mid]){

// key < a[mid] high = mid -1;

high = mid - 1;

}else{

// key == a[mid] //return mid;

return mid;

}

}

// 下面是查找不到的情况

return -1;

}

//折半插入 不建议看

int insertItemLoc(int arr[],int len,int key){

//先要定义变量

int low=0,high=len-1,mid;

//循环

while (low<=high) {

// 计算 mid的位置

mid = (low+high)/2;

// 判断 key a[mid],右半区查找

if (key>arr[mid]) {

// key > a[mid] low = mid +1;

low = mid+1;

}else if (key<arr[mid]){

// key < a[mid] high = mid -1;

high = mid - 1;

}else{

// key == a[mid] //return mid;

return mid + 1;

}

}

// 下面是查找不到的情况

return low;

}

//---------------------二维数组

回顾://----------------------一维数组

如果一个数组的所有元素都不是数组,那么该数组称为一维数组

二维数组定义的一般形式是:

类型说明符 数组名[常量表达式1][常量表达式2]

二维数组定义注意事项:

1)数组名严格遵守标识符命名规范

2)二维数组名不能和变量同名 int a;

int a[2][3];

3)数组长度可以是常量,也可以是常量表达式

int a[2+1][3+3]; //相当于定义了int
a[3][5];

4)二维数组长度可以是宏定义

5)xcode编译器支持,C99标准不允许的写法

int x=3,y=4;

int a[x][y]; //相当于定义了int a[3][4];

6) 只定义不初始化,不可以省略长度

int arr[2][3] = {1,2,3,
---> {{第一行元素(一维数组)}

4,5,6} 第二行元素(一维数组)}}

2行3列

/*

| a[0] a[1] a[2] 第二维

-----------------------

a[0] | 1 2 3

a[1] | 4 5 6

第一维

*/

//--------------------二维数组初始化

二维数组可:

1)按行分段赋值

2)也可按行连续赋值。

1、定义同时初始化

2、先定义后初始化

数组名[行][列]

int a[5][6];

部分初始化,其余自动初始化为0

二维数组可以看作是由一维数组的嵌套而构成的。

二维数组初始化,不能省略第二维

int a[4][3] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12}};

/*

| a[0] a[1] a[2] 第二维

-----------------------

a[0] | 1 2 3

a[1] | 4 5 6

a[2] | 7 8 9

a[3] | 10 11 12

第一维

*/

//--------------------二维数组遍历

二维数组的元素也称为双下标变量

数组名[第一维下标][第二维下标]

数组名[行][列]

for外层循环控制行数

for内层循环控制列数

int score[5][3] = {{80,75,92},

{80,75,92},

{80,75,92},

{80,75,92},

{80,75,92}};

//--------------------二维数组存储

存储方式:

1)计算机会给二维数组分配一块连续的存储空间

2)数组名代表数组的首地址,从首地址位置,依次存入第1行、第2行、.....

3)每一行存储方式,从行首地址还是,依次存储行的第1个元素、第2个元素、第3个元素......

4)每个元素占用相同的字节数(取决于数组类型)

5)并且数组中元素之间的地址是连续。

arr == &arr[0] == arr[0] == &arr[0][0]

arr (数组名 指向第一行元素的地址)

&arr[0](第一行元素的地址)

arr[0](指向第一行第一个元素的地址)

&arr[0][0](第一个元素的地址)

int arr[3][4] = {{第一行3个元素(第一行一维数组)},
----> arr[0]

{第二行4个元素(第二行一维数组)},
----> arr[1]

{第三行4个元素(第三行一维数组)}};
----> arr[2]

arr是一个二维数组

总计占用字节数: sizeof(arr);

每行占用字节数: sizeof(arr[0]);

行数: 总字节数/每行字节数 --> sizeof(arr)/sizeof(arr[0]);

列数: 行数/每个类型 --> sizeof(arr[0])/sizeof(int)



//应用:求多人多门课平均成绩

//找最大值

//-------------------二维数组做函数参数

1、二维数组元素作为函数参数

二维数组元素作为函数参数,相当于变量的值传递过程。

2、二维数组名作为函数参数

二维数组名作为函数参数,相当于地址传递。

1)在被调用函数中对形参数组定义时可以指定所有维数的大小,也可以省略第一维的大小说明,

如:

void Func(int array[3][10]);

void Func(int array[][10]);

注意: 二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,

不合法的:

void Func(int array[][]);

void Func(int array[3][]);

一定不能省略第二维

2)实参数组维数可以大于形参数组

形参数组只取实参数组的一部分,其余部分不起作用。

3)类型长度要一致

//---------------------从键盘接收数据构成二维数组

输出

void printArray(int m,int n,int a[m]
){

for (int i = 0; i<m; i++) {

for (int j=0; j<n; j++) {

printf("%d\t",a[i][j]);

}

printf("\n");

}

}

输入

void initArray(int m,int n,int a[m]
){

for (int i=0; i<m; i++) {

for (int j=0; j<n; j++) {

printf("请输入第%d行第%d列元素赋值\n",i,j);

scanf("%d",&a[i][j]);

}

}

}

//---------------------《迷宫》

/*

| a[0] a[1] a[2] 第二维

-----------------------

a[0] | 1 2 3

a[1] | 4 5 6

a[2] | 7 8 9

a[3] | 10 11 12

第一维

######

#O##

# ## #

# # #

## #

######

*/

判断下一个位置是否是路,是路就让这两个值交换

假设往下走

//核心要知道小人的下一个位置是否是路

if (map[currentX-1][currentY]==street) {

//让小人开始移动,移动的核心:小人和路交换

char temp;

// map[currentX][currentY]; 小人当前位置

// map[currentX-1][currentY];小人的下一个位置

temp = map[currentX][currentY];

map[currentX][currentY] = map[currentX-1][currentY];

map[currentX-1][currentY] = temp;

//重新记录小人的当前位置

currentX--;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: