Java 内功修炼 之 数据结构与算法(一 基本认识)

一、基本认识

1、数据结构与算法的关系?

(1)数据结构(data structure):
  数据结构指的是 数据与数据 之间的结构关系。比如:数组、队列、哈希、树 等结构。

(2)算法:
  算法指的是 解决问题的步骤。

(3)两者关系:
  程序 = 数据结构 + 算法。
  解决问题可以有很多种方式,不同的算法实现 会得到不同的结果。正确的数据结构 是 好算法的基础(算法好坏取决于 如何利用合适的数据结构去 处理数据、解决问题)。

2、数据结构分类?

(1)分类:
  数据结构 可以分为 两种:线性数据结构、非线性数据结构。

(2)线性数据结构:
  线性数据结构指的是 数据元素之间存在一对一的线性关系。比如:一维数组、链表、队列、栈。
其又可以分为:
  顺序存储结构:指的是 使用一组地址连续的存储单元 存储数据元素 的结构,其每个元素节点仅用于 保存数据元素。比如:一维数组。
  链式存储结构:指的是 可使用一组地址不连续的存储单元 存储数据元素 的结构,其每个元素节点 保存数据元素 以及 相邻数据元素的地址 信息。比如:链表。

(3)非线性数据结构:
  非线性数据结构指的是 数据元素之间存在 一对多、多对多 的关系。比如:二维数组、多维数组、树、图 等。

3、时间复杂度、空间复杂度

(1)分析多个算法执行时间:
  事前估算时间:程序运行前,通过分析某个算法的时间复杂度来判断算法解决问题是否合适。
  事后统计时间:程序运行后,通过计算程序运行时间来判断(容易被计算机硬件、软件等影响)。
注:
  一般分析算法都是采用 事前估算时间,即估算分析 算法的 时间复杂度。

扫描二维码关注公众号,回复: 11927507 查看本文章

(2)时间频度、时间复杂度:
时间频度( T(n) ):
  一个算法中 语句执行的次数 称为 语句频度 或者 时间频度,记为 T(n)。
  通常 一个算法花费的时间 与 算法中 语句的执行次数 成正比,即 某算法语句执行次数多,其花费时间就长。

时间复杂度( O(f(n)) ):
  存在某个辅助函数 f(n),当 n 接近无穷大时,若 T(n) / f(n) 的极限值为不等于零的常数,则称 f(n) 为 T(n) 的同数量级函数,记为 T(n) = O(f(n)),称 O(f(n)) 为算法的渐进时间复杂度,简称 时间复杂度。

(3)通过 时间频度( T(n) )推算 时间复杂度 ( O(f(n)) ):
  对于一个 T(n) 表达式,比如: T(n) = an^2 + bn + c,其推算为 O(n) 需要遵循以下规则:
rule1:使用常数 1 替代表达式中的常数,若表达式存在高阶项,则忽略常数项。
  即:若 T(n) = 8,则其时间复杂度为 O(1)。若 T(n) = n^2 + 8,则其时间复杂度为 O(n^2)。
rule2:只保留最高阶项,忽略所有低次项。
  即:T(n) = 3n^2 + n^4 + 3n,其时间复杂度为 O(n^4)。
rule3:去除最高阶项的系数。
  即:T(n) = 3n^2 + 4n^3,其时间复杂度为 O(n^3)。
注:
  T(n) 表达式不同,但是其对应的时间复杂度可能相同。
比如:T(n) = n^2 + n 与 T(n) = 3n^2 + 1 的时间复杂度均为 O(n^2)。

(4)常见时间复杂度

【常见时间复杂度(由小到大排序如下):】
    O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(n^k) < O(2^n)
注:
    时间复杂度越大,算法执行效率越低。    

【常数阶 O(1) :】
    算法复杂度 与 问题规模无关。
比如:
    int a = 1;
    int b = 2;
    int c = a + b;    
分析:
    代码中不存在循环、递归等结构,其时间复杂度即为 O(1)。    

【对数阶 O(logn) :】
    算法复杂度 与 问题规模成对数关系。
比如:   
    int i = 1; 
    while(i < n) {
        i*=2; // 不断乘 2
    }
分析:
    上述代码中存在循环,设循环执行次数为 x,则循环退出条件为 2^x >= n。
    从而推算出 x = logn,此时 log 以 2 为底,即时间复杂度为 O(logn)。

【线性阶 O(n) :】
    算法复杂度 与 问题规模成线性关系。
比如:
    for(int i = 0; i < n; i++) {
        System.out.println(i);
    }    
分析:
    代码中存在循环,且循环次数为 n,即时间频度为 T(n),从而时间复杂度为 O(n)。
        
【线性对数阶 O(nlogn) :】
    算法复杂度 与 问题规模成线性对数关系(循环嵌套)。
比如:
    for(int j = 0; j < n; j++) {
        int i = 1; 
        while(i < n) {
            i*=2; // 不断乘 2
        }
    }    
分析:
    代码中循环嵌套,完成 for 循环需要执行 n 次,每次均执行 while 循环 logn 次,
    即总时间频度为 T(nlogn), 从而时间复杂度为 O(nlogn)。

【平方阶 O(n^2) :】
    算法复杂度 与 问题规模成平方关系(循环嵌套)。
比如:
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            System.out.println(i + j);
        }
    }
分析:
    代码中循环嵌套,总时间频度为 T(n*n),即时间复杂度为 O(n^2)         
    
【立方阶 O(n^3) 、k 次方阶 O(n^k) :】
    类似于平方阶 O(n^2),只是循环嵌套的层数更多了。
    O(n^3) 表示三层循环。O(n^K) 表示四层循环。
    
【指数阶 O(2^n) :】
    算法复杂度 与 问题规模成指数关系(循环嵌套)。
    这个算法的执行效率非常糟糕,一般都不考虑。  
比如:
    int n = 3;
    for (int i = 0; i < Math.pow(2, n); i++) {
        System.out.println(i);
    }
分析:
    上面循环,总时间频度为 T(2^n),即时间复杂度为 O(2^n)。

(5)空间复杂度
  空间复杂度 指的是算法所需耗费的存储空间。与时间复杂度类似,但其关注的是算法执行所需占用的临时空间(非语句执行次数)。
  一般算法分析更看重 时间复杂度,即保证程序执行速度快,比如:缓存 就是空间换时间。

猜你喜欢

转载自blog.csdn.net/zx309519477/article/details/108874439
今日推荐