Files
flypythoncom.github.io/article/python-leetcode-0000/index.html
2020-02-24 17:52:21 +08:00

202 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<head><meta name="generator" content="Hexo 3.9.0">
<!-- Title -->
<meta charset="utf-8">
<meta name="applicable-device" content="pc,mobile">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, viewport-fit=cover">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="author" content="flypython">
<meta name="designer" content="flypython">
<meta name="keywords" content="0000.为什么需要复杂度分析,FlyPython - 专业的Python学习社区,flypython, 飞蟒飞蟒PythonPython入门Python自动化Python日报">
<meta property="og:title" content="0000.为什么需要复杂度分析 | FlyPython - 专业的Python学习社区">
<meta property="og:site_name" content="http://www.flypython.com">
<meta property="og:type" content="article">
<meta property="og:url" content="http://www.flypython.com/article/python-leetcode-0000/">
<meta property="og:image" content="http://www.flypython.com/images/leetcode-0000.png">
<meta property="og:description" content="0000.为什么需要复杂度分析--LeetCode刷题计划第0篇">
<meta name="description" content="0000.为什么需要复杂度分析--LeetCode刷题计划第0篇">
<meta name="rating" content="general">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="format-detection" content="telephone=yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="robots" content="index, follow">
<link rel="icon" href="/images/favicon.ico">
<title>0000.为什么需要复杂度分析 | FlyPython - 专业的Python学习社区</title>
<link rel="stylesheet" href="/css/f25.css">
<link rel="stylesheet" href="/css/highlight.css">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-147288599-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-147288599-1');
</script>
</head>
</head>
<body>
<header class="wrapper header-wrapper">
<div class="container header-nav-wrapper">
<div class="logo"><a href="/" title="FlyPython - 专业的Python学习社区"><h1 class="title">FlyPython</h1></a></div>
<nav class="nav-wrapper">
<a href="https://flypython.com/python" title="飞蟒微课堂">飞蟒微课堂</a>
<a href="https://flypython.com/flypython_daily" title="Python日报">Python日报</a>
<a href="https://flypython.com/PyCon/" title="PyCon">PyCon</a>
<a href="https://github.com/flypythoncom" title="Github">Github</a>
<a href="/article/about" title="关于">关于</a>
</nav>
<span class="btn-menu" id="J_header_menu">
<div class="inner">
<span class="line line-01"></span>
<span class="line line-02"></span>
<span class="line line-03"></span>
</div>
</span>
<div class="wrapper mb-nav-wrapper" id="J_header_menu_list">
<nav class="wrapper mb-nav-container">
<a href="https://flypython.com/python" title="飞蟒微课堂">飞蟒微课堂</a>
<a href="https://flypython.com/flypython_daily" title="Python日报">Python日报</a>
<a href="https://flypython.com/PyCon/" title="PyCon">PyCon</a>
<a href="https://github.com/flypythoncom" title="Github">Github</a>
<a href="/article/about" title="关于">关于</a>
</nav>
</div>
</div>
</header>
<section class="body-wrapper">
<section class="wrapper post-banner">
<div class="container post-banner-container">
<h2 class="wrapper title">0000.为什么需要复杂度分析</h2>
<div class="wrapper tips">
<span>Author</span><span>flypython</span> | <span>Date: </span><span>2020-02-24</span> | <span>Category</span><span><a href="/fly/飞蟒微课堂/" title="飞蟒微课堂">飞蟒微课堂</a><a href="/fly/飞蟒微课堂/LeetCode/" title="LeetCode">LeetCode</a></span>
</div>
</div>
</section>
<section class="wrapper main-wrapper">
<article class="sub-container post-content">
<h4 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h4><p>FlyPython推出的《Python面试专项课程》原计划在春节假期后开始更新考虑到后面刷题的需求可能增多我们对题目进行了调整并于今天开始更新。</p>
<p>原先的<a href="https://mp.weixin.qq.com/s/8EtaSfjyWWJikumG3_l_YQ" target="_blank" rel="noopener">LeetCode刷题计划</a>中的精选TOP面试题推后我们先过一遍《程序员面试金典》这本书上的面试题为基础薄弱的同学夯实基础。</p>
<ul>
<li>网址:<a href="https://leetcode-cn.com/problemset/lcci/" target="_blank" rel="noopener">https://leetcode-cn.com/problemset/lcci/</a></li>
<li>总题数: 97</li>
</ul>
<p>欢迎关注我们的公众号flypython和网站flypython.com我们将持续更新这个专项课程。</p>
<h2 id="为什么需要复杂度分析"><a href="#为什么需要复杂度分析" class="headerlink" title="为什么需要复杂度分析"></a>为什么需要复杂度分析</h2><p>今天是这个专项课程的第0课我们带来算法学习最重要的一个部分复杂度分析。</p>
<p>为什么说复杂度分析是最重要呢?因为数据结构和算法其实解决的是”快”和”省”的问题。”快”就是如何让代码运行得更快,”省”就是让代码节约存储空间。怎么样来衡量你编写的算法代码执行效率呢?这里引出来今天要讲的内容:时间、空间复杂度分析。</p>
<p>你可能会说不就是执行效率么我把代码跑一遍增加cProfile的一些操作不就得到了执行时间和内存占用么。</p>
<p>其实你上面说的也是一种方法叫事后统计法在做性能优化时会大量采用。但是事后统计法有它的局限性。第一它依赖测试环境测试环境不一样得到的数据会都不一样第二它受数据规模的影响大比如排序排10个数和排100万个数肯定不一样。所以我们不能以具体测试数据来表示需要的是一个估算的执行效率的方法。这个方法就是我们要讲的大O复杂度表示法。</p>
<h2 id="大O复杂度表示法"><a href="#大O复杂度表示法" class="headerlink" title="大O复杂度表示法"></a>大O复杂度表示法</h2><p>我们从一个例子开始下面是求1,2,3,…n的累加和的代码</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">1 int cal(int n) &#123;</span><br><span class="line">2 int sum = 0;</span><br><span class="line">3 int i = 1;</span><br><span class="line">4 for (; i &lt;= n; ++i) &#123;</span><br><span class="line">5 sum = sum + i;</span><br><span class="line">6 &#125;</span><br><span class="line">7 return sum;</span><br><span class="line">8 &#125;</span><br></pre></td></tr></table></figure>
<p>我们假设每一行代码的执行时间是相同的为unit_time那么这一段代码第23行执行了一次45行执行了n遍第7行执行了一次那总共执行的次数2n+3,执行时间为(2n+3)* unit_time。</p>
<p>可以看出,所有代码的执行时间 T(n) 与每行代码的执行次数成正比。可以用以下公式表示:</p>
<p><img src="https://tva1.sinaimg.cn/large/0082zybply1gc7k3lrcljj30vh031dfr.jpg" alt></p>
<p>其中T(n)为所有代码的执行时间<br>其中f(n)表示每行代码执行的次数总和</p>
<p>公式中的O表示代码的执行时间 T(n) 与 f(n) 表达式成正比。我们的例子中T(n)=O(2n+3)这就是大O时间复杂度表示法。</p>
<p>大 O 时间复杂度实际上并不具体表示代码真正的执行时间而是表示代码执行时间随数据规模增长的变化趋势所以也叫作渐进时间复杂度asymptotic time complexity简称时间复杂度。</p>
<h4 id="时间复杂度分析"><a href="#时间复杂度分析" class="headerlink" title="时间复杂度分析"></a>时间复杂度分析</h4><ul>
<li>O(1): 常数</li>
<li>O(logn): 对数</li>
<li>O(n): 线性</li>
<li>O(nlogn):线性对数</li>
<li>O(n^2): 平⽅</li>
<li>O(n^3): 立⽅ </li>
<li>O(n^k): K次方</li>
<li>O(2^n): 指数 </li>
<li>O(n!): 阶乘 </li>
</ul>
<h5 id="O-1"><a href="#O-1" class="headerlink" title="O(1)"></a>O(1)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">int a=0;</span><br><span class="line">int b=a;</span><br></pre></td></tr></table></figure>
<p>O(1) 只是常量级时间复杂度的一种表示方法,并不是指只执行了一行代码。只要代码的执行时间不随 n 的增大而增长,这样代码的时间复杂度我们都记作 O(1)。</p>
<h5 id="O-log-n"><a href="#O-log-n" class="headerlink" title="O(log n)"></a>O(log n)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;n; i=i*2)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对数阶时间复杂度非常常见,同时也是最难分析的一种时间复杂度。从代码中可以看出,变量 i 的值从 1 开始取,每循环一次就乘以 2。当大于 n 时,循环结束。还记得我们高中学过的等比数列吗?实际上,变量 i 的取值就是一个等比数列</p>
<p><img src="https://tva1.sinaimg.cn/large/0082zybply1gc7k3xx5grj30vq043aa2.jpg" alt></p>
<p>通过 2^x = n 求解 x, x=log2n这段代码的时间复杂度就是 O(log2n)忽略系数表示为O(logn)。</p>
<h5 id="O-n"><a href="#O-n" class="headerlink" title="O(n)"></a>O(n)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=n; i++)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>for循环代码执行了n遍因此它消耗的时间是随着n的变化而变化的所以通过O(n)来表示。如果平行存在一个for循环m遍的如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=n; i++)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">for(int i=1; i&lt;=m; i++)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>那时间复杂度为O(m+n)。</p>
<h5 id="O-nlogn"><a href="#O-nlogn" class="headerlink" title="O(nlogn)"></a>O(nlogn)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=n; i++)&#123;</span><br><span class="line"> for(int j=1; j&lt;=n;j=j*2) &#123;</span><br><span class="line"> printf(i,j);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>线性对数O(nlogn)就是将时间复杂度为O(logn)的代码循环N遍的话那么它的时间复杂度就是n* O(logn)也就是了O(nlogn)</p>
<h5 id="O-n-2"><a href="#O-n-2" class="headerlink" title="O(n^2)"></a>O(n^2)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=n; i++)&#123;</span><br><span class="line"> for(int j=1; j&lt;=n;j++) &#123;</span><br><span class="line"> printf(i,j);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>平方就是O(n)代码再嵌套循环一遍2层n循环O(n * n) 为O(n^2)。如果嵌套3层n循环则为O(n^3)如果嵌套k层则为O(n^k)。</p>
<p>如果其中一层循环中的n改为m如下所示:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=m; i++)&#123;</span><br><span class="line"> for(int j=1; j&lt;=n;j++) &#123;</span><br><span class="line"> printf(i,j);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>那它的时间复杂度为O(m * n)</p>
<h5 id="O-2-n-和O-n"><a href="#O-2-n-和O-n" class="headerlink" title="O(2^n)和O(n!)"></a>O(2^n)和O(n!)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=math.pow(2,n); i++)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(int i=1; i&lt;=factorial(n); i++)&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>以上两个代码例子分别为O(2^n)复杂度和O(n!)复杂度,指数复杂度和阶乘复杂度属于非多项式量级,我们把时间复杂度为非多项式量级的算法问题叫作 NPNon-Deterministic Polynomial非确定多项式问题。</p>
<p>当数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法。</p>
<p>具体增长曲线如下图所示:</p>
<p><img src="https://tva1.sinaimg.cn/large/0082zybply1gc7k46ndw2j319m0u00yx.jpg" alt></p>
<h4 id="空间复杂度分析"><a href="#空间复杂度分析" class="headerlink" title="空间复杂度分析"></a>空间复杂度分析</h4><ul>
<li>O(1)</li>
<li>O(n)</li>
<li>O(n²)</li>
</ul>
<h5 id="O-1-1"><a href="#O-1-1" class="headerlink" title="O(1)"></a>O(1)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">int i=0;</span><br><span class="line">int j=1;</span><br><span class="line">int m = i + j;</span><br></pre></td></tr></table></figure>
<p>代码中的i,j,m不随着处理数据量变化而变化算法需要的临时空间是固定的可以表示为空间复杂度O(1)</p>
<h5 id="O-n-1"><a href="#O-n-1" class="headerlink" title="O(n)"></a>O(n)</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">int[] m = new int[n];</span><br><span class="line">for(i=1; i&lt;=n; ++i)</span><br><span class="line">&#123;</span><br><span class="line"> printf(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这段代码中第一行new了一个数组出来这个数据占用的大小为n这段代码的2-5行虽然有循环但没有再分配新的空间所以这段代码的空间复杂度为O(n)</p>
<h5 id="O-n-2-1"><a href="#O-n-2-1" class="headerlink" title="O(n^2)"></a>O(n^2)</h5><p>平方空间复杂度与O(n)类似new出来的数组如果是n * n的多维数组那就是O(n^2)复杂度如果是m * n的多维数组,那就是O(m * n)复杂度。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">int[][] a = new int[n][n];</span><br><span class="line">int[][] a = new int[m][n];</span><br></pre></td></tr></table></figure>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>今天我们学习了复杂度分析的相关基础知识,包括了时间复杂度和空间复杂度,主要是用来分析算法执行效率与数据规模之间的增长关系。</p>
<p>常见的复杂度从低阶到高阶有O(1)、O(logn)、O(n)、O(nlogn)、O(n^2),几乎包括了后面学习的所有数据结构和算法的复杂度,希望大家可以好好的掌握。</p>
<p><img src="https://tva1.sinaimg.cn/large/0082zybply1gc7l65ikduj30vq0hs3zg.jpg" alt></p>
<p>时间复杂度分析还有最好,最坏,平均,均摊时间复杂度等话题需要好好讨论,请关注后续文章,我们会在刷题过程中穿插讲解。</p>
<h4 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h4><ul>
<li>analysis_of_algorithms: <a href="https://en.wikipedia.org/wiki/Master_theorem_(analysis_of_algorithms)" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Master_theorem_(analysis_of_algorithms)</a></li>
</ul>
</article>
<div class="sub-container gitalk-wrapper" id="gitalk-container"></div>
</section>
<div class="tips-top-wrapper">
<span class="tip-top-container" onclick="scrollToWindowTop()">
<span class="l-bar"></span>
<span class="r-bar"></span>
</span>
</div>
<footer class="wrapper footer-wrapper">
<div class="container"><span class="copyright">&copy; 2020 FlyPython . All Rights Reserved.</span></div>
</footer>
</section>
<script src="/js/f25.js"></script>
</body>
</html>