算法小技巧:空间换时间,时间换空间?

前言:小细节,大道理,思路在前,代码在后。

名词解释:        

        算法效率:往往由时间效率和空间效率两个方面决定。

        时间效率:时间效率被称为时间复杂度,指的是算法执行过程耗费的时间。

        空间效率:空间效率被称为空间复杂度,指的是算法执行过程所耗费的最大的存储空间。 

        有时候时间效率与空间效率是相互矛盾的,要时间则要牺牲空间,要空间则要牺牲时间。而两者的衡量选择,往往是需要我们人来决定的。

空间换时间:

        用空间换时间: 当内存空间充足的时候,如果我们更加追求代码的执行速度,就可以选择空间复杂度相对较高、但时间复杂度相对很低的算法或者数据结构。

        数据结构与算法往往是来源于我们的生活,举一个生活中的案例:看下面小哥积攒了一个月的袜子,(小编只能说自己读书的时候也是这样干的),其实这也是以空间换时间的一个方式,我们来分析一下。

        洗一双:拿袜子,拿盆,洗袜子、收袜子...  这些都需要时间

        如果每次洗一双,洗完所有袜子,就需要将以上时间倍数相乘。

        理论上,袜子积攒越多(空间占用越大),洗袜子总耗时越少(时间效率越高)。

        (当然,生活中不建议这样做,因为我们是人,不是机器代码)

        言归正传,在计算机中以空间换时间的案例很多很多,例如缓存、索引缓存实际上就是利用了空间换时间的设计思想。如果我们把数据存储在硬盘上,会比较节省内存,但每次查找数据都要询问一次硬盘,会比较慢。但如果我们通过缓存技术,事先将数据加载在内存中,虽然会比较耗费内存空间,但是每次数据查询的速度就大大提高了。

        索引结构通常需要额外的存储空间,但是会提高查询效率。例如B+树是在磁盘上的数据记录之外又建立了一层树形索引,因此需要额外的存储空间,但与此同时用索引可以更快找到需要的记录。

时间换空间:

        用时间换空间:当内存比较紧缺时,例如代码跑在手机或者单片机上,这个时候,就要反过来用时间换空间的设计思路。

        举一个生活中案例:错峰吃饭。你有没有过这样的经历,在某些公共食堂(人多,地方小)吃饭时,吃饭往往会划分为好几个时间段(小编还记得当初被同事拉去第一波吃饭,被部门领导发现后的尬尴场景)。

        在计算机中以时间换空间的案例也很多(不过随着硬件资源的不断提高,这个方向相对不是热门),例如数据压缩。压缩和解压缩时以时间代价,换取更小的空间。通过应用适当的编码(例如哈夫曼树)算法,我们可以将原始数据用更少的比特数表示,然而经过编码的数据通常需要经过解压缩才能处理,如果需要更新数据也要解压缩后加入新数据再重新压缩。

小结:

        总结,程序是为了解决某一个或多个问题的,而具体实现它的方式即为算法,而衡量算法的优劣则有两个考量标准,就是时间效率和空间效率。对于执行较慢的程序,可以通过消耗更多的内存(空间换时间)来进行优化而消耗过多内存的程序,可以通过消耗更多的时间(时间换空间)来降低内存的消耗。


典型案例:

        以下两个简单案例,分别演示空间换时间和时间换空间的具体代码。思维思路很重要,下面感受一波,具体还是要根据实际情况去实现。一般优先考虑空间换时间(实际工作中,往往不仅要考虑高效、稳定和维护问题,成本和开发周期等现实问题也需要重视)

案例一:以空间换时间

        以经典求润年为案例,分析时间与空间的关系。

题目:输入一个从2000到3000 的年份(2000-3000),判断其是否为 “ 闰年 ”。

以下两个条件满足其一即为润年:

  1. 能被4整除而不能被100整除。
  2. 能被400整除。

两个思路:

思路一:直接求润年,根据输入的年份year进行逻辑判断,满足条件即为润年,否则不是润年。(一般直接算法)

#include<stdio.h>

int main()
{
    int year;
    printf("请输入2000-3000的数字:");
    scanf("%d",&year);

    if((year%400==0)||((year%4==0)&&(year%100!=0))){
        printf("%d是润年\n",year);
    }else{
        printf("%d不是润年\n",year);
    }
    return 0;
}

思路二:将2000-3000内为润年的年份放在数组arr中,将输入的年份year与数组arr中的元素进行比较,如果year存在于数组中,则为润年,否则不是润年。(以空间换时间)

#include<stdio.h>

int main()
{
    int arr[]={2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040,2044,
    2048,2052,2056,2060,2064,2068,2072,2076,2080,2084,2088,2092,2096,2104,
    2108,2112,2116,2120,2124,2128,2132,2136,2140,2144,2148,2152,2156,2160,
    2164,2168,2172,2176,2180,2184,2188,2192,2196,2204,2208,2212,2216,2220,
    2224,2228,2232,2236,2240,2244,2248,2252,2256,2260,2264,2268,2272,2276,
    2280,2284,2288,2292,2296,2304,2308,2312,2316,2320,2324,2328,2332,2336,
    340,2344,2348,2352,2356,2360,2364,2368,2372,2376,2380,2384,2388,2392,
    2396,2400,2404,2408,2412,2416,2420,2424,2428,2432,2436,2440,2444,2448,
    452,2456,2460,2464,2468,2472,2476,2480,2484,2488,2492,2496,2504,2508,
    2512,2516,2520,2524,2528,2532,2536,2540,2544,2548,2552,2556,2560,2564,
    2568,2572,2576,2580,2584,2588,2592,2596,2604,2608,2612,2616,2620,2624,
    2628,2632,2636,2640,2644,2648,2652,2656,2660,2664,2668,2672,2676,2680,
    2684,2688,2692,2696,2704,2708,2712,2716,2720,2724,2728,2732,2736,2740,
    2744,2748,2752,2756,2760,2764,2768,2772,2776,2780,2784,2788,2792,2796,
    2800,2804,2808,2812,2816,2820,2824,2828,2832,2836,2840,2844,2848,2852,
    2856,2860,2864,2868,2872,2876,2880,2884,2888,2892,2896,2904,2908,2912,
    2916,2920,2924,2928,2932,2936,2940,2944,2948,2952,2956,2960,2964,2968,
    2972,2976,2980,2984,2988,2992,2996};
    int year;
    int i=0;
    int len=sizeof(arr)/sizeof(int);
    printf("请输入2000-3000的数字:");
    scanf("%d",&year);

    for(i=0;i<len;i++){
        if(year==arr[i]){
            printf("%d是润年\n",year);
            return 0;
        }
    }
    printf("%d不是润年\n",year);
    return 0;
}

案例二:以时间换空间

        以交换两个int类型数据为例,分析时间和空间的关系。

题目:给两个int类型数据,要求实现数据交换。

法一:直接设置第三个int类型变量,作为交换数据的中转站。(常用方式)

#include<stdio.h>
 
int main()
{
	int a=5;
	int b=6;
	int temp=0;//设置int类型变量temp作为数据a与数据b的周转容器
	
	printf("交换前a=%d,b=%d\n",a,b);
	temp=a;
	a=b;
	b=temp;
	printf("交换后a=%d,b=%d\n",a,b);
	
	return 0;
 } 

方法二:利用数据加减的性质,进行运算后达到数据交换的目的。(相对方法一:少了一个int类型变量,空间占用小;多了算术运算,时间效率下降。)

#include<stdio.h>
 
int main()
{
	int a=5;
	int b=6;
	//数据a的容器起到了周转数据的作用
	printf("交换前a=%d,b=%d\n",a,b);
	a=a+b;//此时容器a存储的是a与b的数据之和
	b=a-b;//此时容器b存储的是数据a
	a=a-b;//此时容器a存储的是数据b
	printf("交换后a=%d,b=%d\n",a,b);
	
	return 0;
 } 

都看到这里了,一键三连啊,收藏点赞+关注!

后面陆续更新算法技巧。

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SecureCode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值