数据结构算法 - 1 算法简介


数据结构算法 - 目录



一、数据结构算法 - 算法简介


1. 算法的基本概念

1.1 什么是算法

算法(Algorithm),就是“计算方法”,指解决一个问题具体的步骤和方法。

对于计算机而言,就是一系列解决问题的清晰指令。也就是说,对于一个问题,我们可以通过算法来描述解决的策略和步骤;对于规范的输入,按照算法描述的步骤,就可以在有限时间内得到对应的输出。

1.2 为什么要学习算法

首先,算法是计算机程序的核心。在一个计算机程序中,有两个非常重要的部分:数据结构和算法。数据结构决定了程序中数据组织和存储的形式,而算法决定了程序的功能和效率。算法在具体应用时,与数据结构密不可分,是一个程序的灵魂。

其次,算法是程序员的核心竞争力。算法是解决问题的方法和步骤,所以掌握了算法,就可以拥有更强的解决问题的能力。对于一个程序员而言,这往往比学会用一个框架、一个工具更加重要。

再次,算法是IT大厂面试的必考环节。大家可能知道,在IT特别是互联网公司的面试过程中,数据结构和算法是非常重要的一部分,为什么大厂都青睐于考察算法呢?总结起来,考察的原因以下几点:

  • 看一个程序员的技术功底是否扎实,考算法是最好的方式;
  • 看一个程序员的学习能力,和成长潜力,最好就是看他的算法能力;
  • 算法能力强,可以在面对新问题时,有更强的分析并解决问题的能力;
  • 算法能力,是设计一个高性能系统、性能优化的必备基础。

所以,算法是程序员绕不过去的必修课,也是走向架构师的必经之路。

1.3 怎样学习算法

首先,在学习算法之前,应该至少熟练掌握一门编程语言。本课程中的代码实现,都会以java为例来讲解。

其次,算法和数据结构密不可分,在系统学习算法之前,最好能够对基本的数据结构,数组、链表、哈希表、栈、队列、树都有充分的了解。在后续的课程中,我们会以算法讲解为主、穿插复习一些数据结构的基本知识。当然,算法本身是解题方法,有些也是不依赖于数据结构的,所以即使没有系统学过数据结构,同样可以开始算法的学习。

最后,算法学习的捷径,就是用算法去解决大量的具体问题,也就是通常所说的“刷题”。如果目的在于通过大厂面试,那这一步更是必不可少的准备。最经典的刷题网站,毫无疑问就是leetcode(力扣)。所以我们接下来的学习过程中,就会以leetcode的原题为例,分门别类进行算法的讲解,在具体解题的过程中巩固深化算法的学习。

2. 算法的特征

一个算法应该具有以下五个重要的特征:

  • 有穷性(Finiteness)
    算法的有穷性,是指算法必须能在执行有限个步骤之后终止。
  • 确切性(Definiteness)
    算法的每一步骤必须有确切的定义。
  • 输入项(Input)
    一个算法有0个或多个输入,以刻画运算对象的初始情况。所谓0个输入,是指算法本身定出了初始条件。
  • 输出项(Output)
    一个算法有一个或多个输出,以反映对输入数据加工后的结果。
  • 可行性(Effectiveness)
    算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。

3. 算法复杂度

基于算法的有穷性,我们可以知道算法运行消耗的时间不能是无限的。而对于一个问题的处理,可能有多个不同的算法,它们消耗的时间一般是不同的;运行过程中占用的空间资源也是不同的。

这就涉及到对算法的性能考察。主要有两方面:时间和空间。在计算机算法理论中,用时间复杂度和空间复杂度来分别从这两方面衡量算法的性能。

3.1 时间复杂度(Time Complexity)

算法的时间复杂度,是指执行算法所需要的计算工作量。

一般来说,计算机算法是问题规模n 的函数f(n),算法的时间复杂度也因此记做:T(n)=Ο(f(n))。

问题的规模n 越大,算法执行的时间的增长率与f(n) 的增长率正相关,称作渐进时间复杂度(Asymptotic Time Complexity)。

3.2 空间复杂度

算法的空间复杂度,是指算法需要消耗的内存空间。有时候做递归调用,还需要考虑调用栈所占用的空间。

其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示。同时间复杂度相比,空间复杂度的分析要简单得多。
所以,我们一般对程序复杂度的分析,重点都会放在时间复杂度上。

3.3 时间复杂度的计算

要想衡量代码的“工作量”,我们需要将每一行代码,拆解成计算机能执行一条条“基本指令”。这样代码的执行时间,就可以用“基本指令”的数量来表示了。

真实的计算机系统里,基本指令包括:

算术指令(加减乘除、取余、向上向下取整)、数据移动指令(装载、存储、赋值)、控制指令(条件或无条件跳转,子程序调用和返回)。

我们来看一些具体的代码,分析一下它们的时间复杂度:

  • int a = 1;
    简单赋值操作,运行时间 1(1个单位)
  • if (a > 1) {}
    简单判断操作、条件跳转,运行时间 1
  • for (int i = 0; i < N; i++) { System.out.println(i); }
    有循环,运行时间 1(i赋初值)+ N+1(判断)+N(打印)+N(i自增)= 3N + 2

3.4 复杂度的大O表示法

比起代码具体运行的时间,我们更关心的是,当它的输入规模增长的时候,它的执行时间我们是否还能够接受。

不同的算法,运行时间随着输入规模 n 的增长速度是不同的。我们可以把代码执行时间,表示成输入规模n的函数T(n)。
在这里插入图片描述
在算法分析中,一般用大O符号来表示函数的渐进上界。对于给定的函数g(n),我们用O(g(n))来表示以下函数的集合:

  • O(g(n)) = { f(n): 存在正常量 c 和 n0,使对所有 n≥n0 ,有 0≤f(n) ≤ cg(n) }

这表示,当数据量达到一定程度时,g(n) 的增长速度不会超过 O(g(n))限定的范围。也就是说,大O表示了函数的“阶数”,阶数越高,增长趋势越大,后期增长越快。

下图画出了常见的算法复杂度:
在这里插入图片描述

4. 算法的分类

可以用两种不同的原则,来对算法做一个分类整理:

  • 按照应用的目的来划分
    搜索算法、排序算法、字符串算法、图算法、最优化算法、数学(数论)算法
  • 按照具体实现的策略划分
    暴力法、增量法、分治法、贪心、动态规划、回溯、分支限界法
    在这里插入图片描述

5. 经典算法

在实际应用中,有一些经典算法和策略,都可以作为解决问题的思路:

  • 二分查找
  • 快速排序、归并排序
  • KMP算法
  • 快慢指针(双指针法)
  • 普利姆(Prim)和 克鲁斯卡尔(Kruskal)算法
  • 迪克斯特拉(Dijkstra)算法
  • 其它优化算法:模拟退火、蚁群、遗传算法

接下来,我们就不同的数据结构和算法策略进行分类,用不同的算法在各章节中解决某一类问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qumy97

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

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

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

打赏作者

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

抵扣说明:

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

余额充值