前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >程序员必须掌握哪些算法和数据结构?

程序员必须掌握哪些算法和数据结构?

作者头像
CSDN技术头条
发布于 2019-08-06 09:09:24
发布于 2019-08-06 09:09:24
44300
代码可运行
举报
文章被收录于专栏:CSDN技术头条CSDN技术头条
运行总次数:0
代码可运行

作为一名程序员,大家有没有想过:编码最本质的知识是什么?或许是算法和数据结构,至少很多人这么认为。

本场 Chat 从以下几个方面讨论算法的性能:

  1. 算法研究的科学方法;
  2. 编写衡量算法的时间性能类 StopWatch;
  3. ThreeSum 的例子阐述算法的方方面面;
  4. 衡量时间复杂度的一种简单度量:波浪线表示;
  5. 一些典型的 Order of Growth, 比如 log2n, n, nlog2n, n2 , n3;
  6. 分析 Java 中各种类型的内存消耗,包括原生类型,对象类型,字符串和数组。

1. 科学方法(Scientific method)

以下 5 个步骤总结了此方法,依次为如下,我们设计的实验必须是可以重现的,我们形成的假设必须是具有真伪的。

2. 时间度量

测试程序运行的精确时间有时是困难的,但是我们有许多辅助工具。在这里,我们简化程序运行时间的模型,考虑各种输入情况,并测试每种情况下的运行时间,编写的这个程序名称为:Stopwatch.java,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 public class Stopwatch { 
 2
 3    private final long start;
 4
 5    public Stopwatch() {
 6        start = System.currentTimeMillis();
 7    }
 8
 9    public double elapsedTime() {
10        long now = System.currentTimeMillis();
11        return (now - start) / 1000.0;
12    }
13
14    public static void main(String[] args) {
15        int n = Integer.parseInt(args[0]);
16
17        // sum of square roots of integers from 1 to n using Math.sqrt(x).
18        Stopwatch timer1 = new Stopwatch();
19        double sum1 = 0.0;
20        for (int i = 1; i <= n; i++) {
21            sum1 += Math.sqrt(i);
22        }
23        double time1 = timer1.elapsedTime();
24        StdOut.printf("%e (%.2f seconds)\n", sum1, time1);
25
26        // sum of square roots of integers from 1 to n using Math.pow(x, 0.5).
27        Stopwatch timer2 = new Stopwatch();
28        double sum2 = 0.0;
29        for (int i = 1; i <= n; i++) {
30            sum2 += Math.pow(i, 0.5);
31        }
32        double time2 = timer2.elapsedTime();
33        StdOut.printf("%e (%.2f seconds)\n", sum2, time2);
34    }
35}

对于大多数程序,首先我们能想到的量化观察是它们有问题的大小(problem size)区别,这个表征了计算复杂度或计算难度。

一般地,问题大小既可以指通过输入数据的大小,也可以指通过命令行参数输入值。

直觉上,运行时间应该会随着问题大小而增加,但是增加的程度怎么度量,这是我们编程运行程序时常遇到的问题。

3. ThreeSum 例子

为了阐述方法,我们先引入一个具体的编程问题:ThreeSum,它是在给定的含有 n 个元素的数组中找出三元组之和等于 0 的个数。

这个问题最简单的一个解法:枚举。代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 public class ThreeSum {
 2
 3    // print distinct triples (i, j, k) such that a[i] + a[j] + a[k] = 0
 4    public static void printAll(int[] a) {
 5        int n = a.length;
 6        for (int i = 0; i < n; i++) {
 7            for (int j = i+1; j < n; j++) {
 8                for (int k = j+1; k < n; k++) {
 9                    if (a[i] + a[j] + a[k] == 0) {
10                        StdOut.println(a[i] + " " + a[j] + " " + a[k]);
11                    }
12                }
13            }
14        }
15    }
16
17    // return number of distinct triples (i, j, k) such that a[i] + a[j] + a[k] = 0
18    public static int count(int[] a) {
19        int n = a.length;
20        int count = 0;
21        for (int i = 0; i < n; i++) {
22            for (int j = i+1; j < n; j++) {
23                for (int k = j+1; k < n; k++) {
24                    if (a[i] + a[j] + a[k] == 0) {
25                        count++;
26                    }
27                }
28            }
29        }
30        return count;
31    }
32
33    public static void main(String[] args)  {
34        int[] a = StdIn.readAllInts();
35        Stopwatch timer = new Stopwatch();
36        int count = count(a);
37        StdOut.println("elapsed time = " + timer.elapsedTime());
38        StdOut.println(count);
39    }
40}

我们在这里主要关心的是输入数据大小与运行时长的关系。我们循着如下的思路分析两者间的关系。

3.1 加倍假设

加倍假设(Doubling hypothesis)对于大量的程序而言,我们能很快地形成如下假设:假如输入数据的个数加倍,运行时间怎么变化。

3.2 经验分析

经验分析(Empirical analysis)一种简单的实现加倍假设的方法是使输入数据的个数加倍,然后观察对运行时长的影响。

如下所示为一个简单的通过加倍输入个数,测试运行时长的程序:DoublingTest.Java。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1public class DoublingTest {
 2
 3    public static double timeTrial(int n) {
 4        int[] a = new int[n];
 5        for (int i = 0; i < n; i++) {
 6            a[i] = StdRandom.uniform(2000000) - 1000000;
 7        }
 8        Stopwatch s = new Stopwatch();
 9        ThreeSum.count(a);
10        return s.elapsedTime();
11    }
12
13
14    public static void main(String[] args) {
15        StdOut.printf("%7s %7s %4s\n", "size", "time", "ratio");
16        double previous = timeTrial(256);
17        for (int n = 512; true; n += n) {
18            double current = timeTrial(n);
19            StdOut.printf("%7d %7.2f %4.2f\n", n, current, current / previous);
20            previous = current;
21        }
22    }
23}

再回到 ThreeSum.java 程序中,我们生成一系列随机数,填充到输入数组中,每一个时步都加倍输入元素个数,然后观察到每一次程序所运行的时间都会大致增加 8 倍,这个就可以让我们下结论说输入数据加倍后运行时间增加了 8 倍。

如下图中左侧视图所示,当输入大小为 4K 时,运行时长为 64T,当输入带下为 8K 时,运行时长变为原来的 8 倍:512T。

Log–logplot:绘制 log 图也是衡量运行时间的一种方法,因为是log级别的,所以与上面说的经验公式没有本质区别,如图右所示,取完对数后,输入数据大小与运行时间的对数变为线性关系。

3.3 数学分析

总的运行时长受到两个主要指标影响:

  • 执行每一个语句的成本
  • 执行每一个语句的次数

其中,第一个是系统属性,后面一个是算法的属性。假如我们知道了程序中所有指令的这两个属性值,那么两者相乘求和后便是整个程序的运行时间。

主要的挑战是确定第二个指标,即语句的执行次数。一些语句是很容易分析出执行次数,比如将 count 设置初始值为 0,在 ThreeSum.count() 中仅仅执行一次。

但是,有些需要更高层次的推理演算才能得到语句的执行频次,比如 if 语句被执行的次数恰好为:n(n-1)(n-2)/6。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GitChat精品课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
程序员必知的算法和数据结构:2500字性能总结
以下5个步骤总结了此方法,依次为如下,我们设计的实验必须是可以重现的,我们形成的假设必须是具有真伪的。
double
2018/07/31
3190
求解亲密数代码剖析
这里是自定义了一个求因数和的函数,不建议使用的原因是,它会重复计算大量数据,从而消耗大量时间,不过这个算法却容易理解
一枕眠秋雨
2024/03/11
1510
【Python3】04、内置数据结构
1、把字符串形式的整数或浮点数转化为int或float, 不适用int和float函数
py3study
2020/01/03
2680
英雄会第二届在线编程大赛·线上初赛:AB 题解
集合L:即把1~a,1~b中整数乘积的集合定义为L = {x * y | x,y是整数且1 <= x <=a , 1 <= y <= b};
井九
2024/10/12
930
数据结构与算法 --- 复杂度分析专题(一)
算法复杂度分析的意义在于评估算法的执行效率,找出最优解决方案,是优化算法和改进程序性能的基础。通过对算法的时间复杂度和空间复杂度进行分析,可以帮助我们预估该算法运行所需的资源,从而提高程序的性能。
Niuery Diary
2023/10/22
3730
数据结构与算法 --- 复杂度分析专题(一)
C#版 - Leetcode204. Count Primes - 题解
在线提交: https://leetcode.com/problems/count-primes/description/
Enjoy233
2019/03/05
6430
C#版 - Leetcode204. Count Primes - 题解
LeetCode Weekly Contest 26解题思路
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014688145/article/details/68953387
用户1147447
2019/05/26
3470
ACM算法基础
N3/6-N2/2+N/3 ~ N3/6。使用 ~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数。
爱撸猫的杰
2019/03/29
1.9K0
【数据结构其实真不难】算法分析
前面我们已经介绍了,研究算法的最终目的就是如何花更少的时间,如何占用更少的内存去完成相
陶然同学
2023/02/27
3340
【数据结构其实真不难】算法分析
1-归并排序-算法复习
要了解归并排序算法首先要了解归并这一过程,归并过程处理两个可比较数组(两个数组已经各自有序),在归并过程中,不断对两个数组的当前首元素进行比较,将较小的元素放置到新数组的下一位置。
Ywrby
2022/10/27
3010
【数据结构】初识数据结构与复杂度总结
数据结构是计算机存储,组织数据的方式,指的是相互之间存在一种或多种特定关系的数据元素的集合
用户11290673
2024/09/25
900
【数据结构】初识数据结构与复杂度总结
数据结构初步(二)- oj练习-时间与空间复杂度
进阶: 尽可能想出更多的解决方案,至少有三种 不同的方法可以解决这个问题。 你可以使用空间复杂度为
怠惰的未禾
2023/04/27
2670
数据结构初步(二)- oj练习-时间与空间复杂度
算法设计的艺术:探索时间复杂度和空间复杂度的计算方法
可以看到,第二种算法非常高效。第一种算法需要执行n次,而第二种算法只需要执行1次。
Lion 莱恩呀
2024/11/08
1340
算法设计的艺术:探索时间复杂度和空间复杂度的计算方法
数据结构----算法复杂度
数据结构(Data Structure)是计算机存储、组织数据的⽅式,指相互之间存在⼀种或多种特定关系的数
Undoom
2024/09/23
1100
数据结构----算法复杂度
数据结构与算法(一)
算法的概念 算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后再调用。 算法是独立存在的一种解决问题的方法和思想。 对于算法而言,实现的语言并不重要,重要的是思想。 算法可以有不同的语言描述实现版本(如C描述、C++描述、Python描述等),我们现在是在用Python语言进行描述实现。 算法的五大特性 输入: 算法具有0个或多个输入 输出: 算法至少有
酱紫安
2018/04/16
1.1K0
数据结构与算法(一)
前端学数据结构与算法(一):不会复杂度分析,算法等于白学
兜兜转转了这么久,数据结构与算法始终是逃不过命题。曾几何时,前端学习数据结构与算法,想必会被认为不务正业,但现今想必大家已有耳闻与经历,面试遇到链表、树、爬楼梯、三数之和等题目已经屡见不鲜。想进靠谱大厂算法与数据结构应该不止是提上日程那么简单,可能现在已经是迫在眉睫。这次决定再写一个系列也只是作为我这段时间的学习报告,也不绝对不会再像我之前的vue原理解析那般断更了,欢迎大家监督~
飞跃疯人院
2020/10/07
9360
【数据结构与算法】数组
在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识
程序员波特
2024/09/24
1110
【数据结构与算法】数组
Python数据结构与算法 列表和字典性能比较
可以看到,4种方法运行时间差别挺大的,列表连接(concat)最慢,List range最快,速度相差近 100 倍。append要比 concat 快得多。另外,我们注意到列表推导式速度大约是 append 两倍的样子。
叶庭云
2021/12/01
1K0
Python数据结构与算法 列表和字典性能比较
数据结构与算法(复杂度)
风中的云彩
2024/11/07
1150
数据结构与算法(复杂度)
数据结构与算法Python_数据结构与算法python语言实现
我们已经知道算法是具有有限步骤的过程,其最终的目的是为了解决问题,而根据我们的经验,同一个问题的解决方法通常并非唯一。这就产生一个有趣的问题:如何对比用于解决同一问题的不同算法?为了以合理的方式提高程序效率,我们应该知道如何准确评估一个算法的性能。 通过本节学习,应掌握以下内容:
全栈程序员站长
2022/11/10
4060
数据结构与算法Python_数据结构与算法python语言实现
推荐阅读
相关推荐
程序员必知的算法和数据结构:2500字性能总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验