【转】数据结构和算法分析

问题引出

假设有一道题目:有一组N个数而要确定其中第k个最大者,我们称之为选择问题,那么这个程序如何编写?最直观地,至少有两种思路:

1、将N个数读入一个数组中,再通过某种简单的算法,比如冒泡排序法,以递减顺序将数组排序,则第k个位置上的元素就是我们需要的元素

2、稍微好一些的做法,将k个元素读入数组并以递减顺序排序,接着将接下来的元素再逐个读入,当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则将其放到数组中正确的位置上,同时将数组中的一个元素挤出数组,当算法终止时,位于第k个位置上的元素作为答案返回

这两种算法都很简单,但是假设我们有一千万个元素的随机文件和k=5000000进行模拟将发现,两个算法尽管最终都可以给出正确答案,但是在合理时间内均无法结束。因此,这两种算法都不能被认为是好的算法,因为从实际角度出发,它们无法在合理的时间内处理输入的数据。

数据结构和算法分析的提出

在许多问题中,一个很重要的观念是:写出一个工作程序并不够。如果这个程序在巨大的数据集上运行,那么运行时间就变成了重要的问题,我们将在接下来的文章中看到对于大量的输入如何估计程序的运行时间,尤其是如何在未具体编码的情况下比较两个程序运行的时间。我们还将看到彻底改进程序速度以及确定程序瓶颈的方法,这些方法将使得我们能够发现需要我们集中精力努力优化的那些代码段。

那么,首先,先了解一下什么是数据结构和算法分析(特别指出,后文的例子均以Java代码编写)。

数据结构

数据结构是计算机存储、组织数据的方式,是指数据相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率(这就是为什么我们要研究数据结构的原因),数据结构往往同高效的检索算法和索引技术相关。

常见的数据结构有数组、栈、队列、链表、树、散列等,这些数据结构将是本数据结构的分类中重点研究的对象。

算法分析

算法是为求解一个问题需要遵循的、被清楚指定的简单指令的集合。对于一个问题,一旦某种算法给定并且(以某种方式)被确定是正确的,那么重要的异步就是确定该算法将需要多少注入时间或空间等资源量的问题。如果:

1、一个问题的求解算法竟然需要长达一年时间,那么这种算法就很难有什么用处

2、一个问题需要若干个GB的内存的算法,在当前大多数机器上也是无法使用的

数学基础

无论是数据结构还是算法分析,都用到了大量的数学基础,下面将这些数学的基础简单总结一下:

1、指数

(1)XAXB = XA+B

(2)XA/XB = XA-B

(3)(XA)B = XABsi

(4)XN + X= 2XN ≠ X2N

(5)2N + 2N = 2N + 1

2、对数

(1)X= B当且仅当logxB = A

(2)logAB = logCB / logCA

(3)logAB = logA + logB,A>0且B>0

3、级数

(1)∑2 = 2N+1 - 1

(2)∑Ai = (AN+1 - 1) / (A - 1),若0<A<1,则有∑A≤ 1 / (1 - A)

4、模运算

如果N整除A、N整除B,那么就说A与B模N同余,记为A≡B(mod N)。直观地看,这意味着无论是A还是B被N去除,所得余数都是相同的,于是假如有A≡B(mod N),则:

(1)A + C ≡ B + C(mod N)

(2)AD ≡ BD (mod N)

时间复杂度

在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数,时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数,使用这种方式时,时间复杂度可被称为是渐进的,他考察当输入值大小趋近无穷时的情况。

那么首先先看一个简单的例子,这里是计算Σi3的一个简单程序片段:

 1 public static void main(String[] args)
 2 {
 3     System.out.println(sum(5));
 4 }
 5
 6 public static int sum(int n)
 7 {
 8     int partialSum;
 9
10     partialSum = 0;
11     for (int i = 0; i <= n; i++)
12         partialSum += i * i * i;
13
14     return partialSum;
15 }

对这个程序片段的分析是简单的:

1、声明不记入时间

2、第10行和都14行各占一个时间单元

3、第12行每次执行占用4个时间单元(两次乘法、一次加法和一次赋值),而执行N次共占用4N个时间单元

4、第11行在初始化i,测试i≤n和对i的自增都隐含着开销,所有这些总开销是初始化1个时间单元,所有的测试为N+1个时间单元,所有自增为N个时间单元,共2N+2个时间单元

忽略调用方法和返回值的开销,得到总量是6N+4个时间单元,按照最开始的定义不包括这个函数的低阶项和首项系数,因此我们说该方法的时间复杂度是O(N)。继而,我们顺便得出若干个一般法则:

法则一----for循环

一个for循环的运行时间至多是该for循环内部那些语句(包括测试)的运行时间乘以迭代的次数,因此假如一个for循环迭代N次,那么其时间复杂度应该为O(N)

法则二----嵌套for循环

从里向外分析这些循环,在一组嵌套循环内部的一条语句总的运行时间为该语句的运行时间乘以该组所有的for循环的大小的乘积,因此假如有以下代码:

 1 public static int mutliSum(int n)
 2 {
 3     int k = 0;
 4     for (int i = 0; i < n; i++)
 5     {
 6         for (int j = 0; j < n; j++)
 7         {
 8             k++;
 9         }
10     }
11
12     return k;
13 }

则其时间复杂度应为O(N2)

法则三----顺序语句

将各个语句的运行时间求和即可,比如有以下代码:

 1 public static int sum(int n)
 2 {
 3     int k = 0;
 4
 5     for (int i = 0; i < n; i++)
 6         k++;
 7     for (int i = 0; i < n; i++)
 8     {
 9         for (int j = 0; j < n; j++)
10         {
11             k++;
12         }
13     }
14
15     return k;
16 }

第一个for循环的时间复杂度为N,第二个嵌套for循环的时间复杂度为N2,综合起来看sum方法的时间复杂度为O(N2)

常见的时间复杂度与时间效率的关系有如下的经验规则:

O(1) < O(log2N) < O(N) < O(N * log2N) < O(N2) < O(N3) < O(N!)

至于每种时间复杂度对应哪种数据结构和算法,后面都会讲到,从上面的经验规则来看:前四个算法效率比较高,中间两个差强人意,最后一个比较差(只要n比较大,这个算法就动不了了)。

来源

原文地址:https://www.cnblogs.com/skullboyer/p/8997833.html

时间: 2024-10-09 19:16:24

【转】数据结构和算法分析的相关文章

数据结构1:数据结构和算法分析

问题引出 假设有一道题目:有一组N个数而要确定其中第k个最大者,我们称之为选择问题,那么这个程序如何编写?最直观地,至少有两种思路: 1.将N个数读入一个数组中,再通过某种简单的算法,比如冒泡排序法,以递减顺序将数组排序,则第k个位置上的元素就是我们需要的元素 2.稍微好一些的做法,将k个元素读入数组并以递减顺序排序,接着将接下来的元素再逐个读入,当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则将其放到数组中正确的位置上,同时将数组中的一个元素挤出数组,当算法终止时,位于第k个位置上

《数据结构与算法分析》学习笔记(二)——算法分析

一.对算法分析方法的最简单的理解和使用方法 1.首先大家可能一般会被那些数学的概念搞晕,其实简单理解下来,就是假设任何语句执行的效率都是一样的,所以设定每一个语句的执行时间都是一个时间单位,那么只要计算这个程序到底执行了多少语句,就可以算出其时间复杂度. 2.其次就是我们要明白,我们是个估算,所以可以进行化简,明显我们可以忽略那些相对来说低阶的项,只分洗最高阶项.然后主要就是有这些常见的法则: (1)FOR循环 一次for循环的运行时间至多是该for循环内语句的运行时间乘以迭代次数. (2)嵌套

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝

2014.07.08 22:43 简介: “搜索”与“剪枝”几乎是如影随形的.此处的“搜索”指的是带有回溯算法的深度优先搜索. 在之前的“Minimax策略”中我们给出了一个三连棋的程序,运行后你就知道计算一步棋要花多少时间. 为了计算最优的一步棋,我们可能需要递归9万多次.如果毫无疑问这种阶乘式的穷举过程必须通过剪枝来加速. 本篇介绍一种用于Minimax策略的剪枝思路——α-β剪枝. 剪枝的英语是pruning,所以不要想当然说成trimming. 图示: 在上一篇讲解Minimax策略的博

《数据结构与算法分析:C语言描述》复习——第四章“树”——AVL树

2014.06.15 16:22 简介: AVL树是一种高度平衡的二叉搜索树,其命名源自于联合发明算法的三位科学家的名字的首字母.此处“平衡”的定义是:任意节点的左右子树的高度相差不超过1.有了这个平衡的性质,使得AVL树的高度H总是接近log(N),因此各种增删改查的操作的复杂度能够保证在对数级别.没有bad case是AVL树与普通的二叉搜索树的最大区别.为了实现平衡性质,我们需要记录每个节点的高度(或者平衡因子)来检测不平衡的情况.为了修正高度不平衡,需要用到“旋转”的方法,分为单旋转和双

《数据结构与算法分析—C语言描述》pdf

下载地址:网盘下载 内容简介 编辑 <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行时间分析的基础上考查了一些高级数据结构,从历史的角度和近年的进展对数据结构的活跃领域进行了简要的概括.由于<数据结构与算法分析:C语言描述(原书第2版)>选材新颖,方法实用,题例丰富,取舍得当.<数据结构与算法分析:C语言描述(原书第2版)>的目的是培养学生良好的程序设计技巧和熟练的算

《数据结构与算法分析》引论:选择问题实现

在<数据结构与算法分析--C语言描述>的引论中有提到一个问题: 设有一组N个数而要确定其中第k个最大者. 被称为选择问题(selection problem). 后面有提到两种算法,下面是我根据描述,写的代码: /* * 来源:<数据结构与算法分析>引论 * 题目:选择性问题,从N个数中找出第k大者 * * */ #include <stdio.h> #include <stdlib.h> /* * 输出数组 * */ void printfArray(in

《数据结构与算法分析:C语言描述》复习——第六章“排序”——冒泡排序

2014.06.17 01:04 简介: 冒泡排序是O(n^2)级别的交换排序算法,原理简单,属于必知必会的基础算法之一. 思路: 排序要进行N轮,每一轮从尾部逐个向前扫描,遇到逆序对就进行交换.确保每一轮把最小的元素交换到前面去.这个过程好比水中的气泡向上飘,所以叫冒泡排序.代码非常简单,所以语言描述反而显得麻烦了. 实现: 1 // My implementation for bubble sort. 2 #include <iostream> 3 #include <vector&

[数据结构与算法分析(Mark Allen Weiss)]二叉树的插入与删除 @ Python

二叉树的插入与删除,来自Mark Allen Weiss的<数据结构与算法分析>. # Definition for a binary tree node class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None class BinarySearchTree: # @param root, a tree node # @return a list of integers def

数据结构与算法分析-索引

作者:xiabodan 出处:http://blog.csdn.net/xiabodan 算法和数据结构是计算机科学的核心内容.作为程序猿,编程是我们的实战项目.然而,写出程序还不够.一个程序在应对一些大型而复杂的情况时.会耗费大量的时间.我们能够非常easy写出一个从文件里找到一个词的程序.比方逐词扫描.看是否相符.但假设我们的文件有几十TB,并且要从文件里找到上百个词,逐个扫描的办法就差点儿不可行.我们须要优化程序,以便我们的程序能够应对复杂问题. 算法研究解决这个问题的方法,而数据结构则是

数据结构和算法分析

转自[五月的仓颉] http://www.cnblogs.com/xrq730/p/5122436.html 数据结构 数据结构是计算机存储.组织数据的方式,是指数据相互之间存在一种或多种特定关系的数据元素的集合.通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率(这就是为什么我们要研究数据结构的原因),数据结构往往同高效的检索算法和索引技术相关. 常见的数据结构有数组.栈.队列.链表.树.散列等,这些数据结构将是本数据结构的分类中重点研究的对象. 算法分析 算法是为求解一个问题需要遵