c语言进阶10-算法

一、  数据结构和算法关系

为什么要学数据结构和算法?

通常,计算机解决问题的步骤如下:

 

在数学模型中,计算机处理的对象之间通常存在着一种最简单的线性关系,这类数学模型就是线性的数据结构。著名计算机科学家沃斯(Nikiklaus Wirth)提出一个公式:程序=数据结构+算法。数据结构就是编程的思维,编程的灵魂,算法的精髓所在,没有了数据结构,程序就好像一个空核,是低效率的。算法与数据结构是紧密联系不可分割,必须在一起才能最终解决问题。算法是程序设计的的灵魂。

二、  两种算法的比较

在此之前大家都已经学过C语言了,不管学的好不好,也可以写点小程序。现在写一个求1+2+3+……+100结果的程序,大多数人会马上写出下面的C语言代码(或者其他语言的代码):

int i,sum = 0,n = 100;

for(i = 1;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 = 0,n = 100;

sum = ( 1 + n ) * n / 2;

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. 1.        零个输入或多个输入

算法可以具有零个或多个输入。尽管对于绝大多数算法来说,输入参数都是必要的,但对于个别情况,如打印“hello world!”这样的代码,不需要任何输入参数,因此算法的输入可以是零个。

算法至少有一个或多个输出,算法是一定需要输出的,输出的形式可以是打印输出,也可以是返回一个或多个值等。

如:

#include "stdio.h"

void main()

{

       printf("Hello word!\n");/*没有这一行,程序将没有意义*/

}
  1. 2.        一个输出或多个输出

算法至少有一个或多个输出,算法是一定需要输出的,输出的形式可以是打印输出,也可以是返回一个或多个值等。

 如:  

  #include "stdio.h"

void main()

{ 

int   a,b;

scanf(“%d%d”,&a,&b);  

}
  1. 3.        有穷性

有穷性:指算法在执行有限的步骤后自动结束、不会出现无限循环。当然,这里有穷的概念并不是纯数学意义的,而是在实际应用当中合理的、可以接受的“有边界”,即计算机最终一定会结束。

  void main()

 {

      int sum=0,i=0;

      while(1)

      sum+=i;

      printf(“%d”,sum);

} 
  1. 4.        确定性

确定性:算法的每一步骤都具有确定的含义,不会出现二义性。算法在一定条件下,只有一条执行路径,相同的输入只能有唯一的输出结果。算法的每个步骤被精确定义而无歧义。

 void main()

 {

      int a=0,b,c;

      c=a+b;

      printf(“%d”,c);

}
  1. 5.        可行性

可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。可行性意味着算法可以转换为程序上机运行,并得到正确的结果。

求10!程序如下:

void main()

{

int s=1,i;

for(i=1;i<=10;i++)

s*=i;

printf(“10!=%d”,s);

}

以上代码可以求出10!=3628800,那么要是求100!呢,只改个i<=100是否可行呢?解决方案又是什么呢?那怎么设计出一个合格的算法呢?请继续参阅第四单元

五、  经典算法

  1. 1.        分治算法

有12枚一模一样的硬币,已知其中只有一枚是假币,并且假币和真币的重量不一样(假设已知假币比真币重量轻),方法一:

把12枚硬币分成两等问如何用一个天平把假币从这12枚硬币中找出来,要求只能称3次。

(1)把12枚硬币分成两等份,每份6枚;

(2)把有假币的那端的6枚硬币再分成两等份,每份3枚;

(3)在有假币的那一端的3枚硬币中任取2个去称重。

方法二:

(1)把12枚硬币分成三等份,每份4枚;

(2)在有假币的那一端的4枚硬币中,任取两枚放到天平两端去称重;

(3)若假币在余下的那两枚中,则把这两枚硬币放到天平两端去称重。

 

  1. 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=1;x<=100;x++)

{

for(y=1;y<=100;y++)

{

for(z=1;z<=100;z++)

{

     if(x+y+z==100 && 5*x+3*y+z/3=100 && z%3==0)

    {

                                         printf(“公鸡%d只,母鸡%d只,小鸡%d只\n”,x,y,z);

    }

}

}

}

}

当然,本算法也可以改进,比如公鸡数量根据价钱估计明显不能超过20只,母鸡的数量不能超过33只,修改两层循环的条件会使算法更加高效。

void main()

{

int x,y,z;

for(x=1;x<=20;x++)

{

for(y=1;y<=33;y++)

{

for(z=1;z<=100;z++)

{

     if(x+y+z==100 && 5*x+3*y+z/3=100 && z%3==0)

    {

                                         printf(“公鸡%d只,母鸡%d只,小鸡%d只\n”,x,y,z);

    }

}

}

}

}
  1. 3.        递推算法

猴子吃桃子问题:

有数量未知的桃子,猴子第一天吃了总数量的一半又多吃一个,第二天又吃了剩下的一半有多吃一个,依次类推,到第十天桃子的数量仅剩1个,问最初桃子的数量有多少?

使用递推法则有如下计算:

第十天桃子的数量是:1

第九天的数量则是:(1+1)*2=4

第八天的数量则是:(4+1)*2=10

第七天的数量则是:(10+1)*2=22

......

直到推到第一天,便计算出桃子的总数量了。

 

void main()

{

       int day=10,num=1;   //day表示天数从第十天逆推  num表示当天桃子的数量

    while(day>1)

    {

              num=(num+1)*2;

        day--;

    }

       printf(“桃子的数量是:%d个\n”,num);

}
  1. 4.        迭代算法

有一对兔子不吃不喝不会死,第三个月成熟,从成熟开始每月繁殖生下一对兔子,新生的每对兔子仍是第三个月成熟开始每月生一对兔子,那么每个月兔子的对数如何计算。

其实这就是著名数列斐波那契数列如下:1 1 2 3 5 8 13 21 34 ......

可以采用迭代算法:                                                                                        

void main()

{

       int a=1,b=1c,i;

    printf(“打印前20项的值:\n”);

    printf(“%d\t%d\t”,a,b);

    for(i=3;i<=20;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[4][4] = { 0 };
int check[4] = { 0 };  //判断游戏是否结束,如果都不为0,游戏结束 
int i, j;
 
//
//显示在屏幕上
// 
void print()
{
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            printf("%d\t", map[i][j]);
        }
        printf("\n");
    }
}
 
 
//
//向右移动
//
int moveRight() {
    int flag = 0;  //来标记是否整个map矩阵能否改变,以此判断整个游戏是否结束
    //这个循环的目的是为了做某一行的加减,如某一行为[2 2 4 4],经过以下运算就会变为[0 4 0 8];
    for (i = 0; i < 4; i++)
    {
        for (j = 4 - 1; j >= 0; j--)
        {
            int cell = map[i][j];
            if (cell != 0)
            {
                int k = j - 1;
                while (k >= 0)
                {
                    int nextcell = map[i][k];
                    if (nextcell != 0)
                    {
                        if (cell == nextcell)
                        {
                            flag = 1;
                            map[i][j] += map[i][k];
                            map[i][k] = 0;
                        }
                        k = -1;
                        break;
                    }
                    k--;
                }
            }
        }
    }
 
    //上一步做的是数据加减,这一步对加减后的数据做数据处理
    //例如某一行经过上一步的数据为【0 4 0 8】,将会变为【0 0 4 8】
    for (i = 0; i < 4; i++)
    {
        for (j = 4 - 1; j > 0; j--)
        {
            int cell = map[i][j];
            if (cell == 0)
            {
 
                int k = j - 1;
                while (k >= 0)
                {
                    int nextcell = map[i][k];
                    if (nextcell != 0)
                    {
                        flag = 1;//当前元素为0,说明能移动,改变flag的值
                        map[i][j] = nextcell;
                        map[i][k] = 0;
                        k = -1;
                    }
                    k--;
                }
            }
        }
    }
    if (flag)
        return 0;
    else
        return 4;  //游戏结束 
}
 
 
//
//向左移动
//
int moveLeft() {
    int flag=0;
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            int cell = map[i][j];//cell单词用的不太恰当,表示当前元素,你可以采用更有意义的命名
            if (cell != 0)
            {
                int k = j + 1;
                while (k < 4)
                {
                    int nextcell = map[i][k];
                    if (nextcell != 0)
                    {
                        if (cell == nextcell)
                        {
                            flag = 1;//相邻两个元素相同,就说明能移动,所以改变flag的值
                            map[i][j] += map[i][k];
                            map[i][k] = 0;
                        }
                        k = 4;
                        break;
                    }
                    k++;
                }
            }
        }
    }
 
    //修改部分:for循环中的i或者j的循环条件
 
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4 - 1; j++)
        {
            int cell = map[i][j];
            if (cell == 0)
            {
                int k = j + 1;
                while (k < 4)
                {
                    int nextcell = map[i][k];
                    if (nextcell != 0)
                    {
                        flag = 1;
                        map[i][j] = nextcell;
                        map[i][k] = 0;
                        k = 4;
                    }
                    k++;
                }
            }
        }
    }
    if (flag)
        return 0;
    else
        return 3;
}
 
 
//
//向下移动 
// 
int moveDown() {
    int flag=0;
    for (i = 4 - 1; i >= 0; i--)
    {
        for (j = 0; j < 4; j++)
        {
            int cell = map[i][j];
 
            if (cell != 0)
            {
                int k = i - 1;
                while (k >= 0)
                {
                    int nextcell = map[k][j];
                    if (nextcell != 0)
                    {
                        if (map[i][j] == map[k][j])
                        {
                            flag = 1;
                            map[i][j] += map[k][j];
                            map[k][j] = 0;
                        }
                        k = 0;
                        break;
                    }
                    k--;
                }
            }
        }
    }
 
    //修改部分:for循环中的i或者j的循环条件
    for (i = 4 - 1; i > 0; i--)
    {
        for (j = 0; j < 4; j++)
        {
            int cell = map[i][j];
            if (cell == 0)
            {
                int k = i - 1;
                while (k >= 0)
                {
                    int nextcell = map[k][j];
                    if (nextcell != 0)
                    {
                        flag = 1;
                        map[i][j] = nextcell;
                        map[k][j] = 0;
                        k = 0;
                    }
                    k--;
                }
            }
        }
    }
    if (flag)
        return 0;
    else
        return 2;
}
 
//
//向上移动
// 
int moveUp() {
    int flag=0;
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            int cell = map[i][j];
 
            if (cell != 0)
            {
                int k = i + 1;
                while (k < 4)
                {
                    int nextcell = map[k][j];
                    if (nextcell != 0)
                    {
                        if (cell == nextcell)
                        {
                            flag = 1;
                            map[i][j] += map[k][j];
                            map[k][j] = 0;
                        }
                        k = 4;
                        break;
                    }
                    k++;
                }
            }
        }
    }
 
    //修改部分:for循环中的i或者j的循环条件
    for (i = 0; i < 4 - 1; i++)
    {
        for (j = 0; j < 4; j++)
        {
            int cell = map[i][j];
            if (cell == 0)
            {
 
                int k = i + 1;
                while (k < 4)
                {
                    int nextcell = map[k][j];
                    if (nextcell != 0)
                    {
                        flag = 1;
                        map[i][j] = nextcell;
                        map[k][j] = 0;
                        k = 4;
                    }
                    k++;
                }
            }
        }
    }
    if (flag==0)
        return 0;
    else
        return 1;
}
 
 
//
//随机生成在空格上生成2或者4,并且判断是否游戏结束 
//
int randInteger()  //有缺陷,【0 0 0 0】
{
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            if (map[i][j] == 0)
            {
                map[i][j] = (rand() % 2 + 1) * 2;   //
                return 0;
            }
        }
    }
    return 0;
}
 
 
 
int main()
{
    char key;
    while (1)
    {
        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 0;
        }
    }
    return 0;
}

 

posted @ 2019-07-16 09:57  Timcode  阅读(927)  评论(1编辑  收藏  举报