[C语言]数据结构算法篇

一、首先由一个简单的问题讲一下算法

1+...+100=    这是小学时候的一道比较难的算法题,那么怎样解决呢?
下面给大家写一个比较简单的代码

int i,sum = 0, n = 100;   /*定义一个整数型字符i,sum 的初始值为0,n赋值为100*/

for(i = 1; i < = n; i++)           /*i 开始循环的初始值为1,i++是i每次自增1,直到增加到i<=100*/

{

        sum = sum + i;        /*sum是所有值的总和*/

}

printf("%d" ,sum);      /*最后打印出sum的值*/

OK,这个问题完美的用6行代码解决了,但是是不是还不够短呢?

那让我们再想想怎样剪短呢?高斯解释道1+到100这之间有很多个数加起来得到100

直接套入公式就好了, 

int i, sum = 0; n = 100;   /*定义i为整型,sum为0,即最终结果用sum来存储,n赋值为100*/
sum = (1+n) * n / 2;
printf("%d", sum);          /*打印输出5050*/

二、算法的定义

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

它的特征是:输入、输出、有穷性、确定性和可行性。

输入输出:

算法具有零个或多个输入。算法至少有一个或多个输出,算法是一定需要输出的。

有穷性:

算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。

确定性:

算法的每一步骤都具有确定的含有,不会出现二义性。

可行性:

算法的每一步都必须是可行的,也就是说,每一步都能通过执行有限次数完成。

正确性:

算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。

可读性:

算法设计的另一目的是为了便于阅读、理解和交流。

健壮性:

当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结构。

设计算法应该尽量满足时间效率高和存储量低的需求。

三、算法效率的度量方法

1、通过设计好的测试程序和数据,利用计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。

一个程序的运行时间,依赖于算法的好坏和问题的输入规模。所谓问题输入规模是指输入量的多少。

2、某个算法,随着n的增大,它会越来越优于另一算法,或者越来越差于另一算法。

四、算法时间复杂度

在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并决定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n) =O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模的n 的某个函数

1)推导大O阶方法

1.用常数1取代运行时间中的所有加法常数。

2.在修改后的运行次数函数中,只保留最高阶项。

3.如果在最高阶项存在且不是1,则去除与这个项相乘的常数。得到的结果就是大O阶。

2)常数阶

顺序结构的时间复杂度。

int sum = 0,n = 100;   /*执行一次*/
sum = (1+n) * n/2;     /*执行一次*/
printf("%d, sum");     /*执行一次*/

 另外,我们试想一下,如果这个算法当中的语句 sum = (1+n) *n/2  有10句,
 即:

int sum = 0, n = 100;    /*执行1次*/
sum =  (1+n) *n/2;       /*执行第1次*/
sum =  (1+n) *n/2;       /*执行第2次*/
sum =  (1+n) *n/2;       /*执行第3次*/
sum =  (1+n) *n/2;       /*执行第4次*/
sum =  (1+n) *n/2;       /*执行第5次*/
sum =  (1+n) *n/2;       /*执行第6次*/
sum =  (1+n) *n/2;       /*执行第7次*/
sum =  (1+n) *n/2;       /*执行第8次*/
sum =  (1+n) *n/2;       /*执行第9次*/
sum =  (1+n) *n/2;       /*执行第10次*/

五、线性阶

要确定某个算法的复杂度,关键就要分析循环结构的运行情况。
下面这段代码,它的循环的时间复杂度为 O(n),因为循环中的代码须要执行n次。

int i;
for (i = 0; i < n; i++)
{
/*时间复杂度为O(1)程度步骤序列*/
}

1)对数阶

下面这段代码,时间复杂度是?

int count = i;
while (count < n)
{
count = count * 2;
/*时间复杂度为O(1)的程序步骤序列*/
}

2)平方阶

下面例子是一个循环嵌套,时间复杂度为O(n)。

int i,j;
	for (i = 0; i < n; i++)
	{
	/*时间复杂度为O(1)的程序步骤序列*/
	}
}

​​​​​​​for (j = 0; j < n; j++)
{
    /*时间复杂度为O(1)的程序步骤序列*/
}

所以我们可以总结出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。

那么下面这个循环嵌套,它的时间复杂度是多少呢?

int i,j;
for (i = 0; i < n;  i++)
{
	for (j = i; j < n; j++)        /*注意j = i 而不是0*/
	{
		/*时间复杂度为O(1)的程序步骤序列*/
	}
}

由于当i = 0 时,内循环执行了n次,当i = 1时,执行了n-1次,.......当i=n-1时,执行了1次。所以总的执行次数为。

这样就可以得到一个经验:其实理解大O推理不算难,难的是对数列的一些相关运算,这更多的是考察数学知识能力。
我们继续看例子,对于方法调用的时间复杂度又如何分析。
int i,j;
for (i = 0;i <n;i++)
{
    function(i);
}

上面这段代码调用一个函数function。
void function (int count)
{
     print(count);
}

下面这段相对复杂的语句:

n++;                  /*执行次数为1*/
function(n);       /*执行次数为n*/
int i,j;
for (i = 0; i < n; i++)   /* 执行次数为n^2(n的平方) */
{
	function (i);
}
for (i = 0; i < n; i++)             /* 执行次数为n(n+1)/2 */
{
	for (j = i;j < n;j++)
	{
		/*时间复杂度为O(1)的程序步骤序列*/
}

六、常见的时间复杂度

七、最坏的情况与平均的情况

1、最坏情况运行时间是一种保证,那就是运行时间将不会再坏了。在应用中,这是一种最重要的需求,通常,除非特别设定,我们提到的运行时间都是最坏情况的运行时间。
2、平均运行时间是所有情况中最有意义的,因为它是期待的运行时间。

八、算法空间复杂度


算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

完结撒花

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值