注意:要了解c语言的函数库,会使用里面的函数,如math.h,stdlib.h库, rand()函数等
计算机元素
(1)机器语言:机器指令的集合
机器指令:计算机能够识别的二进制代码
(2)符号语言(汇编语言):一些英文字母和数字表示一个指令,汇编程序转义为机器语言
代真(汇编):转换过程
(3)高级语言
编译程序的软件把源程序转换为目标程序
高级语言发展阶段(了解)
(1)非结构化的语言
(2)结构化语言
(3)面向对象的语言
C语言的主要特点(掌握)
(1)语言简洁、紧凑、使用方便、灵活(2)运算符丰富
(3)数据类型丰富
(4)具有结构化的控制语句
(5)语法限制不太严格,程序设计自由度大
(6)C语言允许直接访问物理地址,能进行位操作,能实现汇编语言大部分功能,直接对硬件操作
(7)可移植性强
(8)生成目标代码质量高,程序执行效率高
C语言程序结构(掌握)
(1)一个程序由一个或多个源程序文件组成
(2)源程序:
1)预处理命令:#include
2)全局变量:函数之外数据声明
3)函数定义
(3)函数是C程序的主要组成部分,函数是C语言的基本单位
(4)一个函数包含两个部分
1)函数首部:函数名,函数类型,函数属性,函数参数名,参数类型
2)函数体:声明部分和执行部分
(5)程序总是从main函数开始执行
(6)程序中要求计算机的操作是由函数中的C语句完成的
(7)程序应当包含注释
C语言运行步骤(掌握)
源程序->目标程序->可执行程序
(1)上机输入和编辑源程序 后缀名.c
(2)编译,用预处理编译器对预处理指令编译预处理
作用:检查源程序是否有误
注:编译系统编译时自动包含预编译和正式编译
(3)进行连接处理,得到后缀.obj,将编译后得到的所有目标文件(模块)连接装配,在与库函数连接成一个整体,生成可供计算机执行的目标程序,称为可执行文件
注:以上连接的工作由一个称为“连接编辑程序”的软件实现
其中实现表示操作流程,虚线表示文件的输入输出
集成开发环境(IDE):把程序的编辑、编译、连接、运行
程序分析任务
(1)问题分析
(2)设计算法
(3)编写程序
(4)对源程序进行编辑、编译、连接
(5)运行程序、分析结果
(6)编写程序文
结构化程序设计
(1)自顶向下
(2)逐步细化
(3)模块化设计
(4)结构化编码
源文件包含若干个函数、预处理指令以及全局变量声明部分 ,一个函数包含声明部分和执行部分
控制语句:9种
(1)控制语句
(2)函数调用语句:一个函数调用加一个分号
(3)表达式语句:一个表达式加一个分号构成
赋值语句:赋值表达式后加分号,注:x+y;合法,但无实际意义
(4)空语句:只有分号
(5)符合语句:用{}把一些语句和声明括起来成复合语句(又称语句块)
赋值语句(掌握)
赋值表达式:标量 赋值运算符 表达式 a=3*5
注:占字节多的类型变量转为少的可能数据会失真,且赋值要在类型允许的数值范围内
赋初值时不能写成 int a=b=c=3;形式
printf中"%7.2lf":指定数据占7列,其中小数占2列
好处:(1)通过实际需要输出小数位数(2)小数点对齐,数据整齐美观
C语言本身不提供输入输出,输入输出来自C语言标准库函数实现
putchar(输出字符)getchar(输入字符) printf(格式输出)scanf(格式输入)puts(输出字符串) gets(输入字符串)
要在程序文件的开头用预处理指令#include 把有关头文件放在本程序中
#include <>:编译系统从存放c编译系统的子目录中去找所要包含的文件,称为标椎方式
#include"" :编译系统现在用户的当前目录中寻找要包含的文件,若找不到,在按标椎方式查找。若在其他目录,写出文件路径(如#include “C:\temp\file.h”)
printf函数一般格式:printf(“格式控制”,输出表列);
“格式控制”:格式控制字符串,简称格式字符串
格式声明:"%d"和格式字符组成
普通字符:原样输出
输出列表:需要输出的数据,常量,变量,表达式
printf函数一般形式:printf(参数1,参数2…,参数n)
格式字符
(1)int%d long int%ld long long int%lld
(2)char %c 输出的数字超过范围则取范围内的数转换 例:如下图%c 取后八位输出y
(3)float,double,long double
%f实数整数部分全部输出,小数部分后六位
%m.nf:右对齐,宽度m列,小数占n列
float:6位有效数字,double:15位有效数字
%-m.nf:左对齐
(4)字符串 %s
(5)实数 %e指数形式输出 默认小数位数6位,指数部分5列,共13列
(6)八进制:%o不带符号 十六进制%x
注:除X,E,G,F外,其他格式字符必须用小写
输出‘%’需要连续两个’%'表示
scanf用法
(1)scanf一般形式scanf(格式控制,地址表列)
地址表列:若干个地址组成的表列,可以是变量的地址,或字符串首地址
字符输入输出函数
1.putchar(a): 输出一个字符
2.char a = getchar(): 输入一个字符
while do…while… for(; ; )
1 | 0 | 0 | 0 |
---|---|---|---|
2 | 0 | 0 | 0 |
3 | 0 | 0 | 0 |
(4)可以只对某几行赋值 int a[3][4]={{1},{5,6}}; int a[3][4]={{1},{},{9}}; | |||
1 | 0 | 0 | 0 |
– | – | – | – |
5 | 6 | 0 | 0 |
0 | 0 | 0 | 0 |
(5)如果对全部元素都赋初值(即提供全部初始数据),则定义数组对第一维的长度可以不指定,但第二维长度不能省 | |||
int a[][4]={1,2,3,4,5,6,7,8,9}; | |||
int a[3][4]={{1,2,3,4},{5,6,7,8},{9}}; |
模块化程序设计
一个C程序可由一个主函数和若干个其他函数构成
(1)一个C程序:一个或多个程序模块组成,每一个程序模块作为一个源程序文件。
注:较大的程序一般按分类放在若干个源程序文件中,由若干个源程序文件组成一个C程序,一个源程序文件可以为多个C程序共用
(2)一个源程序文件:一个或多个函数以及其他相关内容(指令、数据声明与定义等)
注:程序编译是以源程序文件为一个编译单位
(3)程序执行从main函数开始
(4)每个函数相互平行独立,不能嵌套定义,且main函数只能被系统调用
(5)按用户分类:库函数;用户自定义函数
(6)函数形式分类:无参;有参
函数的定义:先定义后使用
(1)指定函数名
(2)指定函数类型
(3)指定函数参数名和类型
(4)指定函数功能
定义函数的方法
(1)定义无参函数
(2)定义有参函数
(3)定义空函数
调用函数
形式:函数名(实参表列)
(1)函数调用语句:即单独调用max(1,2)
(2)函数表达式:调用出现在表达式中c=max(1,2);
(3)函数参数:函数作为另一个函数的参数 如max(max(1,2),3);
形参:定义函数中的参数
实参:调用函数中的参数
虚实结合:函数过程中发生的实参与形参键的数据传递
函数调用过程
(1)形参在函数未调用时不占内存存储单元;发生调用时,形参会被临时分配空间
(2)实参传值给形参,对形参值进行处理,return返回值到主调函数,结束调用,形参释放
注:形参和实参地址不同不会相互影响,只能将实参的值赋值给形参,单向传递
函数值说明
(1)return 获取 return (a);或return a;
(2)函数值类型要与return表达式类型一致,函数类型决定返回值类型
调用函数的声明和函数原型
(1)调用的函数必须已定义
(2)函数的首行称为函数原型
函数类型种类
(1)函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2,…,参数类型n 参数名n);
(2)函数类型 函数名(参数类型1,参数类型2,…,参数类型n);
函数的调用
(1)嵌套调用:在调用函数中,又调用其他函数
(2)递归调用:直接或间接地调用该函数本身
数组作为函数参数
(1)数组元素可以做函数实参但不能用作形参。 在用数组元素作函数实参时,把实参的值传递给形参,是“值传递”方式。数据传递的方向是从实参传到形参,单向传递。
(2)数组名做函数参数(实参和形参)
注:数组名做函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。
注:数组形参的大小无任何作用,因为只是将实参的首地址传给形参数组名,并不检查形参的大小,同时如果改变形参值,实参也会改变
(3)多维数组作形参,除第一维外,其他维度大小不能省略,因为二维数组是由若干个一维数组组成,在内存中数组按行存放,必须确定一行包含几个元素
例:实参 int score[5][10] 形参 int array[][10]或int array[8][10]
局部变量
(1)在函数的开头定义
(2)在函数的复合语句内定义:称分程序(程序块)
注:形式参数也是局部变量
全局变量
也称外部变量:函数之外定义的变量
注:(1)全局变量习惯第一个字母大写
(2)变量名相同时:局部变量优先级大于全局变量
(3)建议不必要使用全局变量原因:
1)全部执行过程中都占用空间
2)使函数通用性降低(文件移植变量冲突)
3)降低代码清晰性
代码要求:高内聚低耦合,移植性好,可读性高
变量存储方式和生存期
(1)变量存储的两种方式:静态存储方式和动态存储方式
静态存储:程序运行期间由系统分配固定的存储空间方式
动态存储:在程序运行期间根据需要进行动态分配存储空间的方式
存储空间:1)程序区 2)静态存储区:全局变量,开始执行程序分配,程序结束完毕释放 3)动态存储区:函数形式参数;自动变量;函数调用时的现场保护和返回地址。动态分配和释放空间,所以两次调用同一函数地址不一定相同
数据存储类别
自动的(auto):函数调用结束后自动释放,可省略auto
静态的(static):函数调用后不会消失而继续保留,即其占用的存储单元不释放,再次调用时该变量已有值
寄存器的(register):将局部变量存储在CPU的寄存器中
外部的(extern):外部变量声明,表示将外部变量的作用域扩展到此位置 extern int A,B,C;或extern A,B,C;
注:auto static register不能单独使用
全局变量的存储类别
(1)全局变量都是存放在静态存储区汇总
(2)外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始到程序文件的末尾
(3)编译遇到extern,先在文本文件中找外部变量的定义,找不到再到连接时从其他文件中找外部变量的定义,还是找不到报错
(4)将外部变量的作用域限制在本文件中,使用static声明,称为静态外部变量
注:局部变量的类型声明和全局变量的类型声明意义不同
局部变量声明存储类型的作用指定变量存储的区域以及由此产生的生存期问题,而对于全局变量来说,由于都是编译时分配内存的,都存放在静态存储区,声明存储类型的作用是变量作用域的扩展问题
变量的声明与定义:建立存储空间的声明称定义(定义性声明),不需要建立存储空间的声明称为声明(引用型声明)(如:extern)
内部函数和外部函数
内部函数(静态函数):static 类型名 函数名(形参表);
外部函数:extern 类型名 fun(形参表)
extern可以默认省略
注:声明时可以省略static和extern
#include
int main(){
int a = 10;
int *point = &a;
printf(" a = %d(a的值)\n &a = %d(a的地址)\n point = %d(a的地址)\n *point = %d(a的值)\n &point = %d(point的地址)\n",a,&a,point,*point,&point);
return 0;
}
指针做函数形参,是将实参变量地址传入调用函数;值传递是将实参的值赋给创建的形参
注意:指针做形参也符合值传递,所以形参指针直接交换指向地址,不改变实参指针指向地址
所谓数组元素的指针就是数组元素的地址。引用数组元素可以是下标法也可以是指针法。例p=&a[0];等价于p=a;
(1)p+1指向同一数组中的下一个元素,p-1指向同一数组的上一个元素
(2)“[]”:变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值
(3)地址相减:是指地址值相减除以类型长度,相减时类型必须相同
(4)引用数组元素:1)a[i] 2)*(a+i) 或 *(p+i)
注:1)数组名a代表数组首元素的地址,是一个指针常量,所以不能移动,如:a++,指针p移动p++要更快,因为指针变量直接指向元素,不必每次重新计算地址。
2)指针也可以p[2],是从p指向的位置开始,容易写错尽量少用
二维数组指针
int a[4][3];
(1)a:二维数组首地址
(2)a+1:&a[1],行地址加一,指向a[1],第二行a[1]首地址
(3) *(a+1):a[1]即&a[1][0],a[1][0]地址
(4) *(a+1)+1: &a[1][1],a[1][0]地址
(5) *( *(a+1)+1):a[1][1]或 *(a[1]+1)或 *(a[1]+1)
(6)地址等价:a+i、a[i]、 *(a+i)、&a[i]、&a[i][0]
注:a[0]与a地址相同,但基类型不同,指向的数据类型不同,前者是整形数据,后者是一维数组。指向一维数组的指针:int ( * pt)[4];
指向多维数组的指针
(1)指向数组元素的指针变量
int p = &a[0][0]
a[i][j]的地址是&a[0][0]的地址为“&a[0][0]+(i * m+j)”或“p+(i * m + j)”
a[2][3]的地址是(p+24+3)即(p+11)
(2)指向由m个元素组成的一维数组的指针变量
int ( *p)[4] = &a[0]
p+1指向a[1],p的增值以一维数组的长度为单位
注:int ( * p)[4]类型int ( *)[4]型,p被定义为指向一维整形数组的指针变量,一维数组有4个元素,因此p的基类型是一维数组,其长度是16字节
int main() {
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int (* p)[4] = a;
printf("%d\n", p);//指向a[0]的首地址
printf("%d\n", p+1);//指向a[1]的首地址
printf("%d\n", *p+1);//指向a[0][1]的首地址
printf("%d\n", (*p + 1)[1]);//a[0][1]的值
printf("%d\n", *(p+1)[1]);//a[2][0]的值 []优先级大于*
printf("%d\n", (*(p+1))[1]);//a[1][1]的值
printf("%d\n", *(p+1)+1);//指向a[1][1]的地址
printf("%d\n", *(*(p+1)+1));//a[1][1]的值
return 0;
}
指向数组的指针做函数参数
(1)用指向变量的指针变量
void fun1(int *p, int n);
int a[3][4];
fun1(*a);
(2)用指向一堆数组的指针变量
void fun2(int (*p)[4],int n)
int a[3][4];
fun2(a);
注:注意类型,不同类型不能赋值
指针引用字符串
(1)使用字符串的更加灵活的方法就是使用指针
输出整个字符printf(“%s\n”,string);
注:c语言中只有字符变量,没有字符串变量,char *string;被定义为一个指针变量,基类型为字符型
(2)形参和实参可以分别用字符数组名或字符指针变量
使用字符指针变量和字符数组的比较
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量存放的是地址
(2)赋值方式:可以对字符指针变量赋值,但不能对数组名赋值
char *a;
a = “I love China!”;//正确,将字符串首元素地址赋给指针变量
char str[14];
str=“I love China!”;//错误,数组名是地址是常量
(3)初始化的含义
char *a = “I love China!”;
str[]=“I love China!”;//错误,企图把字符串赋值给各元素
char str1[14] = “I love China!”;//正确
(4)存储单元内容
编译时为字符数组分配若干存储单元,存放各值,二对字符指针变量,只分配一个存储单元
注:指针变量一定要赋初值
(5)指针变量的值可以改变,字符数组名代表一个固定值,不能改变。
char str[]=“I love China!”;
str = str + 7;//错误
(6)字符数组中各元素的值可以改变,但字符指针变量指向的字符串常量的内容不可改变
char a[]="I love China!";
char * p="I love China!";
a[2] = 'r';//正确
p[2] = 'r';//错误
(7)引用数组元素。a[5]或 *(a+5)
(8)用指针变量指向一个格式字符串
char * format;
format=“a=%d,b=%f\n”;
printf(format,a,b);
称为可变格数输出函数
注:也可以用字符数组实现,但只能在采用定义数组时初始化或逐个对元素赋值的方法
指向函数的指针
(1)函数名代表函数的地址
int ( * p)(int ,int);//指向只有两个整形参数的函数。p类型:( *)(int,int)表示
(2)通过指针变量调用它所指向的函数
int max(int x, int y){return z;}
主函数:
int max(int,int);
int ( *p)(int,int);
p = max;//赋值函数入口地址
int a = 1, b =2;
int c = ( *p )(a,b);
注: int *p(a,b);括号优先级高,所以代表先声明一个p的函数,这个函数的返回值是指向整型变量的指针
(3)用指向函数的指针作函数参数
将函数的入口地址作为参数传递到其他函数
如:void fun(int ( *x1)(int),int( *x2)(int ,int)){}
返回指针值的函数,即地址
类型名 * 函数名(参数表列)
指针数组和多重指针
(1)若其元素均为指针类型数据,称为指针数组
形式:类型名 * 数组名[数组长度]
char *a[5]={“good”,“bad”,“hi”,“food”,“too”}
函数声明
void fun(char *a[], 5);
函数调用
fun(a,5);
注:指针数组的元素只能存放地址,不能存储方整数
(2)指向指针数据的指针变量
指向指针数据的指针变量,简称为指向指针的指针
int **p;
p=a;
(3)指针数组作main函数的形参
1)main函数形式
int main()
int main(void):表示函数没有参数,调用main函数时不必给出实参
一般形式:int main(int argc, char * argv[])
argc(argument count):参数个数
argv(argument vector):参数向量,一个 *char指针数组,数组每一个元素指向命令行中的一个字符串的首字符
2)为什么main需要参数
main函数是操作系统调用,实参只能由操作系统给出。在操作命令状态下,实参是和执行文件的命令一起给出的。
命令行的一般形式
命令名 参数1 参数2…参数n
命令名是可执行文件(包含main函数)
如:执行文件名file1.exe,将“china”和“Beijing”做为传给main函数的参数
命令行形式:file1 China Beijing
以上命令行参数个数为3,命令行都是字符串,字符串首地址构成指针数组
动态内存分配与指向他的指针变量
(1)栈:非静态局部变量分配在内存中的动态存储区
(2)堆:建立动态分配区域,需要时开辟,不需要时释放,只能通过指针引用
(3)存储动态分配的库函数
头文件#include
1)molloc函数开辟动态存储空间
void *malloc(unsigned int size);
2)calloc函数开辟动态存储空间
void *calloc(unsigned n, unsigned size)
3)realloc函数重新分配
void *realloc(void *p, unsigned int size);
4)free函数释放动态存储区
void free(void * p);
(4)void指针类型
解释:指向空类型或不指向确定的类型
在动态分配数组时默认类型为void *,然后强转赋值
归纳
注:
(1)地址型的数据包含:
1)表示内存编号的纯地址
2)它本身的类型,即指针类型
3)以它为标识的存储单元中存放的是什么类型的数据,即基类型
(2)int *p = NULL指针变量指向空
stdio.h 头文件定义 #define NULL 0
(3)区别指针和指针变量:指针就是地址,指针变量是用来存放地址的变量
结构体:用户自己建立由不同类型数据组成的组合型的数据结构
一般形式:struct 结构体名
{成员表列}
成员表列也称“域表”,每一个成员是结构体中的一个域。成员名命名与变量名相同。
定义结构体类型变量(掌握)
(1)先声明,在定义
struct Student{
int num;
int sex;
};
struct Student student1;
(2)声明的同时定义变量
struct Student{
int num;
char sex;
}student1;
(3)不指定类型名而直接定义结构体类型
struct{
成员列表
}变量名表列
struct{
int num;
char sex;
}student1={1,‘a’};
struct Birthday{
int day;
int monday;
};
struct Student{
int num;
char name[10];
Birthday bt;
};
int main(){
Student student1 = {1, "Zhang"};
Birthday bir = {11,1};
student1.bt = bir;
printf("%d %s %d %d",student1.num,student1.name,student1.bt.day,student1.bt.month);
}
结构体指针
(1)stu. 成员(如stu.num)
(2)(*p).成员名
(3)p->成员名
指针处理链表
(1)建立静态链表
(2)建立动态链表
共用体(了解)
union 共用体名
{成员表列}变量表列;
注:同一时刻只能对成员变量其中一个进行赋值,初始化也只需一个值
如:
union Data{
int i;
char ch;
float f;
}a={16};
a.i = 10;//赋值后所有类型值都将改变
注:共用体变量地址和它各成员的地址都是同一地址
不能对共用体赋值,C99可以共用体变量作为函数参数,以前只能用指向共用体的指针做参数
枚举类型(了解)
(1)枚举所有可能值(花括号中为枚举常量或枚举元素)
例:enum Weekday{sun,mon,wed,thu,fri,sat};
(2)枚举变量
enum Weekday workday;
注枚举常量默认从0开始自动赋值,例sun=0,mon=1
enum Weekday{sun=7,mon,wed,thu,fri,sat};//常量只有定义时才能自定义赋值
typedef声明新类型(掌握)
使用方法:
(1)先定义变量的方法写出定义体
(2)将变量名换成新类型名
(3)在最前面加typedef
(4)然后用新类型名定义变量
1、圆括号【()】、下标运算符【[]】、分量运算符的指向结构体成员运算符【->】、结构体成员运算符【.】;
2、逻辑非运算符【!】、按位取反运算符【~】、自增自减运算符【++】【 --】、负号运算符【-】、类型转换运算符【(类型)】、指针运算符和取地址运算符【*】【&】、长度运算符【sizeof】;
3、乘法运算符【*】、除法运算符【/】、取余运算符【%】;
4、加法运算符【+】、减法运算符【-】;
5、左移动运算符【<<】、右移动运算符【>>】;
6、关系运算符【< 】【>】【<=】【 >= 】;
7、等于运算符【==】、不等于运算符【!=】;
8、按位与运算符【&】;
9、按位异或运算符【^】;
10、按位或运算符【|】;
11、逻辑与运算符【&&】;
12、逻辑或运算符【||】;
13、条件运算符【?:】;
14、赋值运算符【=】【/=】【*=】【%=】【+=】【-=】【<<=】【>>=】【&=】【^=】【|=】;
15、逗号运算符【,】。
http://blog.chinaunix.net/uid-21411227-id-1826986.html
#include
//插入排序 假设当前元素前都已排好序,只要将当前元素插入前面符合的位置
/*空间复杂度O(1)
最好时间复杂度O(n) 比较次数最少n-1
最坏时间复杂度O(n^2) 比较次数最多n*(n-1)/2
平均时间复杂度O(n^2)
稳定
*/
void insert_sort(int *nums, int n){
for(int i = 1; i < n; i++){
int cur = nums[i];//暂存当前元素
int j = i-1;
for(; j>=0&&nums[j] > cur; j--)//将其前面大于当前元素的元素后移一位
nums[j+1] = nums[j];
nums[j+1] = cur;//插入符合的位置
}
}
//折半插入排序:先折半查找,再插入
void binary_insert_sort(int *nums, int n){
for(int i = 1; i < n; i++){
int cur = nums[i];
int left = 0, right = i-1;
while(left<=right){//二分查找,找到符合的位置
int mid = (left + right)/2;
if(nums[mid] > cur){
right = mid-1;
}else{
left = mid+1;
}
}
for(int j = i-1; j > right; j--){
nums[j+1] = nums[j];
}
nums[left] = cur;
}
}
//希尔排序
/*
空间复杂度O(1)
时间复杂度:未知
不稳定
*/
void shell_sort(int *nums, int n){
for(int d = n/2; d>=1; d = d/2){
for(int i = d+1; i < n; i++){
int cur = nums[i];
int j = i-d;
for(;j >= 0&&nums[j] > cur; j=j-d){
nums[j+d] = nums[j];
}
nums[j+d] = cur;
}
}
}
//冒泡排序 : 从后往前两两比较,前者大于后者则交换,经过一轮交换将最小值移到最前面
/*空间复杂度O(1)
最好时间复杂度O(n) 比较次数最少n-1
最坏时间复杂度O(n^2) 比较次数最多n*(n-1)/2
平均时间复杂度O(n^2)
稳定
*/
void bubble_sort(int *nums, int n){
for(int i = 0; i < n-1; i++){//执行n-1轮
bool flag = false;
for(int j = n-1; j > i; j--){//从后往前两两比较
if(nums[j-1] > nums[j]){//前者大于后者交换
int temp = nums[j];
nums[j] = nums[j-1];
nums[j-1] = temp;
flag = true;
}
}if(flag==false)break;//没有执行交换,说明都排好序了,停止
}
}
void bubble_sort2(int *nums, int n){
for(int i = n-1 ;i > 0; i--){
bool flag = false;
for(int j = 0; j < i; j++){
if(nums[j+1] < nums[j]){
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
flag = true;
}
}
if(flag==false)break;
}
}
//选择排序 :每次选择最小的元素,与当前排好序的元素末尾元素交换
/*空间复杂度O(1)
时间复杂度O(n^2) 比较次数n*(n-1)/2
不稳定
*/
void select_sort(int *nums, int n){
for(int i = 0 ; i < n; i++){
int cur_min = nums[i];//存储当前元素
int cur_min_index = i;//存储当前元素位置
for(int j = i+1; j < n; j++){
if(cur_min > nums[j]){//找最小值位置和元素值
cur_min = nums[j];
cur_min_index = j;
}
}
if(i!=cur_min_index){//如果最小值不等于当前元素,则交换
int temp = nums[i]; nums[i] = cur_min; nums[cur_min_index] = temp;
}
}
}
//快速排序 : 从第一个元素开始,将小于该元素的值放在该元素的左边,大于该元素的放在该元素右边,不断分治执行上述操作
/*最好空间复杂度O(n)
最坏空间复杂度O(log2(n))
最好时间复杂度O(nlog2(n)):如均等分的数组
最坏时间复杂度O(n^2):如排好序的数组
平均时间复杂度O(nlog2(n))
不稳定
代码记忆方法:
1.找中轴
2.左边快排
3.右边快排
找中轴也分为三步:
1.选定一个中轴(以最左边的中轴为例)
2.从右往左找,右边赋值给左边
3.从左往右找,左边赋值给右边
*/
int partition(int *nums, int left, int right){
int cur = nums[left];//存储第一个元素,便空出一个位置
while(left<right){
while(left<right&&cur<=nums[right])right--;//从右往左找到比第一个元素小的元素,大的不用考虑
nums[left] = nums[right];//将小的元素放在空出位置,即第一个元素左边,right位置空出
while(left<right&&cur>=nums[left])left++;//从左往右找到比第一个元素大的元素
nums[right] = nums[left];//将大的元素放置在 right空出位置
}
nums[left] = cur;//存储第一个位置,代表将数组划分为大小两边
return left;//返回划分位置
}
void quick_sort(int *nums, int left, int right){
if(left<right){//不断划分
int index = partition(nums,left,right);
quick_sort(nums, left, index-1);
quick_sort(nums, index+1, right);
}
}
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}
//堆排序(大根堆) :
/*
1.先将数组变成大根堆
2.将树根元素与数组最后一个元素交换位置(每次排好数组最后一个数)
将交换后的树根元素下沉,将剩下元素继续符合大根堆,循环执行,直到全部交换完成
如何变成大根堆:
所谓大根堆即父节点值要大于左右子节点的值,从n/2-1开始才有子结点
从n/2-1开始下沉元素,直到父节点大于左右子结点
如何下沉元素:
让当前要下沉的值,与其左右子结点比较,如果左右节点其中之一大于当前节点,则交换,
当前子结点继续与子结点的子结点比较,以此类推,直到无子结点或者不能交换为止
*/
/*
空间复杂度O(1)
时间复杂度 建堆O(n)、排序O(nlog2(n)),总 O(nlog2(n))
不稳定
*/
void HeadAdjust(int *nums, int k, int len){
for(int i = k*2+1; i < len; i*=2+1){
if(i+1<len&&nums[i]<nums[i+1]){
i++;
}
if(nums[i] > nums[k]){
swap(&nums[i],&nums[k]);
k=i;
}else{
break;
}
}
}
//小根堆
void RHeadAdjust(int *nums, int k, int len){
for(int i=k*2+1; i < len; i*=2+1){
if(i+1<len&&nums[i]>nums[i+1]){
i++;
}
if(nums[k]>nums[i]){
swap(&nums[k],&nums[i]);
k = i;
}else{
break;
}
}
}
void BuildMaxHeap(int *num, int n){
for(int i = n/2-1; i>=0; i--){
HeadAdjust(num,i,n);
//RHeadAdjust(num,i,n);
}
}
void heap_sort(int *nums, int n){
BuildMaxHeap(nums,n);
for(int i = n-1; i>=0; i--){
swap(&nums[i],&nums[0]);
HeadAdjust(nums,0,i);
//RHeadAdjust(nums,0,i);
}
}
#include
//归并排序 :将数组不断分割,回溯时将分割的两数组按从小到大比较合并
/*
空间复杂度O(n)
时间复杂度O(nlog2(n))
稳定
代码记忆:
1.左分割
2.右分割
3.左右合并
左右合并方法:
1.暂存数组
2.依次比大小
3.存储剩余元素
*/
void merge(int *nums,int left,int mid, int right){
int *temp = (int *)malloc((right-left)*sizeof(int));//创建right-left动态空间
for(int i = left; i <= right; i++){//将数组元素赋值给临时数组
temp[i-left] = nums[i];
}
int l1 = left, l2 = mid+1, i = left;//分成两个数组,一个从left开始,一个从mid+1开始
for(; l1<=mid&&l2<=right&&i <= right; i++){
if(temp[l1-left] <= temp[l2-left]){//比较两数组元素大小,将较小的放入数组,依次类推
nums[i] = temp[l1-left];
l1++;
}else{
nums[i] = temp[l2-left];
l2++;
}
}
//若一个数组比较完,另一个还有元素,剩下的存放剩余数组元素
while(l1<=mid){nums[i++] = temp[l1-left];l1++;}
while(l2<=right){nums[i++] = temp[l2-left];l2++;}
}
void merge_sort(int *nums, int left, int right){
if(left<right){//归并排序,不断分割递归
int mid = (left+right)/2;
merge_sort(nums,left,mid);
merge_sort(nums,mid+1,right);
merge(nums,left,mid,right);
}
}
void print(int *nums, int n){
for(int i = 0; i < n; i++){
printf("%d ",nums[i]);
}
printf("\n");
}
int main(){
int nums[10]={7,5,4,2,1,10,5,9,6,8};
heap_sort(nums,10);
print(nums,10);
return 0;
}
归并排序升序每一趟结果
{10,18,4,3,6,12,1,9,15,8}
首先是分解,注意;的位置:
1)10,18,4,3,6;12,1,9,15,8
2)10,18,4;3,6;12,1,9;15,8
3)10,18;4;3;6;12,1;9;15;8
4)10;18;4;3;6;12;1;9;15;8
然后才是归并(merge),也是该题要的答案:
1)10,18;4;3;6;1,12;9;15;8
2)4,10,18;3,6;1,9,12;8,15
3)3,4,6,10,18;1,8,9,12,15
4)1,3,4,6,8,9,10,12,15,18
//最大公约数
//更相减损法递归
int GCD1(int a,int b){
if(a>b)
return GCD1(b,a-b);
else if(a<b){
return GCD1(a,b-a);
}else{
return a;
}
}
//更相减损法迭代
int GCD2(int a,int b){
while(a!=b){
if(a>b)a = a-b;
if(a<b)b = b-a;
}
return a;
}
//辗转相除法递归
int GCD3(int a, int b){
if(b == 0)return a;
return GCD3(b,a%b);
}
//辗转相除法迭代
int GCD4(int a,int b){
int c = a%b;
while(c){
a = b;
b = c;
c = a%b;
}
return b;
}
//最小公倍数
int LCM(int a,int b){
int c = GCD1(a,b);
return a*b/c;
}
int main(){
int a,b;
while(~scanf("%d%d",&a,&b))
printf("%d\n",LCM(a,b));
}
#define N 5
#include
#include
#include
int main(){
char *a[N],**p = a;
for(int i = 0; i <N; i++){
// a[i]=(char*)malloc(sizeof(char)*20);
*(p+i)= (char*)malloc(sizeof(char)*20);
gets(*(p+i));
}
for(int i = 0; i < N; i++){
for(int j = 0; j < N-i-1; j++){
if(strcmp(p[j],p[j+1])>0){
char* temp;
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
for(int i = 0; i< N; i++){
puts(a[i]);
}
}
#include
void hanoi(int n, char a, char b, char c){
if(n == 1){
printf("将第%d层从塔%c移动到塔%c\n",n,a,c);
return;
}
hanoi(n-1,a,c,b);
printf("将第%d层从塔%c移动到塔%c\n",n,a,c);
hanoi(n-1,b,a,c);
}
int main(){
int n;
scanf("%d",&n);
hanoi(n,'A','B','C');
}