c语言进阶10-算法
一、 数据结构和算法关系
为什么要学数据结构和算法?
通常,计算机解决问题的步骤如下:
在数学模型中,计算机处理的对象之间通常存在着一种最简单的线性关系,这类数学模型就是线性的数据结构。著名计算机科学家沃斯(Nikiklaus Wirth)提出一个公式:程序=数据结构+算法。数据结构就是编程的思维,编程的灵魂,算法的精髓所在,没有了数据结构,程序就好像一个空核,是低效率的。算法与数据结构是紧密联系不可分割,必须在一起才能最终解决问题。算法是程序设计的的灵魂。
二、 两种算法的比较
在此之前大家都已经学过C语言了,不管学的好不好,也可以写点小程序。现在写一个求1+2+3+……+100结果的程序,大多数人会马上写出下面的C语言代码(或者其他语言的代码):
int i,sum = ,n = ; for(i = ;i < = n;i++) { sum = sum + i; } printf("%d",sum);
这是最简单的计算机程序之一,它就是一种算法。问题在于,这样是不是真的很好?是不是最高效?
以高斯的童年故事为例,老师要求学生计算1+2+3+……+100的结果,高斯很快就得出了答案,老师非常惊讶,高斯解释道:
sum = 1+ 2+ 3+……+ 99+100
sum =100+ 99+ 98+……+ 2+ 1
2*sum =101+101+101+……+101+101
所以sum=5050
用程序来实现如下:s
int i,sum = ,n = ; sum = ( + n ) * n / ; printf("%d",sum);
他用的方法相当于另一种求等差数列的算法,不仅仅可以用于1加到100,就是加到一千,一万,一亿(需要更改整型变量类型为长整型,否则会溢出),也就是瞬间之事。但如果用刚才的程序,显然计算机要循环一千、一万、一亿次的加法运算。如果让计算机按沃斯的算法,那么速度可想而知。
三、 算法定义
算法就是解决问题的方法和步骤。在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。指令可以是计算机指令,也可以是我们平时的语言文字。也就是算法可以通过自然语言描述,也可以通过计算机指令描述。
为了解决某个或某类问题,需要把指令表示成一定的操作序列,操作序列包括一组操作,每一个操作都完成特定的功能,这就是算法了。
如:S1: 使t=1
S2: 使i=2
S3: 使t×i, 乘积仍然放在在变量t中,可表示为t×i→t
S4: 使i的值+1,即i+1→i
S5: 如果i≤5, 返回重新执行步骤S3以及其后的S4和S5;否则,算法结束。
四、 算法的五大特性
- 1. 零个输入或多个输入
算法可以具有零个或多个输入。尽管对于绝大多数算法来说,输入参数都是必要的,但对于个别情况,如打印“hello world!”这样的代码,不需要任何输入参数,因此算法的输入可以是零个。
算法至少有一个或多个输出,算法是一定需要输出的,输出的形式可以是打印输出,也可以是返回一个或多个值等。
如:
#include "stdio.h" void main() { printf("Hello word!\n");/*没有这一行,程序将没有意义*/ }
- 2. 一个输出或多个输出
算法至少有一个或多个输出,算法是一定需要输出的,输出的形式可以是打印输出,也可以是返回一个或多个值等。
如:
#include "stdio.h" void main() { int a,b; scanf(“%d%d”,&a,&b); }
- 3. 有穷性
有穷性:指算法在执行有限的步骤后自动结束、不会出现无限循环。当然,这里有穷的概念并不是纯数学意义的,而是在实际应用当中合理的、可以接受的“有边界”,即计算机最终一定会结束。
void main() { int sum=,i=; while() sum+=i; printf(“%d”,sum); }
- 4. 确定性
确定性:算法的每一步骤都具有确定的含义,不会出现二义性。算法在一定条件下,只有一条执行路径,相同的输入只能有唯一的输出结果。算法的每个步骤被精确定义而无歧义。
void main() { int a=,b,c; c=a+b; printf(“%d”,c); }
- 5. 可行性
可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。可行性意味着算法可以转换为程序上机运行,并得到正确的结果。
求10!程序如下:
void main() { int s=,i; for(i=;i<=;i++) s*=i; printf(“!=%d”,s); }
以上代码可以求出10!=3628800,那么要是求100!呢,只改个i<=100是否可行呢?解决方案又是什么呢?那怎么设计出一个合格的算法呢?请继续参阅第四单元
五、 经典算法
- 1. 分治算法
有12枚一模一样的硬币,已知其中只有一枚是假币,并且假币和真币的重量不一样(假设已知假币比真币重量轻),方法一:
把12枚硬币分成两等问如何用一个天平把假币从这12枚硬币中找出来,要求只能称3次。
(1)把12枚硬币分成两等份,每份6枚;
(2)把有假币的那端的6枚硬币再分成两等份,每份3枚;
(3)在有假币的那一端的3枚硬币中任取2个去称重。
方法二:
(1)把12枚硬币分成三等份,每份4枚;
(2)在有假币的那一端的4枚硬币中,任取两枚放到天平两端去称重;
(3)若假币在余下的那两枚中,则把这两枚硬币放到天平两端去称重。
- 2. 穷举算法
解决百钱买百鸡问题:
某人有100元钱,要买100只鸡。公鸡5元钱一只,母鸡3元钱一只,小鸡一元钱3只。问可买到公鸡,母鸡,
小鸡各为多少只?
根据本问题可以采取逐一列举的方法:不如用x表示公鸡的数量,y表示母鸡的数量 ,z表是小鸡的的数量
x的取值范围1-100
y的取值范围1-100
z的取值范围1-100
满足三个个条件 x+y+z==100 且 5*x+3*y+z/3=100 且z%3==0
则算法简单的写成:
void main() { int x,y,z; for(x=;x<=;x++) { for(y=;y<=;y++) { for(z=;z<=;z++) { if(x+y+z== && *x+*y+z/= && z%==) { printf(“公鸡%d只,母鸡%d只,小鸡%d只\n”,x,y,z); } } } } }
当然,本算法也可以改进,比如公鸡数量根据价钱估计明显不能超过20只,母鸡的数量不能超过33只,修改两层循环的条件会使算法更加高效。
void main() { int x,y,z; for(x=;x<=;x++) { for(y=;y<=;y++) { for(z=;z<=;z++) { if(x+y+z== && *x+*y+z/= && z%==) { printf(“公鸡%d只,母鸡%d只,小鸡%d只\n”,x,y,z); } } } } }
- 3. 递推算法
猴子吃桃子问题:
有数量未知的桃子,猴子第一天吃了总数量的一半又多吃一个,第二天又吃了剩下的一半有多吃一个,依次类推,到第十天桃子的数量仅剩1个,问最初桃子的数量有多少?
使用递推法则有如下计算:
第十天桃子的数量是:1
第九天的数量则是:(1+1)*2=4
第八天的数量则是:(4+1)*2=10
第七天的数量则是:(10+1)*2=22
......
直到推到第一天,便计算出桃子的总数量了。
void main() { int day=,num=; //day表示天数从第十天逆推 num表示当天桃子的数量 while(day>) { num=(num+)*; day--; } printf(“桃子的数量是:%d个\n”,num); }
- 4. 迭代算法
有一对兔子不吃不喝不会死,第三个月成熟,从成熟开始每月繁殖生下一对兔子,新生的每对兔子仍是第三个月成熟开始每月生一对兔子,那么每个月兔子的对数如何计算。
其实这就是著名数列斐波那契数列如下:1 1 2 3 5 8 13 21 34 ......
可以采用迭代算法:
void main() { int a=,b=1c,i; printf(“打印前20项的值:\n”); printf(“%d\t%d\t”,a,b); for(i=;i<=;i++) { c=a+b; printf(“%d\t”,c); a=b; b=c; }
}
2048游戏代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h> #define Key_Up 0x4800 // 向上方向键
#define Key_Down 0x5000 // 向下方向键
#define Key_Right 0x4d00 // 向右方向键
#define Key_Left 0x4b00 // 向左方向键 int map[][] = { };
int check[] = { }; //判断游戏是否结束,如果都不为0,游戏结束
int i, j; //
//显示在屏幕上
//
void print()
{
for (i = ; i < ; i++)
{
for (j = ; j < ; j++)
{
printf("%d\t", map[i][j]);
}
printf("\n");
}
} //
//向右移动
//
int moveRight() {
int flag = ; //来标记是否整个map矩阵能否改变,以此判断整个游戏是否结束
//这个循环的目的是为了做某一行的加减,如某一行为[2 2 4 4],经过以下运算就会变为[0 4 0 8];
for (i = ; i < ; i++)
{
for (j = - ; j >= ; j--)
{
int cell = map[i][j];
if (cell != )
{
int k = j - ;
while (k >= )
{
int nextcell = map[i][k];
if (nextcell != )
{
if (cell == nextcell)
{
flag = ;
map[i][j] += map[i][k];
map[i][k] = ;
}
k = -;
break;
}
k--;
}
}
}
} //上一步做的是数据加减,这一步对加减后的数据做数据处理
//例如某一行经过上一步的数据为【0 4 0 8】,将会变为【0 0 4 8】
for (i = ; i < ; i++)
{
for (j = - ; j > ; j--)
{
int cell = map[i][j];
if (cell == )
{ int k = j - ;
while (k >= )
{
int nextcell = map[i][k];
if (nextcell != )
{
flag = ;//当前元素为0,说明能移动,改变flag的值
map[i][j] = nextcell;
map[i][k] = ;
k = -;
}
k--;
}
}
}
}
if (flag)
return ;
else
return ; //游戏结束
} //
//向左移动
//
int moveLeft() {
int flag=;
for (i = ; i < ; i++)
{
for (j = ; j < ; j++)
{
int cell = map[i][j];//cell单词用的不太恰当,表示当前元素,你可以采用更有意义的命名
if (cell != )
{
int k = j + ;
while (k < )
{
int nextcell = map[i][k];
if (nextcell != )
{
if (cell == nextcell)
{
flag = ;//相邻两个元素相同,就说明能移动,所以改变flag的值
map[i][j] += map[i][k];
map[i][k] = ;
}
k = ;
break;
}
k++;
}
}
}
} //修改部分:for循环中的i或者j的循环条件 for (i = ; i < ; i++)
{
for (j = ; j < - ; j++)
{
int cell = map[i][j];
if (cell == )
{
int k = j + ;
while (k < )
{
int nextcell = map[i][k];
if (nextcell != )
{
flag = ;
map[i][j] = nextcell;
map[i][k] = ;
k = ;
}
k++;
}
}
}
}
if (flag)
return ;
else
return ;
} //
//向下移动
//
int moveDown() {
int flag=;
for (i = - ; i >= ; i--)
{
for (j = ; j < ; j++)
{
int cell = map[i][j]; if (cell != )
{
int k = i - ;
while (k >= )
{
int nextcell = map[k][j];
if (nextcell != )
{
if (map[i][j] == map[k][j])
{
flag = ;
map[i][j] += map[k][j];
map[k][j] = ;
}
k = ;
break;
}
k--;
}
}
}
} //修改部分:for循环中的i或者j的循环条件
for (i = - ; i > ; i--)
{
for (j = ; j < ; j++)
{
int cell = map[i][j];
if (cell == )
{
int k = i - ;
while (k >= )
{
int nextcell = map[k][j];
if (nextcell != )
{
flag = ;
map[i][j] = nextcell;
map[k][j] = ;
k = ;
}
k--;
}
}
}
}
if (flag)
return ;
else
return ;
} //
//向上移动
//
int moveUp() {
int flag=;
for (i = ; i < ; i++)
{
for (j = ; j < ; j++)
{
int cell = map[i][j]; if (cell != )
{
int k = i + ;
while (k < )
{
int nextcell = map[k][j];
if (nextcell != )
{
if (cell == nextcell)
{
flag = ;
map[i][j] += map[k][j];
map[k][j] = ;
}
k = ;
break;
}
k++;
}
}
}
} //修改部分:for循环中的i或者j的循环条件
for (i = ; i < - ; i++)
{
for (j = ; j < ; j++)
{
int cell = map[i][j];
if (cell == )
{ int k = i + ;
while (k < )
{
int nextcell = map[k][j];
if (nextcell != )
{
flag = ;
map[i][j] = nextcell;
map[k][j] = ;
k = ;
}
k++;
}
}
}
}
if (flag==)
return ;
else
return ;
} //
//随机生成在空格上生成2或者4,并且判断是否游戏结束
//
int randInteger() //有缺陷,【0 0 0 0】
{
for (i = ; i < ; i++)
{
for (j = ; j < ; j++)
{
if (map[i][j] == )
{
map[i][j] = (rand() % + ) * ; //
return ;
}
}
}
return ;
} int main()
{
char key;
while ()
{
printf("上:w;下:s;左:a;右:d;退出:q\n");
printf("请输入要执行的操作:");
scanf("%c", &key);
switch (key)
{
case 'w':
{
randInteger();
moveUp();
printf(" 向上方向键被按下\n");
print();
break;
}
case 's':
{
randInteger();
moveDown();
printf(" 向下方向键被按下\n");
print();
break;
}
case 'a':
{
randInteger();
moveLeft();
printf(" 向左方向键被按下\n");
print();
break;
}
case 'd':
{
randInteger();
moveRight();
printf(" 向右方向键被按下\n");
print();
break;
}
case 'q':
return ;
}
}
return ;
}
c语言进阶10-算法的更多相关文章
- 10个经典的C语言面试基础算法及代码
10个经典的C语言面试基础算法及代码作者:码农网 – 小峰 原文地址:http://www.codeceo.com/article/10-c-interview-algorithm.html 算法是一 ...
- C语言的10大基础算法
C语言的10大基础算法 算法是一个程序和软件的灵魂,作为一名优秀的程序员,只有对一些基础的算法有着全面的掌握,才会在设计程序和编写代码的过程中显得得心应手.本文包括了经典的Fibonacci数列.简易 ...
- 0.数据结构(python语言) 基本概念 算法的代价及度量!!!
先看思维导图: *思维导图有点简陋,本着循循渐进的思想,这小节的知识大多只做了解即可. *重点在于算法的代价及度量!!!查找资料务必弄清楚. 零.四个基本概念 问题:一个具体的需求 问题实例:针对问题 ...
- 【R笔记】R语言进阶之4:数据整形(reshape)
R语言进阶之4:数据整形(reshape) 2013-05-31 10:15 xxx 网易博客 字号:T | T 从不同途径得到的数据的组织方式是多种多样的,很多数据都要经过整理才能进行有效的分析,数 ...
- 《C语言进阶剖析》课程目录
<C语言进阶剖析>学习笔记 本文总结自狄泰软件学院唐佐林老师的<C语言 ...
- 数据结构C语言版 弗洛伊德算法实现
/* 数据结构C语言版 弗洛伊德算法 P191 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h>#include <limits.h> # ...
- 银行卡号码校验算法(Luhn算法,又叫模10算法)
有时候在网上办理一些业务时有些需要填写银行卡号码,当胡乱填写时会立即报错,但是并没有发现向后端发送请求,那么这个效果是怎么实现的呢. 对于银行卡号有一个校验算法,叫做Luhn算法. 一.银行卡号码的校 ...
- python进阶10 MySQL补充 编码、别名、视图、数据库修改
python进阶10 MySQL补充 编码.别名.视图.数据库修改 一.编码问题 #MySQL级别编码 #修改位置: /etc/mysql/mysql.conf.d/mysqld.cnf def ...
- js 进阶 10 js选择器大全
js 进阶 10 js选择器大全 一.总结 一句话总结:和css选择器很像 二.JQuery选择器 原生javaScript中,只能使用getELementById().getElementByNam ...
- [技术栈]C#利用Luhn算法(模10算法)对IMEI校验
1.Luhn算法(模10算法) 通过查看ISO/IEC 7812-1:2017文件可以看到对于luhn算法的解释,如下图: 算法主要分为三步: 第一步:从右边第一位(最低位)开始隔位乘2: 第二步:把 ...
随机推荐
- tftp的安装及配置
1.安装tftp服务客户端sudo apt-get install tftp 2.安装tftp服务器端sudo apt-get install tftpd 3.安装xinetd注意同类似的还有open ...
- 比较DirectX和OpenGL的区别(比较详细)
OpenGL是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库.OpenGL的前身是SGI公司为其图形工作站开发的IRIS GL.IRIS GL是一个工业标准的3D图形软件接口,功能虽然 ...
- Mysql 自定义HASH索引带来的巨大性能提升
有这样一个业务场景,需要在2个表里比较存在于A表,不存在于B表的数据.表结构如下: T_SETTINGS_BACKUP | CREATE TABLE `T_SETTINGS_BACKUP` ( `FI ...
- linux dll hell--链接库real name, soname, link name
DLL hell 是指 Windows 系统上动态库的新版本覆盖旧版本,且新版本不能兼容旧版本的问题. 例如:装新软件,但原有的软件运行不起来了. Linux 系统下也同样面临着和 Windows ...
- SYN5006型电机同步编码脉冲分配器
SYN5006型电机同步编码脉冲分配器 编码器信号分配板增量式编码器脉冲分配器使用说明视频链接: http://www.syn029.com/h-pd-81-0_310_13_-1.html 请将此链 ...
- SpringBoot2.1.6 整合CXF 实现Webservice
SpringBoot2.1.6 整合CXF 实现Webservice 前言 最近LZ产品需要对接公司内部通讯工具,采用的是Webservice接口.产品框架用的SpringBoot2.1.6,于是采用 ...
- .NET开发框架(一)-框架介绍与视频演示
本文主要介绍一套基于.NET CORE的SPA高并发.高可用的开发框架. 我们暂且称它为:(让你懂.NET)开发框架. 以此为主线,陆续编写教程,讲述如何构建高并发.高可用的框架. (欢迎转载与分享) ...
- 无法启动print spooler服务,错误2,系统找不到指定的文件
来自百度: 无法启动print spooler服务,错误2,系统找不到指定的文件 我的打印机无法运行:出现"打印后台程序没有执行"提示.查:print spooler没有启动.点击 ...
- 记录微信浏览器里word链接点击没反应的bug
有用户反应点击下载附件时没有反应,让用户把该下载链接复制到微信对话框中,发现点击该链接仍然无反应,但是在内置的手机浏览器中打开是正常的而且可以下载. 链接地址,有需要的可以拿去进行测试: http:/ ...
- Skyline WEB端开发1——入门
Skyline是一套优秀的三维数字地球平台软件.凭借其国际领先的三维数字化显示技术,它可以利用海量的遥感航测影像数据.数字高程数据以及其他二三维数据搭建出一个对真实世界进行模拟的三维场景.目前在国内, ...