<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>Bliner</title><link>https://www.bliner.me</link><atom:link href="https://www.bliner.me/rss.xml" rel="self" type="application/rss+xml"/><description>Bliner</description><generator>Halo v2.24.2</generator><language>zh-cn</language><lastBuildDate>Fri, 12 Jun 2026 07:54:56 GMT</lastBuildDate><item><title><![CDATA[Halo 离线安装升级指南]]></title><link>https://www.bliner.me/archives/halo-chi-xian-an-zhuang-sheng-ji-zhi-nan</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=Halo%20%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97&amp;url=/archives/halo-chi-xian-an-zhuang-sheng-ji-zhi-nan" width="1" height="1" alt="" style="opacity:0;">
<h1 style="" id="%E6%83%85%E5%86%B5">情况</h1>
<p style="">根据<a href="https://docs.halo.run/getting-started/install/offline">官方教程</a>，使用离线安装脚本进行安装。</p>
<p style="">安装后实际上在 <code>/opt/halo</code> 下运行的一个 Docker 的实例，实际是从 Nginx 反向代理出去的。</p>
<p style="">如果你和我的情况，那么就可以往后看（我使用的是 linux 系统）</p>
<p style=""></p>
<h1 style="" id="%E6%8B%89%E5%8F%96%E6%9C%80%E6%96%B0%E7%9A%84-halo-%E5%BA%94%E7%94%A8">拉取最新的 halo 应用</h1>
<pre><code class="language-shell">docker pull registry.fit2cloud.com/halo/halo-pro:2.24.2</code></pre>
<p style="">这里的版本号是从下面的网址获取的，包括仓库地址</p>
<p style=""><a href="https://releases.halo.run/releases/">https://releases.halo.run/releases/</a></p>
<figure style="align-items: start; display: flex; flex-direction: column" data-content-type="image">
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2Fimage.png&amp;size=m" width="1278px">
</figure>
<p style="text-align: center">如图所示，就是最新的镜像地址</p>
<p style="text-align: center"></p>
<figure style="align-items: center; display: flex; flex-direction: column" data-content-type="image">
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2Fimage-ceax.png&amp;size=m" width="1196px">
</figure>
<p style="text-align: center">拉取成功后如上图所示</p>
<h1 style="" id="%E4%BF%AE%E6%94%B9%E6%9C%AC%E5%9C%B0docker-compose.yaml">修改本地docker-compose.yaml</h1>
<p style="">进入/opt/halo 目录，如果你是别的目录就进入你自定义的目录</p>
<p style="">大概率会看到一个Data 目录，一个install.log 还有一个docker-compose.yaml</p>
<p style="">我们主要改这个docker-compose.yaml</p>
<p style="">首先把我们docker-compose.yaml的第一个 image 改成上面那个 Docker 地址，即你 pull 的地址</p>
<p style=""></p>
<pre><code class="language-yaml">services:
  halo:
    image: registry.fit2cloud.com/halo/halo-pro:${HALO_VERSION}</code></pre>
<p style="">改成下面的，注意跟上面拉取的仓库路径是否相同</p>
<pre><code class="language-shell">services:
  halo:
    image: registry.fit2cloud.com/halo/halo-pro:2.24.2</code></pre>
<p style=""></p>
<p style="">上面的 2.24.2 就是版本号</p>
<p style=""></p>
<blockquote>
 <p style="">${HALO_VERSION} 不用管，这个对应的是隐藏文件<code>.env</code> 的版本号，有能力的直接改 env 文件，切记要检查你的registry 仓库是否一样。</p>
</blockquote>
<p style=""></p>
<h1 style="" id="%E5%81%9C%E6%AD%A2%E5%B9%B6%E6%9B%B4%E6%96%B0">停止并更新</h1>
<p style="">停止当前运行的 Halo 实例</p>
<pre><code class="language-docker"> 先 cd 切换到 docker-compose.yaml 所在的目录

 先查看当前运行的实例列表
 docker ps
 然后记住 CONTAINER ID 

 然后停止即可
 docker stop 753cf24e3e12

 然后更新并开启容器
 docker-compose up -d</code></pre>
<p style=""></p>
<p style="">正常会看到下图所示，我是 PG 数据库，所以显示 pg，如果你是 mysql，则显示 mysql，启动后直接刷新博客即可</p>
<figure style="align-items: start; display: flex; flex-direction: column" data-content-type="image">
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2Fimage-bho6.png&amp;size=m" width="1264px">
</figure>
<p style=""></p>
<h1 style="" id="%E6%A3%80%E6%9F%A5">检查</h1>
<figure style="align-items: start; display: flex; flex-direction: column" data-content-type="image">
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2Fimage-hrng.png&amp;size=m" width="1042px">
</figure>
<p style="">进入 console/overview 查看概览，即可看到已经升级到最新的 2.24.2 系统了</p>
<p style=""></p>
<blockquote>
 <p style=""><strong>难点</strong></p>
 <ol>
  <li>
   <p style="">要先拉取官方提供的 Halo 的 docker image</p>
  </li>
  <li>
   <p style="">然后修改 docker-compose.yaml 文件中的 image 与拉取的相同</p>
  </li>
  <li>
   <p style="">进入 halo 目录，先停止，再<code>docker-compose up -d</code>即可</p>
  </li>
 </ol>
</blockquote>
<p style=""></p>]]></description><guid isPermaLink="false">/archives/halo-chi-xian-an-zhuang-sheng-ji-zhi-nan</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 11 Jun 2026 15:11:56 GMT</pubDate></item><item><title><![CDATA[AI ｜ 代码 Review 变高级了？还是难以理解]]></title><link>https://www.bliner.me/archives/ai-dai-ma-review-bian-gao-ji-liao-huan-shi-nan-yi-li-jie</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=AI%20%EF%BD%9C%20%E4%BB%A3%E7%A0%81%20Review%20%E5%8F%98%E9%AB%98%E7%BA%A7%E4%BA%86%EF%BC%9F%E8%BF%98%E6%98%AF%E9%9A%BE%E4%BB%A5%E7%90%86%E8%A7%A3&amp;url=/archives/ai-dai-ma-review-bian-gao-ji-liao-huan-shi-nan-yi-li-jie" width="1" height="1" alt="" style="opacity:0;">
<p style="">最近用 AI 来改造代码和框架，我在 Review 的时候，发现自己<code>屎山</code>变高级了，或者说<code>屎山</code>上多了一块扎眼的金子。</p>
<p style=""></p>
<p style="">本来是非常简单、没有太多复用、只有一两个页面在用的逻辑，现在不光要写Schema 配置、设计字典、抽离业务函数、还要有一个函数做校验、解析字典并吐出最终规则。</p>
<p style=""></p>
<p style="">在 Review 的时候看的云里雾里，<strong>写的真好，</strong>但好像不适合我，把清爽的屎山镀上了金，即不清爽了但却还是屎山。</p>
<p style=""></p>
<blockquote>
 <p style="">我在 Review 的时候就在想，是变高级了？还是更难以理解了。看来在初期让 AI 充分的理解项目风格、包括自己的代码习惯都需要严格的写到 MD 里面去，否则真的会跑偏。</p>
</blockquote>
<p style=""></p>]]></description><guid isPermaLink="false">/archives/ai-dai-ma-review-bian-gao-ji-liao-huan-shi-nan-yi-li-jie</guid><dc:creator>Bliner</dc:creator><enclosure url="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F%25E6%259C%25AA%25E5%2591%25BD%25E5%2590%258D%252014-zbh5.png&amp;size=m" type="image/jpeg" length="349377"/><category>开发&amp;调优</category><pubDate>Thu, 11 Jun 2026 08:17:54 GMT</pubDate></item><item><title><![CDATA[2026年， Halo 新的开始]]></title><link>https://www.bliner.me/archives/2026nian-xin-de-kai-shi</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=2026%E5%B9%B4%EF%BC%8C%20Halo%20%E6%96%B0%E7%9A%84%E5%BC%80%E5%A7%8B&amp;url=/archives/2026nian-xin-de-kai-shi" width="1" height="1" alt="" style="opacity:0;">
<p style="">从上次写博客到现在好久了，Hexo 博客系统好像也不这么流行了，其实主要是没有写作后台，2026年在 AI 的帮助下，从新迁移这套主题到更流行的 Halo 博客系统，希望能够继续博客的写作。</p>
<p style=""></p>
<p style="">不过从今年起，这个博客可能不只有精雕细琢的大作，也有一些碎碎念、吐槽以及个人观点。先记录下来，比什么都重要。18年到26年，转瞬即逝...</p>
<p style=""></p>
<p style="">岁月不饶人，我也没绕过岁月。2026 加油 💪 ！</p>]]></description><guid isPermaLink="false">/archives/2026nian-xin-de-kai-shi</guid><dc:creator>Bliner</dc:creator><enclosure url="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F4A8D3B00-A250-489A-81AC-4AB3C9779C75.jpg&amp;size=m" type="image/jpeg" length="54801"/><category>成长&amp;感悟</category><pubDate>Thu, 11 Jun 2026 02:59:00 GMT</pubDate></item><item><title><![CDATA[数据结构之图-创建邻接矩阵图结构-学习笔记-65]]></title><link>https://www.bliner.me/archives/Data_structure_course_065_graph_create_graph</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E5%9B%BE-%E5%88%9B%E5%BB%BA%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5%E5%9B%BE%E7%BB%93%E6%9E%84-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-65&amp;url=/archives/Data_structure_course_065_graph_create_graph" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>我们知道如何构建一个图的数据结构，那么现在，我们看看如何根据一棵图的图形，创建一张图到内存中去。</p>
<h1 id="%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84">创建数据结构</h1>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">define</span> MAX_VEX_NUM 50  <span class="hljs-comment">//最大的容量暂定为50</span></span>
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> {</span>DG, UDG} GraphType;  <span class="hljs-comment">//选择有向图还是无向图</span>
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>
    <span class="hljs-type">char</span> vexs[MAX_VEX_NUM];  <span class="hljs-comment">//一维数组存储顶点</span>
    <span class="hljs-type">int</span> arcs[MAX_VEX_NUM][MAX_VEX_NUM];   <span class="hljs-comment">//二维数组（邻接矩阵）存储边</span>
    <span class="hljs-type">int</span> vexnum, arcnum;  <span class="hljs-comment">//记录顶点的数量和边的数量，等下用作循环判断条件</span>
    GraphType type;  <span class="hljs-comment">//记录图得类型</span>
} MGraph;
</code></pre>
<p>数据结构中</p>
<ul>
 <li>记录顶点的数量和边的数量，等下用作循环判断条件</li>
 <li>记录图得类型，等下用于写入矩阵</li>
</ul>
<!-- more -->
<h2 id="%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE">获取数据</h2>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">create_MG</span><span class="hljs-params">(MGraph *MG)</span>
{
    
    <span class="hljs-type">int</span> type;  <span class="hljs-comment">//存储图得类型</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入图的类型，有向图（输入0）无向图（输入1） :"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;type);
    <span class="hljs-comment">//如果是0，那就是有向图，如果是1那就是无向图</span>
    <span class="hljs-keyword">if</span> (type == <span class="hljs-number">0</span>)
        MG-&gt;type = DG;
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (type == <span class="hljs-number">1</span>)
        MG-&gt;type = UDG;
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入正确的图类型，有向图（输入0）无向图（输入1）"</span>);
        <span class="hljs-keyword">return</span>;
    }
    
    <span class="hljs-comment">//获取数值</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入顶点个数:"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;MG-&gt;vexnum);  <span class="hljs-comment">//数值赋值给 MG 的 vexnum</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入边个数："</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>, &amp;MG-&gt;arcnum);
    getchar();  <span class="hljs-comment">//接收最后那个回车</span>
    
    <span class="hljs-type">int</span> i;
    <span class="hljs-type">int</span> v1, v2;
    <span class="hljs-comment">//有多少个顶点，就输入多少次</span>
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= MG-&gt;vexnum; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入第%d个顶点的值:"</span>, i);
        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>, &amp;MG-&gt;vexs[i]);
        getchar();
    }
    <span class="hljs-comment">//以上就完成了所有信息的获取</span>
</code></pre>
<p>我们一部分一部分的讲</p>
<ul>
 <li>先输入1或0判断图类型，然后存到结构体中</li>
 <li>然后输入顶点数和边树，方便下面判断循环次数</li>
 <li>接着根据顶点数，接收每个顶点得值</li>
 <li>所有数据获取完毕</li>
</ul>
<h2 id="%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9F%A9%E9%98%B5">初始化矩阵</h2>
<pre><code class="hljs language-c"><span class="hljs-comment">//初始化邻接矩阵，双层循环将二维数组都填充为0</span>
   <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= MG-&gt;vexnum; i++)
   {
       <span class="hljs-keyword">for</span> (j = <span class="hljs-number">1</span>; j &lt;= MG-&gt;vexnum; j++)
       {
           MG-&gt;arcs[i][j] = <span class="hljs-number">0</span>;
       }
   }
</code></pre>
<p>这里我们先把矩阵创建出来</p>
<ul>
 <li>判断条件就是顶点的个数，有多少顶点创建多大的矩阵</li>
 <li>矩阵中默认的值为0</li>
</ul>
<h2 id="%E5%9C%A8%E7%9F%A9%E9%98%B5%E4%B8%AD%E5%AE%9A%E4%BD%8D">在矩阵中定位</h2>
<pre><code class="hljs language-c"><span class="hljs-comment">//定位</span>
<span class="hljs-comment">// 输入矩阵指针和顶点得值，判断在一维数组中这是第几号，并返回</span>
<span class="hljs-type">int</span> <span class="hljs-title function_">getIndexOfVexs</span><span class="hljs-params">(<span class="hljs-type">char</span> vex, MGraph *MG)</span>
{
    <span class="hljs-type">int</span> i;
    <span class="hljs-comment">//有多少结点循环多少次</span>
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= MG-&gt;vexnum; i++)
    {
    <span class="hljs-comment">//如果值等于输入的值，那么就把在一维数组中的位置返回</span>
        <span class="hljs-keyword">if</span> (MG-&gt;vexs[i] == vex)
        {
            <span class="hljs-keyword">return</span> i;
        }
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>这个定位的函数，主要是</p>
<ul>
 <li>将用户输入的值，在一维数组中找到它的位置</li>
 <li>然后将这个位置返回</li>
 <li>我们就能通过这个位置在矩阵中定位到它了</li>
</ul>
<h2 id="%E5%A4%84%E7%90%86%E8%BE%B9%E7%9A%84%E4%BF%A1%E6%81%AF">处理边的信息</h2>
<pre><code class="hljs language-c"><span class="hljs-comment">//输入边的信息，建立邻接矩阵，有多少边执行多少次</span>
   <span class="hljs-type">char</span> c1, c2;
   <span class="hljs-type">int</span> j, k;
   <span class="hljs-keyword">for</span> (k = <span class="hljs-number">1</span>; k &lt;= MG-&gt;arcnum; k++) {
       <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入第%d个边: "</span>, k);
       <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c %c"</span>, &amp;c1, &amp;c2);
       <span class="hljs-comment">//利用上面的定位函数，找到输入的顶点在一维数组中的位置</span>
       v1 = getIndexOfVexs(c1, MG);
       v2 = getIndexOfVexs(c2, MG);
       
       <span class="hljs-keyword">if</span> (MG-&gt;type == <span class="hljs-number">1</span>)  <span class="hljs-comment">//如果是y无向图，则对称矩阵，把对称位置上的值同时赋值为1</span>
           MG-&gt;arcs[v1][v2] = MG-&gt;arcs[v2][v1] = <span class="hljs-number">1</span>;
       <span class="hljs-keyword">else</span>  <span class="hljs-comment">//如果是有向图，则只在指定位置赋值。</span>
           MG-&gt;arcs[v1][v2] = <span class="hljs-number">1</span>;
       getchar();
   }
</code></pre>
<p>有多少个顶点就循环多少次</p>
<ul>
 <li>接收到构成边的两个顶点</li>
 <li>然后利用上面的定位函数，返回在一维数组中的位置</li>
 <li>接着判断图得类型</li>
 <li>无向图，那么矩阵对称，则在对称位置同时赋值 1</li>
 <li>有向图，矩阵不对称，所以只在对应位置赋值 1</li>
</ul>
<h1 id="%E6%80%BB%E4%BB%A3%E7%A0%81">总代码</h1>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> MAX 50</span>

<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span>{</span>DG,UDG} GraphType;

<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>
    <span class="hljs-type">char</span> vex[MAX];  <span class="hljs-comment">//定义一维数组存储顶点</span>
    <span class="hljs-type">int</span> arcs[MAX][MAX];  <span class="hljs-comment">//定义二维数组存储边</span>
    <span class="hljs-type">int</span> vexnum,arcnum;  <span class="hljs-comment">//存储顶点和边的数量</span>
    GraphType type;  <span class="hljs-comment">//存储图的类型</span>
}Graph;

<span class="hljs-type">int</span> <span class="hljs-title function_">position</span><span class="hljs-params">(<span class="hljs-type">char</span> c , Graph *G)</span>{
    <span class="hljs-type">int</span> i;
    <span class="hljs-keyword">for</span>(i=<span class="hljs-number">1</span>;i&lt;=G-&gt;vexnum;i++){
        <span class="hljs-keyword">if</span>(G-&gt;vex[i]==c){
            <span class="hljs-keyword">return</span> i;
        }
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-type">void</span> <span class="hljs-title function_">Create</span><span class="hljs-params">(Graph * G)</span>{
    <span class="hljs-comment">//获取图的类型</span>
    <span class="hljs-type">int</span> type;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入图的类型，有向图输入1，无向图输入0:"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>,&amp;type);
    <span class="hljs-keyword">if</span>(type==<span class="hljs-number">1</span>){
        G-&gt;type=DG;
    }<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(type==<span class="hljs-number">0</span>){
        G-&gt;type=UDG;
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"输入的类型不正确！\n"</span>);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-comment">//获取图得顶点数和边数</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图有几个顶点:"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>,&amp;G-&gt;vexnum);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图有几条边:"</span>);
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%d"</span>,&amp;G-&gt;arcnum);
    getchar();
    <span class="hljs-comment">//获得每个顶点得值</span>
    
    <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> k=<span class="hljs-number">1</span>;k&lt;=G-&gt;vexnum;k++){
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入第%d个结点的值："</span>,k);
        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%s"</span>,&amp;G-&gt;vex[k]);
        getchar();
    }
    <span class="hljs-comment">//值全部获得完毕</span>
    
    <span class="hljs-comment">//矩阵初始化</span>
    <span class="hljs-type">int</span> i,j;
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= G-&gt;vexnum; i++)
    {
        <span class="hljs-keyword">for</span> (j = <span class="hljs-number">1</span>; j &lt;= G-&gt;vexnum; j++)
        {
            G-&gt;arcs[i][j] = <span class="hljs-number">0</span>;
        }
    }
    <span class="hljs-comment">//有多少个结点循环多少次</span>
    <span class="hljs-type">char</span> V1,V2;
    <span class="hljs-type">int</span> V1p,V2p;
    <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> l=<span class="hljs-number">1</span>;l&lt;=G-&gt;arcnum;l++){
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输第%d个边的信息："</span>,l);
        <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c %c"</span>,&amp;V1,&amp;V2);
        V1p=position(V1,G);
        V2p=position(V2,G);
        
        <span class="hljs-keyword">if</span>(G-&gt;type==UDG){
            G-&gt;arcs[V1p][V2p]=G-&gt;arcs[V2p][V1p]=<span class="hljs-number">1</span>;
        }<span class="hljs-keyword">else</span>{
            G-&gt;arcs[V1p][V2p]=<span class="hljs-number">1</span>;
        }
        getchar();
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">print_MG</span><span class="hljs-params">(Graph MG)</span>{
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-------------------------------\n"</span>);
    <span class="hljs-type">int</span> i, j;
    <span class="hljs-keyword">if</span>(MG.type == DG)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图类型 : 有向图\n"</span>);
    }
    <span class="hljs-keyword">else</span>
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图类型：无向图\n"</span>);
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图中的顶点有: %d 个\n"</span>,MG.vexnum);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"图中的边/弧有: %d 个\n"</span>,MG.arcnum);
    
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"顶点的集合:"</span>);
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= MG.vexnum; i++){
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c "</span>, MG.vex[i]);
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"邻接矩阵:\n"</span>);
    
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i &lt;= MG.vexnum; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c "</span>, MG.vex[i]);
        j = <span class="hljs-number">1</span>;
        <span class="hljs-keyword">for</span> (; j &lt; MG.vexnum; j++)
        {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, MG.arcs[i][j]);
        }
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d "</span>, MG.arcs[i][j]);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    }
}
<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>
{
    Graph MG;
    Create(&amp;MG);
    print_MG(MG);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447540206199.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>0</td>
   <td>1</td>
   <td>1</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
 </tbody>
</table>
<p>我们将上面的图输入程序，我们用 a、b、c、d 代替v1、v2、v3、v4</p>
<blockquote>
 <p><strong>运行结果</strong>
  <br>
  请输入图的类型，有向图输入1，无向图输入0:0
  <br>
  图有几个顶点:4
  <br>
  图有几条边:5
  <br>
  请输入第1个结点的值：a
  <br>
  请输入第2个结点的值：b
  <br>
  请输入第3个结点的值：c
  <br>
  请输入第4个结点的值：d
  <br>
  请输第1个边的信息：a b
  <br>
  请输第2个边的信息：b c
  <br>
  请输第3个边的信息：c d
  <br>
  请输第4个边的信息：d a</p>
 <h2 id="%E8%AF%B7%E8%BE%93%E7%AC%AC5%E4%B8%AA%E8%BE%B9%E7%9A%84%E4%BF%A1%E6%81%AF%EF%BC%9Aa-c">请输第5个边的信息：a c</h2>
 <p>图类型：无向图
  <br>
  图中的顶点有: 4 个
  <br>
  图中的边/弧有: 5 个
  <br>
  顶点的集合:a b c d
  <br>
  邻接矩阵:
  <br>
  a 0 1 1 1
  <br>
  b 1 0 1 0
  <br>
  c 1 1 0 1
  <br>
  d 1 0 1 0</p>
</blockquote>
<p>运行结果同矩阵相同，成功！</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_065_graph_create_graph</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Fri, 14 Dec 2018 08:25:39 GMT</pubDate></item><item><title><![CDATA[数据结构之图-图的存储结构-学习笔记-64]]></title><link>https://www.bliner.me/archives/Data_structure_course_064_graph_graph_storage_structure</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E5%9B%BE-%E5%9B%BE%E7%9A%84%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-64&amp;url=/archives/Data_structure_course_064_graph_graph_storage_structure" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>学了那么多图的知识，是时候动手设计一下图的存储结构了，图跟线性表、树有那么大得差别，所以图的数据结构要复杂很多。</p>
<h1 id="%E6%80%9D%E8%80%83">思考</h1>
<p>我们先回忆一下线性表和树得存储结构</p>
<ul>
 <li>**线性表：**一对一的关系，用数组和链表就能很好的表示。</li>
 <li>**树：**一对多得关系，用数组和链表得特性结合在一起就可以很好的表示。</li>
</ul>
<p>那么图呢？</p>
<ul>
 <li>图上的任意一个顶点都可以是第一个顶点，谁开始都行。</li>
 <li>任意一个顶点的邻接点也不存在次序关系，多对多，大家都一样。</li>
</ul>
<p>我们观察下面的四张图
 <br>
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447533743006.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<!-- more -->
<p>这四张图形态各异，但是</p>
<ul>
 <li>各个结点的邻接点都相同</li>
 <li>边也都相同</li>
 <li>所以位置虽然不一样，但其实<code>都是一张图。</code></li>
</ul>
<p>那么该如何将图在内存中表示呢？</p>
<ul>
 <li>既然任意两个顶点都可能存在联系</li>
 <li>我们不能线性的表达数据元素之间得关系</li>
 <li>因为内存的物理位置也是线性的，而图的元素关系是平面的。</li>
 <li>既然线性关系不行，那用多重链表呢？可以！</li>
 <li>但会造成巨大的资源浪费，各个顶点得度数如果相差很大，就会造成很大的浪费。</li>
</ul>
<h1 id="%E8%A7%A3%E5%86%B3">解决</h1>
<p>想明白上面得问题，我们就来回答这个问题</p>
<ul>
 <li>图是由顶点和边两部分组成的，合在一起表示困难，我们分开表示就好了</li>
 <li>**顶点：**因为不分大小、主次，用一个简单的一维数组来存储就好</li>
 <li>**边、弧：**其实就是顶点和顶点之间得关系，一个二维数组来存储就好</li>
 <li>所以，我们使用<code>邻接矩阵方案</code>，撒花！</li>
</ul>
<h1 id="%E6%97%A0%E5%90%91%E5%9B%BE%E7%9A%84%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5">无向图的邻接矩阵</h1>
<p>**图的邻接矩阵（Adjacency Matrix）：**存储方式是用两个数组来表示图。</p>
<ul>
 <li>一个一维数组存储图中得顶点信息</li>
 <li>一个二维数组（邻接矩阵）存储图中边、弧的信息</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447540206199.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们试着来存放上面这个无向图，首先设计一个一维数组 vexs 存放顶点信息</p>
<table>
 <thead>
  <tr>
   <th>vexs0</th>
   <th>vexs1</th>
   <th>vexs2</th>
   <th>vexs3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>V1</td>
   <td>V2</td>
   <td>V3</td>
  </tr>
 </tbody>
</table>
<p>接着设计一个邻接矩阵（二维数组）arcs，存放边和边得信息</p>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>0</td>
   <td>1</td>
   <td>1</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
 </tbody>
</table>
<p>因为是无向图，所以</p>
<ul>
 <li>无论是先行再列看（V0，V1）还是 先列再行看（V1，V0）都可以</li>
 <li>邻接矩阵存储得是边、弧，如果存在就写1，反之则写0</li>
 <li>这个邻接矩阵也是一个对称矩阵，因为这是一个无向图。</li>
</ul>
<h1 id="%E6%97%A0%E5%90%91%E5%9B%BE%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5%E7%9A%84%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">无向图邻接矩阵的存储结构</h1>
<p>我们设计这个存储结构时</p>
<ul>
 <li>设计两个数组</li>
 <li>顶点数组vertex[4]</li>
 <li>边数组 arc[4][4] 为对称矩阵</li>
 <li>边数组中，0表示顶点间不存在边，1表示顶点间存在边</li>
</ul>
<p>所谓对称矩阵，就是</p>
<ul>
 <li>N 阶矩阵的元满足</li>
 <li>a[i][j]=a[j]<a href="https://www.bliner.me/0%3C=i,j%3C=n">i</a></li>
 <li>也就是从矩阵的左上角到右下角为轴</li>
 <li>右上角得元与左下角相对应得元全都是相等的</li>
</ul>
<p>有了这个数据结构，我们就可以很轻松的知道</p>
<ul>
 <li>任意两个顶点之间是否存在边，0就是没边，1就是有边</li>
 <li>某个顶点的度，就是顶点 Vi 在邻接矩阵中第 i 行或 i 列的元素的和。</li>
 <li>还记得无向图顶点的度嘛？就是该顶点邻接的边的个数</li>
 <li>就是这一列或这一行，有多少个 1，加起来就是这个 Vi 的度</li>
 <li>最后一个就是求 Vi 顶点的所有邻接点</li>
 <li>就是将第 i 行元素扫描一遍，只要 arc[ i ][ j ]为1就是邻接点</li>
</ul>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>0</td>
   <td>1</td>
   <td>1</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
 </tbody>
</table>
<p>V1 的度是2，因为无论行、列，为1的元素加起来，都是2
 <br>
 V1 的邻接点是 V0、V2，因为 V1 行只有 V0、V2 的元素为1</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447540206199.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们看看图对不对，没有问题！</p>
<h1 id="%E6%9C%89%E5%90%91%E5%9B%BE%E7%9A%84%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5">有向图的邻接矩阵</h1>
<p>我们在无向图中发现，邻接矩阵竟然是对称的，原因也很简单</p>
<ul>
 <li>因为无论是顶点 A 到顶点 B 还是顶点 B 到顶点 A 都是相同的</li>
 <li>那么在有向图中，给顶点规定了方向之后，会不会好一些呢？</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447577692706.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>有向图最重要的一点就是区分方向，我们按照上面的思路，首先设计一个一维数组 vexs 存放顶点信息</p>
<table>
 <thead>
  <tr>
   <th>vexs0</th>
   <th>vexs1</th>
   <th>vexs2</th>
   <th>vexs3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>V1</td>
   <td>V2</td>
   <td>V3</td>
  </tr>
 </tbody>
</table>
<p>接着设计一个邻接矩阵（二维数组）arcs，存放边和边得信息</p>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>1</td>
   <td>1</td>
   <td>0</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
  </tr>
 </tbody>
</table>
<p>因为我们要注意方向</p>
<ul>
 <li>所以行为弧为、列为弧头</li>
 <li>如果不规定，那么 A-&gt;B 和 B-&gt;A 就一样了</li>
 <li>这就不是有向图了</li>
</ul>
<h1 id="%E6%9C%89%E5%90%91%E5%9B%BE%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5%E7%9A%84%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">有向图邻接矩阵的存储结构</h1>
<p>我们设计这个存储结构时</p>
<ul>
 <li>设计两个数组</li>
 <li>顶点数组vertex[4]</li>
 <li>边数组 arc[4][4] ，有向图矩阵并不对称</li>
 <li>边数组中，0表示顶点间不存在边，1表示顶点间存在边</li>
</ul>
<p>有向图有入度和出度的概念，所以</p>
<ul>
 <li>顶点 V1 的入度为1，正好是第 V1列的各数之和</li>
 <li>顶点 V1 的出度为2，正好是第 V2行的各数之和</li>
 <li>我们看看下表</li>
</ul>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
   <td>1</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>1</td>
   <td>0</td>
   <td>1</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>1</td>
   <td>1</td>
   <td>0</td>
   <td>0</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
  </tr>
 </tbody>
</table>
<p>V1 的入度是1，出度是2</p>
<ul>
 <li>V1列所有元素的和为1</li>
 <li>V1行所有元素的和为2</li>
 <li>我们看下图验证一下</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447577692706.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>没有问题，我们接下来实际设计一下数据结构。</p>
<h1 id="%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5%EF%BC%88%E7%BD%91%EF%BC%89">邻接矩阵（网）</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-14-15447584041093.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们在上一节，说到了网这个概念，实际上就是每条边上带有权的图，就是网。</p>
<ul>
 <li>网跟上面的思路一样，使用两个数组搞定</li>
 <li>在数组中的元素就不在是0和1了</li>
 <li>因为每条边有权，所以我们就将每条边的权值赋值到二维数组中</li>
</ul>
<p>网最重要的一点就是看好权值，我们按照上面的思路，首先设计一个一维数组 vexs 存放顶点信息</p>
<table>
 <thead>
  <tr>
   <th>vexs0</th>
   <th>vexs1</th>
   <th>vexs2</th>
   <th>vexs3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>V1</td>
   <td>V2</td>
   <td>V3</td>
  </tr>
 </tbody>
</table>
<p>接着设计一个邻接矩阵（二维数组）arcs，存放边和边得信息</p>
<table>
 <thead>
  <tr>
   <th></th>
   <th>V0</th>
   <th>V1</th>
   <th>v2</th>
   <th>v3</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>V0</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
   <td>18</td>
  </tr>
  <tr>
   <td>V1</td>
   <td>8</td>
   <td>$\infty$</td>
   <td>2</td>
   <td>$\infty$</td>
  </tr>
  <tr>
   <td>V2</td>
   <td>4</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
  </tr>
  <tr>
   <td>V3</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
   <td>$\infty$</td>
  </tr>
 </tbody>
</table>
<p>这里有三种情况</p>
<ul>
 <li>如果有弧，则将权值写入</li>
 <li>如果指向自己，那就写$\infty$</li>
 <li>如果没有弧，那就写入$\infty$</li>
 <li>$\infty$ 表示计算机允许的，一个大于所有权值的值。</li>
</ul>
<h1 id="%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0">代码实现</h1>
<pre><code class="hljs language-c"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> {</span> DG, UDG } GraphType; <span class="hljs-comment">//声明图的类型，有向还是无向</span>
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>
    <span class="hljs-type">char</span> vexs[MAX_VEX_NUM];  <span class="hljs-comment">//一维数组，用于存放顶点</span>
    <span class="hljs-type">int</span> arcs[MAX_VEX_NUM][MAX_VEX_NUM];  <span class="hljs-comment">//二维邻接矩阵，存放边、弧</span>
    <span class="hljs-type">int</span> vexnum, arcnum;  <span class="hljs-comment">//存放顶点数量和边、弧的数量</span>
    GraphType type;  <span class="hljs-comment">//从有向和无向中选择一个</span>
} MGraph;
</code></pre>
<p>好了，理解了上述概念，我们下节课来讲，如何创建一个图。</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_064_graph_graph_storage_structure</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Fri, 14 Dec 2018 02:04:26 GMT</pubDate></item><item><title><![CDATA[数据结构之图-图结构的简介和引入（下）-学习笔记-63]]></title><link>https://www.bliner.me/archives/Data_structure_course_063_graph_graph_introduction_2</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E5%9B%BE-%E5%9B%BE%E7%BB%93%E6%9E%84%E7%9A%84%E7%AE%80%E4%BB%8B%E5%92%8C%E5%BC%95%E5%85%A5%EF%BC%88%E4%B8%8B%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-63&amp;url=/archives/Data_structure_course_063_graph_graph_introduction_2" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>前面我们基本讲了什么是图结构，以及顶点、边和弧之间不同状态的特殊图结构，我们今天继续，看看这些顶点、边和弧之间的关系。</p>
<h1 id="%E5%85%B3%E7%B3%BB">关系</h1>
<p>我们来讲讲顶点和边的一些关系</p>
<h2 id="%E9%A1%B6%E7%82%B9%E5%92%8C%E8%BE%B9%E7%9A%84%E5%85%B3%E7%B3%BB">顶点和边的关系</h2>
<p>我们说，如果存在一个<code>无向图</code>，G（V，E）</p>
<ul>
 <li>图中的两个顶点构成的边$(V1 , V2)\in E$属于 E 的集合</li>
 <li>那么我们就说 V1 和 V2 互为<code>邻接点（Adjacent）</code></li>
 <li>也就说明 V1 和 V2 <code>相邻接，不是连接！</code>。</li>
 <li>对于边(V1 , V2)来说</li>
 <li>边(V1 , V2)依附（Incident）于 顶点 V1和 V2</li>
 <li>也可以说边(V1 , V2)与顶点 V1 、V2 相关联</li>
</ul>
<h2 id="%E6%97%A0%E5%90%91%E5%9B%BE%E4%B8%AD%E7%9A%84%E5%BA%A6">无向图中的度</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446757823062.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<!-- more -->
<p>顶点 V 的度（Degree）是和 V 相关联的边的数目，记为 TD（V）</p>
<ul>
 <li>V 相关联得边根据上面得定义，就是根顶点 V 相连接的边</li>
 <li>上图中顶点 A、B互为邻接点</li>
 <li>边（A，B）依附于顶点 A 与 B 上</li>
 <li>顶点 A 的度为 A 关联得边得数目</li>
 <li>关联的边有（A，B）、（A，C）（A，D）</li>
 <li>所以顶点 A 得度为 3</li>
 <li>B 的关联边是（B，A）、（B，C）度是 2</li>
</ul>
<h2 id="%E6%9C%89%E5%90%91%E5%9B%BE%E4%B8%AD%E7%9A%84%E5%BA%A6">有向图中的度</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446766779780.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>那么在有向图中的度呢？现在有有向图 G=（V，E）</p>
<ul>
 <li>如果弧$&lt;V1,V2&gt;\in E$，<code>注意是尖括号。</code>属于边的集合E</li>
 <li>则称顶点 V1邻接到顶点 V2</li>
 <li>顶点 V2 邻接自顶点 V1</li>
 <li>V1是弧尾，V2是弧头，所以这里得邻接是有方向的</li>
 <li><code>邻接自</code>和<code>邻接到</code>是不同的，请注意！</li>
</ul>
<p>所以，有向图就跟无向图不同了</p>
<ul>
 <li>以顶点 V 为头的弧的数目称为 V 的入度（InDegree）记为 ID（V）</li>
 <li>以顶点 V 为尾的弧的数目称为 V 的出度（OutDegree）记为 OD（V）</li>
 <li>因此，该顶点 V 的度为$TD(V)=ID(V)+OD(V)$</li>
</ul>
<p>我们看上面的有向图中</p>
<ul>
 <li>顶点 A 为头，指向 A 的入度为2</li>
 <li>顶点 A 为尾，从 A 出去的出度为1</li>
 <li>A 的度为$2+1=3$</li>
</ul>
<h1 id="%E8%B7%AF%E5%BE%84">路径</h1>
<p>在图中，我们从一个顶点到另一个顶点的路线，<code>称为路径（Path）</code></p>
<h2 id="%E6%97%A0%E5%90%91%E5%9B%BE%E7%9A%84%E8%B7%AF%E5%BE%84">无向图的路径</h2>
<p>以下四种是在无向图 G 中顶点 A 到顶点 D 的四种不同的路径
 <br>
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15447050631466.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们看到顶点到顶点之间有这么多种不同的走法</p>
<ul>
 <li>以后我们学习图的主要目的就是简化路径</li>
 <li>找出最优路径</li>
</ul>
<h2 id="%E6%9C%89%E5%90%91%E5%9B%BE%E7%9A%84%E8%B7%AF%E5%BE%84">有向图的路径</h2>
<p>以下是在有向图 G 中从 B 到 D 的路径
 <br>
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15447052521603.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>有向图的路径需要额外注意方向</p>
<ul>
 <li>如果我从 A 到 B 是不行的，因为没有 A 为弧头、B 为弧尾的边。</li>
 <li>必须是按照有向图的指向来从一个顶点到另一个顶点</li>
</ul>
<h2 id="%E8%B7%AF%E5%BE%84%E7%9A%84%E9%95%BF%E5%BA%A6">路径的长度</h2>
<p>路径的长度就是路径上<code>边或弧的数量。</code></p>
<h2 id="%E5%9B%9E%E8%B7%AF%EF%BC%88%E7%8E%AF%EF%BC%89">回路（环）</h2>
<p>如果从第一个顶点到最后一个顶点相同的路径，我们称之为环</p>
<h2 id="%E7%AE%80%E5%8D%95%E7%8E%AF">简单环</h2>
<p>既然一个顶点到最后一个顶点得路径如果相同，我们就称之为环，那么什么是简单环呢？</p>
<ul>
 <li>在第一个顶点到最后一个顶点得环路上，每个顶点只出现一次。</li>
 <li>除了第一个和最后一个顶点外，其余顶点都不重复出现得回路</li>
 <li>满足以上条件，就是简单环了</li>
 <li>简单来说，就是路径上的顶点不能重复，且能构成环状。</li>
</ul>
<table>
 <thead>
  <tr>
   <th>简单环</th>
   <th>不是简单环</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>右侧图不是简单环得原因是</p>
<ul>
 <li>环可以是 BCADCB，C 出现了两次</li>
 <li>所以不是简单环。</li>
</ul>
<h1 id="%E8%BF%9E%E9%80%9A%E5%9B%BE">连通图</h1>
<p>如果在一个<code>无向图中</code></p>
<ul>
 <li>顶点 V1 到顶点 V2 有路径，则称 V1 和 V2 是连通的。</li>
 <li>如果图中得任意两个顶点 Vi 和 Vj 都是连通的</li>
 <li>我们就称这个图 G 为连通图（ConnectedGraph）</li>
</ul>
<table>
 <thead>
  <tr>
   <th>不是连通图</th>
   <th>连通图</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>为什么左边得图不是连通图呢？因为</p>
<ul>
 <li>E F没有一个顶点跟 ABCD 连通</li>
 <li>所以并不是所有顶点都互相连通，不是连通图</li>
</ul>
<blockquote>
 <p><strong>注意：</strong>
  <br>
  完全图是：任意两个顶点必须是连接得，<code>也就是得有边。</code>
  <br>
  连通图是：任意两个顶点只要<code>有路径能连过去就行。</code></p>
</blockquote>
<h2 id="%E8%BF%9E%E9%80%9A%E5%88%86%E9%87%8F">连通分量</h2>
<p>无向图中的极大连通子图称为<code>连通分量。</code></p>
<ul>
 <li>无向图，就是边没有指定方向得图。</li>
 <li>极大，就是必须包含这个子图中所有的顶点</li>
 <li>连通，就是任意两个顶点都互相连通。</li>
 <li>子图，就是顶点和边都属于无向图 G （V，E）。</li>
 <li>**注意:**具有极大顶点数的连通子图包含依附于这些顶点所有的边。</li>
</ul>
<table>
 <thead>
  <tr>
   <th>图 G</th>
   <th>极大连通图-1</th>
   <th>极大连通图-2</th>
   <th>不是极大连通图</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>我们看上面的图</p>
<ul>
 <li>ABCD 一定是图 G 的子图</li>
 <li>ABCD 的任意两个结点相互连接的</li>
 <li>子图 ABCD 顶点数也是最大</li>
 <li>那么我们就说子图 ABCD 是图 G 的极大连通子图</li>
 <li>也是图 G 的连通分量</li>
</ul>
<h1 id="%E5%BC%BA%E8%BF%9E%E9%80%9A%E5%9B%BE">强连通图</h1>
<p>我们说的连通图是无向图，对于有向图来说</p>
<ul>
 <li>如果对于每一对儿 Vi 到 Vj 都存在路径，则称 G 是强连通图。</li>
 <li>相应的，对于有向图中得极大强连通子图称为有向图的<code>强连通分量。</code></li>
</ul>
<table>
 <thead>
  <tr>
   <th>不是强连通图</th>
   <th>强连通图</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>为什么左侧得不是强连通图？</p>
<ul>
 <li>因为任意两个顶点没有连通</li>
</ul>
<p>但是右侧得可以是左侧图到极大强连通子图</p>
<ul>
 <li>任意两个顶点联通</li>
 <li>是左侧图的子图</li>
 <li>所有的顶点都包含，极大。</li>
</ul>
<h1 id="%E8%BF%9E%E9%80%9A%E5%9B%BE%E7%94%9F%E6%88%90%E6%A0%91">连通图生成树</h1>
<p>连通图生成树，顾名思义，通过连通图生成一棵树，<code>连通图的生成树是一个极小的连通子图，</code>为什么呢？因为：</p>
<ul>
 <li>它含有图中全部的 n 个顶点</li>
 <li>但只有 n-1条边，因为 n-1条边就可以生成一棵树了</li>
</ul>
<table>
 <thead>
  <tr>
   <th>普通图 G</th>
   <th>极小连通子图/树</th>
   <th>极小连通子图/树</th>
   <th>不是极小连通子图/树</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>中间两个图，都符合以下条件</p>
<ul>
 <li>它含有图中全部的 n 个顶点</li>
 <li>但只有 n-1条边，因为 n-1条边就可以生成一棵树了</li>
 <li>所以是<code>极小连通子图，也是连通图生成树</code></li>
</ul>
<p>最右边的图，不符合以下条件</p>
<ul>
 <li>虽然符合只有 n-1 条边</li>
 <li>没有包含有图中全部的 n 个顶点</li>
 <li>所以不是极小连通子图</li>
</ul>
<h1 id="%E6%9C%89%E5%90%91%E6%A0%91">有向树</h1>
<p>如果一个<code>有向图</code>，符合以下条件</p>
<ul>
 <li>有一个顶点入度为0，即没有其它顶点指向它</li>
 <li>其它顶点得入度都是1，也就是其他顶点都只有一个顶点指向它</li>
 <li>我们就称这是<code>一棵有向树。</code></li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15447094924083.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>上面两张图不是有向树，左图因为 D 结点的入度为3，右图因为 A 结点入度为2</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15447095594099.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>上图是有向树</p>
<ul>
 <li>F 得入度为0，没有顶点指向 F</li>
 <li>E G的入度都为1</li>
 <li>所以上图是有向树</li>
</ul>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_063_graph_graph_introduction_2</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 13 Dec 2018 12:00:15 GMT</pubDate></item><item><title><![CDATA[数据结构之图-图结构的简介和引入（上）-学习笔记-62]]></title><link>https://www.bliner.me/archives/Data_structure_course_062_graph_graph_introduction_1</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E5%9B%BE-%E5%9B%BE%E7%BB%93%E6%9E%84%E7%9A%84%E7%AE%80%E4%BB%8B%E5%92%8C%E5%BC%95%E5%85%A5%EF%BC%88%E4%B8%8A%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-62&amp;url=/archives/Data_structure_course_062_graph_graph_introduction_1" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>终于，我们迎来了新的数据结构，图结构。之前的线性表，主要是一对一的关系，一个元素和另一个元素的关系。树结构，主要层与层之间一对多的关系，即一个结点可以有 N 个子树，N 个子树也只对应一个双亲结点。那么大家也应该猜到了，图解决的是多对多的数据关系。</p>
<h1 id="%E5%9B%BE%E7%9A%84%E5%AE%9A%E4%B9%89">图的定义</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446748009361.png%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 （顶点1有一个环的图结构）</p>
<blockquote>
 <p>**图（Graph）：**是有顶点的又穷非空集合和顶点之间的边集合组成。</p>
</blockquote>
<ul>
 <li>通常表示为G（V，E）</li>
 <li>G 是一个图</li>
 <li>V 是图 G 中顶点的集合</li>
 <li>E 是图 G 中边的集合。</li>
</ul>
<!-- more -->
<p>对于上面的图</p>
<ul>
 <li>每一个圆圈表示图的一个顶点，顶点放在一起就是 V</li>
 <li>连接顶点的连线称之为图的边，边放在一起就是 E</li>
</ul>
<h2 id="%E6%95%B0%E6%8D%AE%E5%85%83%E7%B4%A0%E7%9A%84%E7%A7%B0%E5%91%BC">数据元素的称呼</h2>
<p>我们回顾一下线性表和树的数据</p>
<ul>
 <li>线性表中我们把数据称之为元素</li>
 <li>树中我们把数据称之为结点</li>
 <li>图中我们把数据称之为顶点（Vertex）</li>
</ul>
<h2 id="%E6%B2%A1%E6%9C%89%E6%95%B0%E6%8D%AE%E5%85%83%E7%B4%A0">没有数据元素</h2>
<p>我们回顾一下线性表和树没有数据元素时</p>
<ul>
 <li>线性表可以没有数据元素，称为空表。</li>
 <li>树中可以没有结点，称为空树。</li>
 <li>图一般不为空，我们一般说<code>顶点集合 V 要有穷非空。</code></li>
</ul>
<h2 id="%E6%95%B0%E6%8D%AE%E5%85%83%E7%B4%A0%E7%9A%84%E5%85%B3%E7%B3%BB">数据元素的关系</h2>
<p>我们回顾一下线性表和树中数据元素之间的关系</p>
<ul>
 <li>线性表中的数据元素，一对一，我们说它们有线性关系。</li>
 <li>树结构中的数据元素，一对多，我们说相邻两层的结点具有层次关系。</li>
 <li>图结构中的数据元素，多对多，任意两个顶点之间都可能有关系，顶点之间的逻辑关系用边来表示，边集可以为空，即数据元素可以跟任何其他数据元素无关。</li>
</ul>
<h1 id="%E5%9B%BE%E7%9A%84%E8%BE%B9">图的边</h1>
<p>图中的各种特性和定义比较多，我们分开讲</p>
<h2 id="%E6%97%A0%E5%90%91%E8%BE%B9">无向边</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446757823062.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 看着上面这张图，我们来说明一下</p>
<ul>
 <li>若顶点 Vi 到 Vj 之间的<code>边没有方向</code></li>
 <li>则称这条边为<code>无向边（Edge）</code></li>
 <li>用无序偶（Vi，Vj） 来表示</li>
 <li>V 是顶点的集合哈，Vi 和 Vj 就是其中的两个顶点。别忘了。</li>
</ul>
<p>知道了上面的概念之后，我们就可以说</p>
<ul>
 <li>上图G1是一个<code>无向图</code>，如果 $G1=\left { V1 , E2\right }$</li>
 <li>顶点的集合：$V1=\left{A,B,C,D\right}$</li>
 <li>边的集合：$E1=\left{(A,B),(B,C),(C,D),(D,A),(A,C)\right}$</li>
</ul>
<h2 id="%E6%9C%89%E5%90%91%E8%BE%B9">有向边</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446766779780.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>看着上面这张图，我们来说明一下</p>
<ul>
 <li>若顶点 Vi 到 Vj 之间的<code>边有方向</code></li>
 <li>则称这条边为<code>有向边，也称为弧（Arc）</code></li>
 <li>用有序偶 &lt; Vi , Vj &gt; 表示，注意这里是尖括号</li>
 <li>Vi 称为弧尾，Vj 称为弧头。</li>
 <li>因为是有向边，所有 Vi 和 Vj 顺序不能错，一定是尾巴指向头。</li>
</ul>
<p>知道了上面的概念之后，我们就可以说</p>
<ul>
 <li>上图G2是一个<code>有向图</code>，如果 $G2=\left { V2 , E2\right }$</li>
 <li>顶点的集合：$ V1= \left{ A,B,C,D \right} $</li>
 <li>边的集合：$E1=\left{(B,A),(B,C),(C,A),(A,D)\right}$</li>
 <li>这里边的集合要注意顺序！！！</li>
</ul>
<h2 id="%E7%AE%80%E5%8D%95%E5%9B%BE">简单图</h2>
<p>什么叫做简单图呢？在一个图结构中</p>
<ul>
 <li>若不存在顶点到其自身的边</li>
 <li>同一条边不重复出现</li>
 <li>满足以上两个条件的图，叫做简单图。</li>
</ul>
<p>以下两个都<code>不是简单图</code></p>
<table>
 <thead>
  <tr>
   <th>同一条边出现两次</th>
   <th>A 不能指向自己</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<blockquote>
 <p>**注意：**我们目前讲的主要都是简单图，因为应用广泛。</p>
</blockquote>
<h1 id="%E7%89%B9%E6%AE%8A%E7%9A%84%E5%9B%BE">特殊的图</h1>
<p>根据图边、弧和顶点的不同情况，又分为以下几种常见图</p>
<h2 id="%E6%97%A0%E5%90%91%E5%AE%8C%E5%85%A8%E5%9B%BE">无向完全图</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446911282424.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果在一个<code>无向图中</code></p>
<ul>
 <li>任意两个顶点之间都存在边</li>
 <li>则称该图为无向完全图</li>
 <li>n 个顶点的无向完全图有 $n\times(n-1)\div2$ 条边</li>
 <li>上面就是一张无向完全图，所有顶点都连着边</li>
 <li>ABCD 4个顶点就是 $4\times(4-1)\div2=6$ 条边</li>
</ul>
<h2 id="%E6%9C%89%E5%90%91%E5%AE%8C%E5%85%A8%E5%9B%BE">有向完全图</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446917095559.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果在一个<code>有向图中</code></p>
<ul>
 <li>任意两个顶点之间都存在方向互为相反的两条弧</li>
 <li>则称该图为有向完全图</li>
 <li>n 个顶点的无向完全图有 $n\times(n-1)$ 条边</li>
 <li>上面就是一张无向完全图，所有顶点都与另一个顶点互为相反弧</li>
 <li>ABCD 4个顶点就是 $4\times(4-1)=12$ \条边</li>
</ul>
<h2 id="%E7%A8%80%E7%96%8F%E5%9B%BE%E5%92%8C%E7%A8%A0%E5%AF%86%E5%9B%BE">稀疏图和稠密图</h2>
<p>这里讲的稀疏和稠密都是相对而言的，通常</p>
<ul>
 <li>边或者弧数小于 $n\times \log n$时的图为稀疏图</li>
 <li>反之为稠密图</li>
</ul>
<h2 id="%E5%B8%A6%E6%9D%83%E5%9B%BE">带权图</h2>
<p>这里说的权根我们在哈夫曼树中说的权是一样的</p>
<ul>
 <li>图的边或者弧带有与它相关的数字</li>
 <li>我们把这些相关树称作<code>权（weight）</code></li>
 <li>我们称这种<code>带权图为网（NetWork）</code></li>
</ul>
<h2 id="%E5%AD%90%E5%9B%BE">子图</h2>
<p>如果有两个图 G1=（V1，E1）和 G2=（V2，E2）</p>
<ul>
 <li>如果 $V2 \subseteq V1$，G2的顶点都在 G1 的顶点的集合内</li>
 <li>并且！！！</li>
 <li>$E2 \subseteq E1$，G2的边都在 G1 的边的集合内</li>
 <li>记住，上面两个条件如果同时成立！</li>
 <li>我么你就说 G2 是 G1 的子图</li>
 <li>简单就是，我的顶点你也有，我的边你也有，那我就是你的子图</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446924501218.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-13-15446925426071.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>上面两张图中，红框内的都是左边图的子图。
 <br>
 记住有向图还要关注方向是否相同！</p>
<p>基础知识还有不少，我们下节继续！</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_062_graph_graph_introduction_1</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 13 Dec 2018 06:16:39 GMT</pubDate></item><item><title><![CDATA[数据结构之树-哈夫曼编码-学习笔记-61]]></title><link>https://www.bliner.me/archives/Data_sturcture_course_061_tree_Huffman_coding_2</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E5%93%88%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-61&amp;url=/archives/Data_sturcture_course_061_tree_Huffman_coding_2" width="1" height="1" alt="" style="opacity:0;">
<blockquote>
 <p>未完待续…</p>
</blockquote>]]></description><guid isPermaLink="false">/archives/Data_sturcture_course_061_tree_Huffman_coding_2</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 13 Dec 2018 04:16:39 GMT</pubDate></item><item><title><![CDATA[数据结构之树-哈夫曼编码-学习笔记-60]]></title><link>https://www.bliner.me/archives/Data_sturcture_course_060_tree_Huffman_coding_1</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E5%93%88%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-60&amp;url=/archives/Data_sturcture_course_060_tree_Huffman_coding_1" width="1" height="1" alt="" style="opacity:0;">
<blockquote>
 <p>未完待续…</p>
</blockquote>]]></description><guid isPermaLink="false">/archives/Data_sturcture_course_060_tree_Huffman_coding_1</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 13 Dec 2018 02:16:39 GMT</pubDate></item><item><title><![CDATA[数据结构之树-哈夫曼树-学习笔记-59]]></title><link>https://www.bliner.me/archives/Data_sturcture_course_059_tree_Huffman_tree</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-59&amp;url=/archives/Data_sturcture_course_059_tree_Huffman_tree" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>上一节我们发现，树、森林和二叉树之间竟然有这么多有趣的关联，前根和后根遍历结果竟然都可以跟二叉树相同。二叉树竟然这么的独特，今天我们就来看看，如何打造一棵<code>完美二叉树！</code></p>
<h1 id="%E4%BB%8E%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98%E5%87%BA%E5%8F%91">从一个问题出发</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445857246635.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们在使用二叉树的时候，会遇到类似这样的问题</p>
<ul>
 <li>这是一棵二叉树，结点连接线上的数字，是访问这个结点的概率</li>
 <li>我们有5%的几率访问 A、15%访问 B、70%访问 C、10%访问 D</li>
 <li>那么问题来了，既然绝大多数都是访问 C 的</li>
 <li>那访问 C 必须要先判断是否是 A 或者 B，这样太麻烦了</li>
</ul>
<p>要弄清楚这个问题，我先首先要弄清楚刚才哪些<code>加了数字的结点是什么。</code></p>
<!-- more -->
<h1 id="%E5%B8%A6%E6%9D%83%E5%8F%B6%E5%AD%90%E7%BB%93%E7%82%B9">带权叶子结点</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445857246635.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>还是这张图，我们看到这棵二叉树有一点点不同</p>
<ul>
 <li>结点前有一个数值，我们把这种<code>结点间连线的相关书叫做权。</code></li>
 <li>我们管这些叶子结点，叫做带权叶子结点。</li>
 <li>数值越大，权重越大。</li>
</ul>
<h1 id="%E7%BB%93%E7%82%B9%E7%9A%84%E8%B7%AF%E5%BE%84%E9%95%BF%E5%BA%A6">结点的路径长度</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445857246635.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 我们引申出一个新定义，结点的路径长度，我们来看一下</p>
<h2 id="%E7%BB%93%E7%82%B9%E7%9A%84%E8%B7%AF%E5%BE%84%E9%95%BF%E5%BA%A6">结点的路径长度</h2>
<blockquote>
 <p>**定义：**根结点到叶子结点的路径上的连接数。</p>
</blockquote>
<p>上图中，C 结点的路径长度为 3。</p>
<ul>
 <li>根到右子树，为1</li>
 <li>右子树到右子树为，为2</li>
 <li>右子树到右子树的左子树 C ，为3</li>
</ul>
<h2 id="%E6%A0%91%E7%9A%84%E8%B7%AF%E5%BE%84%E9%95%BF%E5%BA%A6">树的路径长度</h2>
<blockquote>
 <p>**定义：**根到每一个结点的路径长度之和。</p>
</blockquote>
<p>上图中，我们来算一下</p>
<ul>
 <li>根到 A 是1</li>
 <li>根到 B 是2</li>
 <li>根到 C 是3</li>
 <li>根到 D 是3</li>
 <li>所以$1+2+3+3=9$</li>
 <li>上面这棵<code>树的路径长度为9。</code></li>
</ul>
<h2 id="%E7%BB%93%E7%82%B9%E5%B8%A6%E6%9D%83%E8%B7%AF%E5%BE%84%E9%95%BF%E5%BA%A6">结点带权路径长度</h2>
<p>结点的路径长度是根到结点，那么带上权呢？</p>
<blockquote>
 <p>**定义：**结点的路径长度与结点权值的乘积。</p>
</blockquote>
<p>我们还是拿 C 结点举例</p>
<ul>
 <li>C 结点的路径长度我们算过了，长度是 3</li>
 <li>那么 C 结点的带权路径长度就是$3 \times 70=210$</li>
 <li>C 结点的带权路径长度为210</li>
</ul>
<h2 id="%E6%A0%91%E7%9A%84%E5%B8%A6%E6%9D%83%E8%B7%AF%E5%BE%84%E9%95%BF%E5%BA%A6">树的带权路径长度</h2>
<blockquote>
 <p>**定义：**根到每一个结点的路径长度乘以权值的和。</p>
</blockquote>
<p>上图中，我们来算一下</p>
<ul>
 <li>根到 A 是1，权值是5，$1 \times 5=5$</li>
 <li>根到 B 是2，权值是15，$2 \times 15=30$</li>
 <li>根到 C 是3，权值是70，$3 \times 70=210$</li>
 <li>根到 D 是3，权值是10，$3 \times 10=30$</li>
 <li>所以$5+30+210+30=275$</li>
 <li>上面这棵<code>树的带权路径长度为275。</code></li>
</ul>
<p>我们将树的带权路径长度称为 WPL（Weighted Path Length）
 <br>
 记住，WPL 就是树中所有叶子结点的带权路径长度之和。</p>
<h1 id="%E6%9C%80%E4%BC%98%E4%BA%8C%E5%8F%89%E6%A0%91">最优二叉树</h1>
<p>我们称 WPL 的值越小，构造出来的二叉树就约优秀，那么该如何构造出最优的二叉树呢？对了，<code>最优二叉树我们也称为哈夫曼树！</code></p>
<h2 id="%E5%88%9D%E5%A7%8B%E5%8C%96">初始化</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15446023950288.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>首先，我们在森林中选择两课根结点权值最小的树，上图所示</p>
<ul>
 <li>我们把 A B C D 看做四棵树组成的森林</li>
 <li>A B C D 就是四个根节点，四个根节点都有一个权值</li>
 <li>我们把最小的两个提取出来，即 C 和 D</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15446025187699.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>接着我们来操作上面的两个最小的结点</p>
<ul>
 <li>把两个结点中权值较小的放到左边，即 C 放到左边，作为左孩子</li>
 <li>权值较大的结点放在右边，作为右子树</li>
 <li>将左右子树的权值相加，得到6，作为左右子树的双亲结点，我们记作N1</li>
</ul>
<h2 id="%E7%BB%A7%E7%BB%AD%E6%B7%BB%E5%8A%A0">继续添加</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15446032514500.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 我们剩下的还有两个结点，A 和 B</p>
<ul>
 <li>上一步我们得到了一个 N1，权值是 C D 的和</li>
 <li>此时我们从 A 和 B 中选择一个较小的结点 B，拿出来</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15446033807921.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 我们开始比较 B 和 N1</p>
<ul>
 <li>如果 B 比 N1 大，那就放右边作为右子树，反之作为左子树</li>
 <li>因为 B 是5，N1是6，所以我们把 B 放在左边</li>
 <li>同样的生成一个 N2，N2的权值是 N1和 B 的和，即5+6=11</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15446034794809.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 最后剩下的结点 A 相同，最后得到这棵哈夫曼树的根权值为18。
 <br>
 哈夫曼树构造完毕。</p>
<h2 id="%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91%E7%9A%84%E4%BC%98%E7%82%B9%EF%BC%9F">哈夫曼树的优点？</h2>
<p>我们费劲儿构建的这棵哈夫曼树有什么厉害的地方呢？</p>
<ul>
 <li>WPL 最小，WPL 还记得吧？就是树的带权路径</li>
 <li>我们不可能构建出比哈夫曼树更好的排列方式</li>
 <li>所以我们将哈夫曼树也成为<code>完美二叉树。</code></li>
</ul>
<h1 id="%E5%9B%9E%E5%88%B0%E9%97%AE%E9%A2%98">回到问题</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445857246635.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 还记得我们开头这个问题吗？既然访问节点的概率就是权值的话，我们用哈夫曼树的构建方式，将 A B C D 重新构建之后，就可以解决啦！</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_sturcture_course_059_tree_Huffman_tree</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Wed, 12 Dec 2018 08:35:54 GMT</pubDate></item><item><title><![CDATA[数据结构之树-树、二叉树、森林的相互转换-学习笔记-58]]></title><link>https://www.bliner.me/archives/Data_structure_course_058_tree_transfer_between_tree_binary_tree_and_forest</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E6%A0%91%E3%80%81%E4%BA%8C%E5%8F%89%E6%A0%91%E3%80%81%E6%A3%AE%E6%9E%97%E7%9A%84%E7%9B%B8%E4%BA%92%E8%BD%AC%E6%8D%A2-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-58&amp;url=/archives/Data_structure_course_058_tree_transfer_between_tree_binary_tree_and_forest" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>我们学习了树、二叉树、以及森林的知识。我们直到遍历二叉树非常的方便，甚至学会了给二叉树增加线索，提高效率。那么问题来了，如果一棵树，不是二叉树，一个结点下有 N 个孩子，那么我们遍历和操作起来，就会很复杂，因为不知道这棵树是深度长还是宽度广，如果都能转换成二叉树来操作就爽了！今天我们就看看普通树和森林，如何转换成二叉树。</p>
<h1 id="%E6%99%AE%E9%80%9A%E6%A0%91%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%BA%8C%E5%8F%89%E6%A0%91">普通树转换为二叉树</h1>
<p>既然二叉树最方便操作，我们如何能够将普通树转换成二叉树呢？</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-11-15444989268811.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>上面是一棵普通树，有三层，我们将这棵树转换成二叉树</p>
<!-- more -->
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-11-15444990152675.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>第一步，操作兄弟结点</p>
<ul>
 <li>兄弟结点就是位于二叉树同一层的所有结点</li>
 <li>同一个双亲的所有兄弟结点连接起来</li>
 <li>如果没有兄弟则不需要链接</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-11-15444999425466.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>第二步，去掉原有的连接线，规则如下</p>
<ul>
 <li>每一个结点，只保留结点和其长子的连接线</li>
 <li>如果没有左孩子只有右孩子的话，那就保留右孩子的，以此类推</li>
 <li>去掉结点与其他孩子的连接线</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-11-15445000806243.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>第三步，变成二叉树</p>
<ul>
 <li>仔细看就是前一张图的结点连接线</li>
 <li>红线表示左子树</li>
 <li>绿线表示右子树</li>
 <li>把二叉树画出来即可</li>
</ul>
<h1 id="%E6%A3%AE%E6%9E%97%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%BA%8C%E5%8F%89%E6%A0%91">森林转换为二叉树</h1>
<p>森林转换为二叉树，其实跟刚才的普通树转换为二叉树差不多</p>
<ul>
 <li>先将森林的每棵普通树转换为二叉树，不会的看上面。</li>
 <li>然后将森林里，每棵二叉树的根看做兄弟，进行连线</li>
 <li>每棵树的根，都是右子树，所以如果森林有三棵树</li>
 <li>那就是第一棵树作为二叉树的根，第二棵树是根的右子树，第三颗树是第二棵树的右子树</li>
</ul>
<p>我们来看一个例子
 <br>
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445780864584.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>这片森林有三棵树，每棵树都不是二叉树，我们将其先都转换成二叉树的形式</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445779874753.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>然后我们把三棵树的根连在一起，每棵树的根，都是右子树</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445781800931.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>然后我们根据普通树转二叉树的规则，再加上每棵树的根都是右子树，将森林转换成二叉树</p>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E8%BD%AC%E6%8D%A2%E5%88%B0%E6%A0%91%E3%80%81%E6%A3%AE%E6%9E%97">二叉树转换到树、森林</h1>
<p>第一步：我们知道如何将二叉树转换成树和森林，我们接着看看，如何反向转换</p>
<ul>
 <li>如果一个结点 A，是结点 X 的左孩子</li>
 <li>那么这个结点 A 的右孩子的右孩子的右孩子…所有的右孩子</li>
 <li>都划线连接到 X，就是结点 A 的双亲</li>
 <li>我们看看下面的图</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445785065074.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>这张图就是根据上面的规则进行连线，我们来看一下</p>
<ul>
 <li>先找哪些节点是双亲的左孩子，B、F、H</li>
 <li>再看看 B、F、H 有没有右子树，B 和 H 有</li>
 <li>接着看看 B 和 H 的右子树还有没有右子树，B的右子树下还有个 D</li>
 <li>B 的右子树 C、B 的右子树的右子树 D 连接到 B 的双亲 A</li>
 <li>H 的右子树 I 连接到 H 的双亲 G</li>
 <li>Done！</li>
</ul>
<p>第二步：去掉所有双亲到右孩子之间的连线</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445788762184.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<blockquote>
 <p>注意，G 到 I 是可以的，因为 H 才是 I 的双亲。</p>
</blockquote>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445789692279.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>最后我们把二叉树转换回了森林。</p>
<blockquote>
 <p>**注意：**判断二叉树是森林还是普通树的方法是看二叉树的根节点有没有右子树，如果有，那根据倒推原理，一定是森林，没有就是普通树。</p>
</blockquote>
<h1 id="%E6%80%BB%E7%BB%93">总结</h1>
<p>普通树转换为二叉树</p>
<ul>
 <li>**加线：**兄弟结点加线</li>
 <li>**去线：**每个结点都只保留与第一个孩子的连线</li>
 <li>**调整：**树本身的连线是左子树，我们划的线是右子树</li>
</ul>
<p>森林转换为二叉树</p>
<ul>
 <li>**二叉：**先把普通树都转换为二叉树</li>
 <li>**连根：**把所有二叉树的根连接起来</li>
 <li>**调整：**第一棵二叉树是根，其它根都是右孩子</li>
</ul>
<p>二叉树转换为树、森林</p>
<ul>
 <li>**判断：**判断根有没有右子树，有就是森林，没有就是普通树</li>
 <li>**连线：**把右孩子们和双亲链接在一起。</li>
 <li>**去线：**把所有结点到右孩子的连线去掉</li>
 <li>**调整：**剩下的就是普通树或者森林</li>
</ul>
<h1 id="%E6%80%9D%E8%80%83">思考</h1>
<p>我们为什么费劲把普通树和森林转换为二叉树呢？
 <br>
 因为普通树和森林遍历不方便。</p>
<h2 id="%E6%99%AE%E9%80%9A%E6%A0%91%E7%9A%84%E9%81%8D%E5%8E%86">普通树的遍历</h2>
<p>如果我们使用<code>先根</code>的方法遍历普通树</p>
<ul>
 <li>先输出根</li>
 <li>然后从左到右输出全部子树</li>
 <li>然后再从左到右输出子树的子树</li>
 <li>依次类推</li>
</ul>
<p>同样的，如果我们使用<code>后根</code>的方法遍历普通树</p>
<ul>
 <li>先输出子树</li>
 <li>然后再从左到右输出子树的双亲</li>
 <li>依次类推</li>
</ul>
<p>我们看看例题
 <br>
 <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-12-15445796506565.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<blockquote>
 <p>**先根遍历结果是：**A B E F C G D H I J
  <br>
  **后根遍历结果是：**A B E F C G D H I J</p>
</blockquote>
<h2 id="%E6%A3%AE%E6%9E%97%E7%9A%84%E9%81%8D%E5%8E%86">森林的遍历</h2>
<p>其实森林的遍历跟树的遍历一样</p>
<ul>
 <li>第一棵结束就下一棵</li>
 <li>然后把所有的便利结果放在一起就行了</li>
</ul>
<h1 id="%E5%8F%91%E7%8E%B0">发现</h1>
<p>前人们惊人的发现了以下事实，对于森林、树的遍历和二叉树的遍历</p>
<ul>
 <li>森林、树的前根遍历 等同于 二叉树的前序遍历</li>
 <li>森林、树的后跟遍历 等同于 二叉树的中序遍历</li>
</ul>
<blockquote>
 <p>好了！有意思的事情来了！下节继续！</p>
</blockquote>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_058_tree_transfer_between_tree_binary_tree_and_forest</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Tue, 11 Dec 2018 03:15:10 GMT</pubDate></item><item><title><![CDATA[Nintendo Switch 升级切换 microSD/TF 内存卡 保留游戏数据及存档]]></title><link>https://www.bliner.me/archives/Nintendo_Switch_switch_microSD_card_and_reserve_the_database</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=Nintendo%20Switch%20%E5%8D%87%E7%BA%A7%E5%88%87%E6%8D%A2%20microSD%2FTF%20%E5%86%85%E5%AD%98%E5%8D%A1%20%E4%BF%9D%E7%95%99%E6%B8%B8%E6%88%8F%E6%95%B0%E6%8D%AE%E5%8F%8A%E5%AD%98%E6%A1%A3&amp;url=/archives/Nintendo_Switch_switch_microSD_card_and_reserve_the_database" width="1" height="1" alt="" style="opacity:0;">
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-IMG_1831.JPG%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="IMG_1831"></p>
<h1 id="60%E7%A7%92%E8%AF%BB%E5%85%A8%E6%96%87">60秒读全文</h1>
<p>这是一篇在 Nintendo Switch 6.2.0系统上，将游戏数据和存档从 128G 内存卡 转移 到 256G 内存卡，转移成功后，下载的游戏数据及存档有被 Switch 系统认到，并可正常使用的。</p>
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>前天下了《任天堂明星大乱斗 特别版》的时候，发现自己去年11月买的 128G microSD 卡竟然满了，以为能用一段时间呢… 今天进入 Manage Software 一看《DOOM》竟然要 30G ，《异度之刃2》、《任天堂明星大乱斗 特别版》、《Diabol III》都要十几个 G ，所以入了 256 G 的新 microSD 卡，老卡就留着之后进军 Vlog 吧。
 <br>
 至于为什么要写这篇文章呢？因为发现网上流传的几种方法我复现都失败了，也算是记录一下，万一下次换 512G 的内存卡呢？废话不多说，老 128G microSD 卡换256G，游戏文件和存档该如何转移呢？我们接着看~</p>
<h1 id="%E5%87%86%E5%A4%87">准备</h1>
<p>我们在开始前，请确认下面的东西已经准备好了</p>
<ul>
 <li><strong>Nintendo Switch 主机一台。</strong></li>
 <li>**老内存卡：**我的就是128G 的老卡，不要动里面的数据。</li>
 <li>**新内存卡：**不用提前格式化，等下会讲。</li>
 <li>**高速读卡器：**如果没有像我 110+G 要等死…</li>
 <li>**PC / Mac：**用于操作文件转移。</li>
</ul>
<!-- more -->
<h1 id="%E6%80%9D%E8%B7%AF">思路</h1>
<p>我们的大概思路如下</p>
<ul>
 <li><code>格式化新内存卡</code>，让 Switch 认到</li>
 <li>老内存卡的<code>数据拷贝到新内存卡</code></li>
 <li>存储老内存卡数据之后的<code>新内存卡 Switch 还能认到。</code></li>
</ul>
<h1 id="%E7%A2%B0%E5%A3%81">碰壁</h1>
<p>我 Google 了一下目前网络上的换内存卡的方法，大致有三种
 <br>
 <strong>（你如果只想解决问题，可以跳过碰壁这一节）</strong></p>
<blockquote>
 <p><strong>第一种不推荐方法</strong>
  <br>
  昨天刚换了
  <br>
  先关机 把旧卡nintendo文件夹拷贝到电脑上
  <br>
  插入新卡开机，进系统后识别到新卡后会看见在卡上的数字版游戏会出现云图标，再关机拔新卡
  <br>
  然后把nintendo文件夹覆盖到新卡上
  <br>
  新卡插回机器，开机后游戏仍然有云图标，点进去会让你下载，不过是瞬间就下好了 因为数据是有的
  <br>
  来源：<a href="https://bbs.nga.cn/read.php?tid=12603922&amp;rand=638">https://bbs.nga.cn/read.php?tid=12603922&amp;rand=638</a></p>
</blockquote>
<p>不推荐原因</p>
<ul>
 <li>这种方法卡还是没有被机器认到，因为显示云图标</li>
 <li>有可能 Switch 会删除你的文件，因为文件校验可能会有问题</li>
</ul>
<blockquote>
 <p><strong>第二种不推荐方法</strong>
  <br>
  1把老卡nintendo文件夹全部拷贝到电脑上；
  <br>
  2 把新卡放进任天堂Switch中，此时，NintendoSwitch会提示你重启，确定重启，然后拔出新卡，ns会关机；
  <br>
  3新卡连上电脑，将之前拷贝的nintendo文件夹复制到新卡根目录 最后百分之几的时候会提示有相同内容，选择全部覆盖；
  <br>
  4将任天堂Switch开机，所有游戏图标都有个云下载提示，不要管它，插入拷贝好的新卡，提示重启，确定—然后就ok了。
  <br>
  PS：这里一定要让NintendoSwitch在开机状态下插入新卡自己重启一次，如果关机状态插入新卡，开机以后进所有游戏都会报错。
  <br>
  来源：<a href="https://zhuanlan.zhihu.com/p/49452671">https://zhuanlan.zhihu.com/p/49452671</a></p>
</blockquote>
<p>不推荐原因</p>
<ul>
 <li>新卡放进去会提示校验失败，让你重新格式化卡片</li>
 <li>但这一种方法提供了一个思路，让新卡在开机状态下，让 Switch 认一次，也就是不弹出校验失败的提示框。</li>
</ul>
<blockquote>
 <p><strong>第三种不推荐方法</strong>
  <br>
  1把老卡nintendo文件夹全部拷贝到电脑上；
  <br>
  2 把新卡放进任天堂Switch中，此时，NintendoSwitch会提示你重启，确定重启，然后拔出新卡，ns会关机；
  <br>
  3新卡连上电脑，将之前拷贝的nintendo文件夹复制到新卡根目录 最后百分之几的时候会提示有相同内容，选择全部覆盖；
  <br>
  4将任天堂Switch开机，所有游戏图标都有个云下载提示，不要管它，插入拷贝好的新卡，提示重启，确定—然后就ok了。
  <br>
  PS：这里一定要让NintendoSwitch在开机状态下插入新卡自己重启一次，如果关机状态插入新卡，开机以后进所有游戏都会报错。
  <br>
  来源：<a href="https://bbs.a9vg.com/thread-5284744-1-1.html">https://bbs.a9vg.com/thread-5284744-1-1.html</a></p>
</blockquote>
<p>不推荐原因</p>
<ul>
 <li>其实不用在 Switch 开机状态下强行拔卡，容易造成老内存卡数据损坏</li>
 <li>其实有其他方法让 Switch 认到新卡</li>
</ul>
<blockquote>
 <p>**当然了：**上面三种方法，我都没能成功，有运气因素，不过如果我下面的方法失效了，你们也可以试试上面的方法。</p>
</blockquote>
<h1 id="%E5%8A%A8%E6%89%8B">动手</h1>
<p>我们开始吧，<code>我标红的地方一定要着重看，顺序、先后很重要！</code></p>
<h2 id="Switch-%E7%AB%AF%E6%93%8D%E4%BD%9C">Switch 端操作</h2>
<p>我们先操作 Switch</p>
<ul>
 <li>长按 Switch 电源键，关闭 Switch</li>
 <li>然后将 Switch 支架打开，向里轻推内存卡</li>
 <li>将老内存卡拿出备用</li>
</ul>
<h2 id="%E5%A4%87%E4%BB%BD%E8%80%81%E5%86%85%E5%AD%98%E5%8D%A1">备份老内存卡</h2>
<p>我们将老内存卡先进行备份，防止数据丢失</p>
<ul>
 <li>将老内存卡插入<code>高速读卡器</code>，否则等到天荒地老…</li>
 <li>然后打开内存卡内的文件，里面只有一个 Nintendo 的文件夹</li>
 <li>右键该文件夹，复制到你的 Mac / PC 的硬盘上</li>
 <li>等待时间缓缓的流逝…岁月静好。</li>
</ul>
<h2 id="%E5%87%86%E5%A4%87%E6%96%B0%E5%8D%A1">准备新卡</h2>
<p>在等待的过程中，我们先来准备新卡</p>
<ul>
 <li>用剪刀剪开新内存卡的包装</li>
 <li>然后<code>在 Switch 关机状态下插入新内存卡</code></li>
 <li>打开 Switch 电源，进入 Switch</li>
</ul>
<h2 id="Switch-%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%96%B0%E5%8D%A1">Switch 格式化新卡</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121022172100-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121022172100-57B4628D2267231D57E0FC1078C0596D"></p>
<p>点击齿轮图标，进入设置。</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121022172900-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121022172900-57B4628D2267231D57E0FC1078C0596D"></p>
<p>左边列表向下划最后，进入 System，在System 界面最下面找到初始化（Formatting Option）</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121022173800-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121022173800-57B4628D2267231D57E0FC1078C0596D"></p>
<p>在初始化（Formatting Option）里，选择格式化 microSD 卡（Format microSD card）</p>
<p><strong>注意</strong></p>
<ol>
 <li>请再次确认是新卡在机器中</li>
 <li>请再次确认老卡在你的桌子上、或读卡器中正在备份</li>
</ol>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121022174100-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121022174100-57B4628D2267231D57E0FC1078C0596D"></p>
<p>点击 Continue，将<code>新卡格式化</code>。</p>
<h2 id="%E8%AE%A9-Switch-%E8%AE%A4%E5%8F%AF%E6%96%B0%E5%86%85%E5%AD%98%E5%8D%A1">让 Switch 认可新内存卡</h2>
<p><code>这一步是至关重要的</code>，因为如果 Switch 不认，数据拷贝到老卡上也没用
 <br>
 我估计我上面三种方法失败的原因就出在这一步。</p>
<ul>
 <li>首先，将 Switch 关机，将备份好数据的老内存卡放在旁边。</li>
 <li>然后将新内存卡拔出，放在旁边</li>
 <li>接着<code>不插任何内存卡开机</code>。</li>
 <li>然后解锁 Switch ，进入主屏幕</li>
 <li>将<code>老内存卡插入 Switch</code>，正常会提示如下</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121023080000-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121023080000-57B4628D2267231D57E0FC1078C0596D"></p>
<ul>
 <li>这里<code>选择later</code>，然后在<code>开机状态下拔出老内存卡</code>，不会有任何提示</li>
 <li>接着<code>将新内存卡也插入</code>，正常会提示如下</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121023080000-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121023080000-57B4628D2267231D57E0FC1078C0596D"></p>
<p>这里同样<code>选择later</code>，然后<code>关闭 Switch</code>，将新内存卡拔出</p>
<h2 id="%E6%81%A2%E5%A4%8D%E6%95%B0%E6%8D%AE">恢复数据</h2>
<p>将执行完上面操作的新内存卡，连接读卡器</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444540977630.jpg&amp;size=m" alt=""></p>
<p>打开刚才在PC / Mac 上备份的Nintendo 文件夹，里面是三个文件</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444541452926.jpg&amp;size=m" alt=""></p>
<p>将 Album 文件夹中的以年份开头的所有文件，复制到新内存卡的 Album 文件夹中</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444541606754.jpg&amp;size=m" alt=""></p>
<p>将 Contents 文件夹中的 private 文件，复制到新内存卡的 Contents 文件夹中</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444541821290.jpg&amp;size=m" alt=""></p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444541949908.jpg&amp;size=m" alt=""></p>
<p>将 Contents 文件夹中的 placehld 和 registered 文件夹中的所有内容都复制到新内存卡对应的文件夹中，切记<code>不要直接复制 placehld 和 registered 这两个文件夹，只操作 placehld 和 registered 文件夹中的文件。</code></p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-15444542147002.jpg&amp;size=m" alt=""></p>
<p>将 save 文件夹中的所有文件，复制到新内存卡的 save 文件夹中</p>
<p><strong>特别注意</strong></p>
<ol>
 <li>只赋值目标<code>文件夹内</code>的文件，不要操作整个文件夹。</li>
 <li>如果提示<code>文件重复，直接覆盖文件即可。</code></li>
</ol>
<h2 id="Switch-%E6%A3%80%E6%9F%A5">Switch 检查</h2>
<p>上述文件全部操作完毕后</p>
<ul>
 <li>将<code>Switch关闭电源</code>，拔出里面的内存卡</li>
 <li>然后在<code>不插内存卡状态开机</code></li>
 <li>将新内存卡插入<code>已经开机的 Switch 主机中</code></li>
 <li>如果有如下提示
  <br>
  <img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121023080000-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121023080000-57B4628D2267231D57E0FC1078C0596D"></li>
</ul>
<p>选择 restart ，重启 Switch，此时</p>
<ul>
 <li>开机不会报 Card 不认的提示</li>
 <li>所有游戏图标的右上角<code>没有云图标</code></li>
 <li>进入设置，剩余容量显示正常</li>
 <li>进入游戏正常，存档显示正常</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-10-2018121022482300-57B4628D2267231D57E0FC1078C0596D.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="2018121022482300-57B4628D2267231D57E0FC1078C0596D"></p>
<p>恭喜你！大功告成！</p>
<h1 id="%E5%95%B0%E5%97%A6">啰嗦</h1>
<p>至于为什么几十字的东西我要说的这么啰嗦，是因为你觉得你会初始化内存卡、你知道该如何备份数据，你很优秀！你可以跳过！如果我不写清楚，不清楚的又要去搜来搜去，会更麻烦！然而他们也很优秀，他们没法不跳过。他们只是想知道这些啰嗦。</p>]]></description><guid isPermaLink="false">/archives/Nintendo_Switch_switch_microSD_card_and_reserve_the_database</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Mon, 10 Dec 2018 13:41:14 GMT</pubDate></item><item><title><![CDATA[数据结构之树-线索二叉树（二）-学习笔记-57]]></title><link>https://www.bliner.me/archives/Data_structure_course_057_tree_threaded_binary_tree_02</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E7%BA%BF%E7%B4%A2%E4%BA%8C%E5%8F%89%E6%A0%91%EF%BC%88%E4%BA%8C%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-57&amp;url=/archives/Data_structure_course_057_tree_threaded_binary_tree_02" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>上一节，我们试着解决了树在存储时空间浪费和没法找到双亲结点的问题，这一节，我们着重看一下如何用代码实现。这篇难度有一点大，请务必沉住气，认真仔细。</p>
<h1 id="%E6%80%9D%E8%B7%AF%E6%8B%86%E8%A7%A3">思路拆解</h1>
<p>我们一步一步的拆解代码来看看。</p>
<h2 id="%E5%88%9B%E5%BB%BA%E6%A0%91%E7%BB%93%E6%9E%84%E4%BD%93">创建树结构体</h2>
<p>我们首先声明我们重新设计的线索二叉树结点</p>
<ul>
 <li>char data 存放结点数据</li>
 <li>左孩子、右孩子这个不用细说了吧，</li>
 <li>Ltag 和 Rtag 主要是判断左孩子和右孩子究竟是指向左右孩子还是前驱后继</li>
 <li>我们定义结构体的时候，使用了 typedef</li>
 <li>后面所有的 BiTNode 你可以理解为 <code>struct BiTNode</code>，代表该结构体</li>
 <li>后面所有的 BiTree 你可以理解为 <code>struct BiTNode *</code>，代表结构体指针</li>
 <li>注意上面有个星号哈！</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> LEN sizeof(struct BiTNode)</span>

<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span>{</span>link,thread} NodeState;
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span>{</span>
    <span class="hljs-type">char</span> data;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Lchild</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Rchild</span>;</span>
    NodeState Ltag;
    NodeState Rtag;
}BiTNode,*BiTree;
</code></pre>
<!-- more -->
<h2 id="%E5%88%9B%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91">创建二叉树</h2>
<p>我们刚才重新设计了结点，现在开始生成树，首先接收数据</p>
<ul>
 <li>**重点：**我们这里的形参，是 <code>BiTree *tempTree</code></li>
 <li><code>BiTree *tempTree</code> 你可以理解为 <code>struct BiTNode **tempTree</code></li>
 <li>就是一个指向二叉树结点的结构体的<code>指针的指针。</code></li>
 <li>然后 tempC 是用来存放输入的结点 data 的</li>
 <li>我们使用 scanf 来接收数据</li>
</ul>
<p>然后我们判断 scanf 中的数据</p>
<ul>
 <li>如果输入的是 # 号，表示结点为空，我们就把结点设置为 NULL</li>
 <li>这里注意，我们是将<code>*tempTree</code>设置为 NULL，不是<code>** tempTree</code></li>
 <li>是1个星号的，1个星号表示的是结点结构体的指针</li>
</ul>
<p>如果scanf 中接受的不是#号</p>
<ul>
 <li>先创建动态存储空间，1个星号表示的是结点结构体的指针</li>
 <li>所以给1个星号表示的是结点结构体的指针创建动态存储空间</li>
 <li><code>(*tempTree)</code>这里为什么加括号呢？学过指针都知道！</li>
 <li>*tempTree 表示的是 tempTree 中地址指向的数据</li>
 <li>既然 tempTree 存储的是<code>指针的指针</code>，所以把 tempTree 加一个星号</li>
 <li>表示 tempTree 中结点的地址，也就是结点结构体的开始地址</li>
 <li>我们先给 data 赋值为 tempC</li>
 <li>然后默认左右 tag 是 Link，也就是链接状态</li>
 <li>后面如果遇到子树为空，我们会修正 tag 的内容</li>
 <li>然后利用递归，创建左子树和右子树</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">Create</span><span class="hljs-params">(BiTree *tempTree)</span>{
    <span class="hljs-type">char</span> tempC;
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>,&amp;tempC);
    <span class="hljs-keyword">if</span>(tempC==<span class="hljs-string">'#'</span>){
        *tempTree=<span class="hljs-literal">NULL</span>;
    }<span class="hljs-keyword">else</span>{
        *tempTree=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
        (*tempTree)-&gt;data=tempC;
        (*tempTree)-&gt;Ltag=link;
        (*tempTree)-&gt;Rtag=link;
        Create(&amp;(*tempTree)-&gt;Lchild);
        Create(&amp;(*tempTree)-&gt;Rchild);
    }
}
</code></pre>
<h2 id="%E7%BB%99%E4%BA%8C%E5%8F%89%E6%A0%91%E6%B7%BB%E5%8A%A0%E7%BA%BF%E7%B4%A2">给二叉树添加线索</h2>
<p>我们生成了一个二叉树，这棵二叉树的每个结点都有 Ltag 和 Rtag，该如何利用呢</p>
<ul>
 <li>我们这一步就是给哪些没有前驱、后继的结点，修改 Ltag 和 Rtag 状态。</li>
 <li>pre 是一个结点结构体指针的全局变量，保存根的前驱</li>
 <li>然后我们传入二叉树，给二叉树添加线索</li>
 <li>利用中序遍历，左中右的方式，递归遍历这棵树。</li>
</ul>
<p>判断根节点左子树的情况</p>
<ul>
 <li>在根节点时，判断根节点左右子树的状态，记住有右必有左</li>
 <li>先判断左子树，如果为空，那么这个结点的 Lchild 改为前序的地址</li>
 <li>Ltag 改为 thread，表示这个 Lchild 是线索</li>
 <li>这里的 Lchild 是 pre，pre 等下会存储每个结点的前驱。</li>
</ul>
<p>判断根节点右子树的情况</p>
<ul>
 <li>怎么判断右子树呢？如果只有左没有右呢？</li>
 <li>所以判断 pre 的右子树，只要当前结点的前一结点的右子树为空</li>
 <li>那么就改变 Rtag 状态为线索</li>
 <li>Rchild 右孩子为当前结点</li>
 <li>也就是当前结点前驱没有右子树，那么前驱的右孩子指向当前结点。</li>
 <li>记得最后把当前的地址个 pre，这样指针才能向后走，pre 才能保存前驱结点</li>
</ul>
<pre><code class="hljs language-c">BiTree pre;

<span class="hljs-type">void</span> <span class="hljs-title function_">Inthread</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        Inthread(t-&gt;Lchild);
        
        <span class="hljs-keyword">if</span>(!t-&gt;Lchild){
            t-&gt;Ltag=thread;
            t-&gt;Lchild=pre;
        }
        
        <span class="hljs-keyword">if</span>(!pre-&gt;Rchild){
            pre-&gt;Rtag=thread;
            pre-&gt;Rchild=t;
        }
        
        pre=t;
        
        Inthread(t-&gt;Rchild);
    }
}
</code></pre>
<h2 id="%E6%A0%B9%E7%BB%93%E7%82%B9%E7%9A%84%E5%89%8D%E9%A9%B1">根结点的前驱</h2>
<p>我们知道，根结点是没有双亲的，那么怎么获得根结点的前驱呢？</p>
<ul>
 <li>创建一个 p ，模拟根的前驱，让根的前驱指向这个 p</li>
 <li>先创建一个 p，星号的道理和创建树的时候相同，就不解释了。</li>
 <li>Rtag 因为指向自己，所以为 thread 线索</li>
 <li>Ltag 因为等下要指向树本身，所以为 link</li>
 <li>然后让 Rchild 指向子集</li>
 <li>判断树是不是空的，如果是空的，那没得玩，左右子树都指向 p 自己</li>
 <li>如果不是空的，那就让左子树指向树根</li>
 <li>pre 就能获取根的前驱了，就是 p</li>
 <li>然后从这里调用给二叉树加线索的函数</li>
 <li>因为给二叉树加线索，需要用到根的前驱。</li>
</ul>
<p>记得收尾</p>
<ul>
 <li>最后，我们让这棵树连成圈</li>
 <li>让 pre，也就是最后一个结点的前驱的右孩子指向 P，也就是根的前驱</li>
 <li>让后 p 的右孩子指向 pre</li>
 <li>这样，就连起来了</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">Inorderthread</span><span class="hljs-params">(BiTree t , BiTree *p)</span>{
    *p=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
    (*p)-&gt;Rtag=thread;
    (*p)-&gt;Ltag=link;
    (*p)-&gt;Rchild=*p;
    <span class="hljs-keyword">if</span>(!t){
        (*p)-&gt;Lchild=*p;
    }<span class="hljs-keyword">else</span>{
        (*p)-&gt;Lchild=t;
        pre=*p;
        Inthread(t);

        pre-&gt;Rtag=thread;
        pre-&gt;Rchild=*p;
        (*p)-&gt;Rchild=pre;
    }
}
</code></pre>
<h2 id="%E4%B8%A4%E7%A7%8D%E9%81%8D%E5%8E%86%E6%96%B9%E5%BC%8F">两种遍历方式</h2>
<p>用递归的时候注意，要在加线索之前使用，否则树都连起来就会出错。</p>
<p>###递归中序遍历，不多说了，需要的话看前面</p>
<pre><code class="hljs language-c">
<span class="hljs-type">void</span> <span class="hljs-title function_">Inthreading</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        Inthreading(t-&gt;Lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,t-&gt;data);
        Inthreading(t-&gt;Rchild);
    }
}
</code></pre>
<h3 id="%E8%BF%AD%E4%BB%A3%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%EF%BC%8C%E5%B0%B1%E6%98%AF%E5%88%A9%E7%94%A8-while-%E5%BE%AA%E7%8E%AF%E9%81%8D%E5%8E%86">迭代中序遍历，就是利用 while 循环遍历</h3>
<p>同样是左中右</p>
<ul>
 <li>如果 p=t-&gt;Lchild,那就证明转了一圈了。</li>
 <li>然后判断最左边的结点，输出</li>
 <li>判断右边结点是否是叶子，也输出</li>
 <li>p 向右孩子移动一位</li>
 <li>直到转完所有的结点</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">Inorderthreading</span><span class="hljs-params">(BiTree t)</span>{
    BiTree p;
    p=t-&gt;Lchild;
    <span class="hljs-keyword">while</span>(p!=t){
        <span class="hljs-keyword">while</span>(p-&gt;Ltag==link){
            p=p-&gt;Lchild;
        }
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        <span class="hljs-keyword">while</span>(p-&gt;Rtag==thread &amp;&amp; p-&gt;Rchild!=t){
            p=p-&gt;Rchild;
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        }
        p=p-&gt;Rchild;
    }
}
</code></pre>
<h2 id="%E4%B8%BB%E8%B0%83%E5%87%BD%E6%95%B0">主调函数</h2>
<p>这个其实没什么好讲的，就是星号和顺序的问题</p>
<ul>
 <li>星号请注意，因为传过去的是指针的地址，所以要加<code>&amp;</code>符号</li>
 <li>顺序请注意，先使用递归输出，再增加线索，最后用迭代循环输出</li>
 <li>原因是增加线索后，树连起来了，递归没法结束。</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{
    BiTree t,p;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入结点：\n"</span>);
    Create(&amp;t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"递归-中序遍历是："</span>);
    Inthreading(t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"迭代-中序遍历是："</span>);
    Inorderthread(t,&amp;p);
    Inorderthreading(p);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h1 id="%E4%B8%BB%E8%A6%81%E9%97%AE%E9%A2%98">主要问题</h1>
<p>这个也绕了我好久，主要问题是</p>
<ul>
 <li>指向指针的指针，出了问题！</li>
 <li>递归增加线索，出了问题！</li>
</ul>
<h1 id="%E6%80%BB%E4%BB%A3%E7%A0%81">总代码</h1>
<pre><code class="hljs language-c">
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> LEN sizeof(struct BiTNode)</span>

<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span>{</span>link,thread} NodeState;
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span>{</span>
    <span class="hljs-type">char</span> data;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Lchild</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Rchild</span>;</span>
    NodeState Ltag;
    NodeState Rtag;
}BiTNode,*BiTree;

BiTree pre;

<span class="hljs-type">void</span> <span class="hljs-title function_">Create</span><span class="hljs-params">(BiTree *tempTree)</span>{
    <span class="hljs-type">char</span> tempC;
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>,&amp;tempC);
    <span class="hljs-keyword">if</span>(tempC==<span class="hljs-string">'#'</span>){
        *tempTree=<span class="hljs-literal">NULL</span>;
    }<span class="hljs-keyword">else</span>{
        *tempTree=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
        (*tempTree)-&gt;data=tempC;
        (*tempTree)-&gt;Ltag=link;
        (*tempTree)-&gt;Rtag=link;
        Create(&amp;(*tempTree)-&gt;Lchild);
        Create(&amp;(*tempTree)-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">Inthread</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        Inthread(t-&gt;Lchild);
        
        <span class="hljs-keyword">if</span>(!t-&gt;Lchild){
            t-&gt;Ltag=thread;
            t-&gt;Lchild=pre;
        }
        
        <span class="hljs-keyword">if</span>(!pre-&gt;Rchild){
            pre-&gt;Rtag=thread;
            pre-&gt;Rchild=t;
        }
        
        pre=t;
        
        Inthread(t-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">Inorderthread</span><span class="hljs-params">(BiTree t , BiTree *p)</span>{
    *p=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
    (*p)-&gt;Rtag=thread;
    (*p)-&gt;Ltag=link;
    (*p)-&gt;Rchild=*p;
    <span class="hljs-keyword">if</span>(!t){
        (*p)-&gt;Lchild=*p;
    }<span class="hljs-keyword">else</span>{
        (*p)-&gt;Lchild=t;
        pre=*p;
        Inthread(t);

        pre-&gt;Rtag=thread;
        pre-&gt;Rchild=*p;
        (*p)-&gt;Rchild=pre;
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">Inthreading</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        Inthreading(t-&gt;Lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,t-&gt;data);
        Inthreading(t-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">Inorderthreading</span><span class="hljs-params">(BiTree t)</span>{
    BiTree p;
    p=t-&gt;Lchild;
    <span class="hljs-keyword">while</span>(p!=t){
        <span class="hljs-keyword">while</span>(p-&gt;Ltag==link){
            p=p-&gt;Lchild;
        }
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        <span class="hljs-keyword">while</span>(p-&gt;Rtag==thread &amp;&amp; p-&gt;Rchild!=t){
            p=p-&gt;Rchild;
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        }
        p=p-&gt;Rchild;
    }
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{
    BiTree t,p;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入结点：\n"</span>);
    Create(&amp;t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"递归-中序遍历是："</span>);
    Inthreading(t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"迭代-中序遍历是："</span>);
    Inorderthread(t,&amp;p);
    Inorderthreading(p);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<blockquote>
 <p><strong>输出结果：</strong>
  <br>
  请输入结点：
  <br>
  ABDH##I##E#J##CF#K##G##
  <br>
  递归-中序遍历是：HDIBEJAFKCG
  <br>
  迭代-中序遍历是：HDIBEJAFKCG</p>
</blockquote>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>遍历的是上面这棵树。</p>
<h1 id="%E5%B8%A6%E6%B3%A8%E9%87%8A%E7%9A%84%E6%80%BB%E4%BB%A3%E7%A0%81">带注释的总代码</h1>
<p>我第二天一早又写了一遍，加了一些注释</p>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> LEN  sizeof(struct BiTNode)</span>

<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">enum</span>{</span>link,thread} Nodestate;  <span class="hljs-comment">//着重看一下</span>
<span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span>{</span>
    <span class="hljs-type">char</span> data;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Lchild</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Rchild</span>;</span>
    Nodestate Ltag;
    Nodestate Rtag;
}BiTNode,*BiTree;

BiTree pre;

<span class="hljs-type">void</span> <span class="hljs-title function_">Create</span><span class="hljs-params">(BiTree *tempt)</span>{
    <span class="hljs-type">char</span> tempc;
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>,&amp;tempc);
    
    <span class="hljs-keyword">if</span>(tempc==<span class="hljs-string">'#'</span>){
        *tempt=<span class="hljs-literal">NULL</span>;  <span class="hljs-comment">//因为 *temp 才是结点</span>
    }<span class="hljs-keyword">else</span>{
        *tempt=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
        <span class="hljs-comment">//先赋值</span>
        (*tempt)-&gt;data=tempc;
        <span class="hljs-comment">//左右孩子的默认状态是链接的</span>
        (*tempt)-&gt;Ltag=link;
        (*tempt)-&gt;Rtag=link;
        <span class="hljs-comment">//接着用递归的方式先输入左子树再输入右子树，这种前序遍历</span>
        Create(&amp;(*tempt)-&gt;Lchild);
        Create(&amp;(*tempt)-&gt;Rchild);
    }
}

<span class="hljs-comment">//给上面的二叉树创建线索</span>
<span class="hljs-type">void</span> <span class="hljs-title function_">Inthread</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        <span class="hljs-comment">//因为是用中序给树增加线索，所以先递归左孩子</span>
        Inthread(t-&gt;Lchild);
        
        <span class="hljs-keyword">if</span>(!t-&gt;Lchild){
            <span class="hljs-comment">//如果中间根结点左孩子为空，状态改变</span>
            t-&gt;Ltag=thread;
            t-&gt;Lchild=pre;  <span class="hljs-comment">//这里的 pre 记录的是 t 的前驱结点</span>
        }
        
        <span class="hljs-keyword">if</span>(!pre-&gt;Rchild){
            <span class="hljs-comment">//因为没法用该结点判断右孩子，所以用前驱判断，如果右孩子为空</span>
            pre-&gt;Rtag=thread;
            pre-&gt;Rchild=t;  <span class="hljs-comment">//如果前驱的右孩子为空，那么前驱的左右都指向当前结点</span>
        }
        
        pre=t;  <span class="hljs-comment">//让前驱向后移动一位</span>
        
        Inthread(t-&gt;Rchild);  <span class="hljs-comment">//完成左中右的遍历过程</span>
    }
}

<span class="hljs-comment">//创建根的 pre</span>

<span class="hljs-type">void</span>  <span class="hljs-title function_">Inorderthread</span><span class="hljs-params">(BiTree t , BiTree *p)</span>{
    *p=(BiTree)<span class="hljs-built_in">malloc</span>(LEN);
    <span class="hljs-comment">//右孩子等于 thread，并且指向自己</span>
    (*p)-&gt;Rchild=*p;
    (*p)-&gt;Rtag=thread;
    <span class="hljs-comment">//左孩子为连通，等下指向树的根</span>
    (*p)-&gt;Ltag=link;
    
    <span class="hljs-keyword">if</span>(!t){
        (*p)-&gt;Lchild=*p;
    }<span class="hljs-keyword">else</span>{
        <span class="hljs-comment">//把 p 的左子树指向 t 树的根</span>
        (*p)-&gt;Lchild=t;
        pre=*p;  <span class="hljs-comment">//pre 就是这个指向根的结点</span>
        Inthread(t);  <span class="hljs-comment">//有了 pre，开始给树增加线索</span>
        
        <span class="hljs-comment">//收尾，此时 pre 是最后一个结点</span>
        pre-&gt;Rtag=thread;
        pre-&gt;Rchild=*p;
        (*p)-&gt;Rchild=pre;
    }
}

<span class="hljs-comment">//开始递归中序遍历</span>
<span class="hljs-type">void</span> <span class="hljs-title function_">Invisit</span><span class="hljs-params">(BiTree t)</span>{
    <span class="hljs-keyword">if</span>(t){
        Invisit(t-&gt;Lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,t-&gt;data);
        Invisit(t-&gt;Rchild);
    }
}

<span class="hljs-comment">//开始迭代中序遍历</span>
<span class="hljs-type">void</span> <span class="hljs-title function_">Inthreading</span><span class="hljs-params">(BiTree t)</span>{
    BiTree p;
    p=t-&gt;Lchild;
    <span class="hljs-comment">//如果转到 t，那么证明指回根节点了，转了一圈了</span>
    <span class="hljs-keyword">while</span>(p!=t){
        <span class="hljs-comment">//如果左边是连通的，那么向下</span>
        <span class="hljs-keyword">while</span>(p-&gt;Ltag==link){
            p=p-&gt;Lchild;
        }
        
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        
        <span class="hljs-keyword">while</span>(p-&gt;Rtag==thread &amp;&amp; p-&gt;Rchild!=t){
            p=p-&gt;Rchild;
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,p-&gt;data);
        }
        
        p=p-&gt;Rchild;
    }
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{
    BiTree t,p;
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入结点：\n"</span>);
    Create(&amp;t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"递归-中序遍历："</span>);
    Invisit(t);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"迭代-中序遍历："</span>);
    Inorderthread(t,&amp;p);
    Inthreading(p);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}
</code></pre>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_057_tree_threaded_binary_tree_02</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Sat, 8 Dec 2018 04:53:32 GMT</pubDate></item><item><title><![CDATA[数据结构之树-线索二叉树（一）-学习笔记-56]]></title><link>https://www.bliner.me/archives/Data_structure_course_056_tree_threaded_binary_tree_01</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E7%BA%BF%E7%B4%A2%E4%BA%8C%E5%8F%89%E6%A0%91%EF%BC%88%E4%B8%80%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-56&amp;url=/archives/Data_structure_course_056_tree_threaded_binary_tree_01" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>前面，我们说过，二叉树的遍历方法，以及如何使用代码创建二叉树并将三种遍历顺序都输出出来。如果你还对单链表有印象，我们会发现上节我们树的创建过程其实就是根结点指向左右子树，很像单链表指向唯一后继，对不对？还记得为什么我们要使用双向链表嘛？因为双向链表在找寻唯一前驱的时候方便。因此，二叉树也遇到了相同的问题，左右子树不知道自己的双亲是谁，我们该怎么解决呢？</p>
<h1 id="%E9%97%AE%E9%A2%98">问题</h1>
<p>现在的二叉树</p>
<ul>
 <li>一个结点下，数据域和指针域</li>
 <li>数据域，存放数据</li>
 <li>指针域，指向该结点下的左右子树</li>
</ul>
<h2 id="%E9%97%AE%E9%A2%98%E4%B8%80">问题一</h2>
<p>如果我要找这个结点的双亲该怎么办呢？</p>
<ul>
 <li>麻烦了！我们要重新遍历这个棵二叉树</li>
 <li>直到遇到这个结点，然后记录它得双亲</li>
</ul>
<h2 id="%E9%97%AE%E9%A2%98%E4%BA%8C">问题二</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-08-15442407258724.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<!-- more -->
<p>我们看上面这张图，发现</p>
<ul>
 <li>一个结点的指针域，如果没有左右子树！</li>
 <li>例如 EFGHI ，它们的指针域会非常的浪费空间！</li>
 <li>计算下来，一共9个结点，18个指针，10个都被浪费了。</li>
</ul>
<p>好了，现在的问题是</p>
<ul>
 <li>左右子树找到不到双亲结点，孩子找不到爹妈。</li>
 <li>如果没有左右子树，结点空间又被浪费掉！</li>
</ul>
<h1 id="%E6%80%9D%E8%80%83">思考</h1>
<p>那么把剩余的空间利用起来，存放结点的双亲信息不就好了？</p>
<ul>
 <li>那我们就把结点的结构改造一下</li>
 <li>让程序直到，我的左右子树指向的是我的左右子树，还是前驱后继</li>
</ul>
<h2 id="%E6%94%B9%E9%80%A0">改造</h2>
<p>我们将原有的结点结构体，改造如下</p>
<table>
 <thead>
  <tr>
   <th>左孩子</th>
   <th>左孩子状态</th>
   <th>数据</th>
   <th>右孩子状态</th>
   <th>右孩子</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Lchild</td>
   <td>Ltag</td>
   <td>Data</td>
   <td>Rtag</td>
   <td>Rchild</td>
  </tr>
 </tbody>
</table>
<p>我们发现，增加了左右孩子的状态</p>
<ul>
 <li>左孩子<code>状态为0</code>，那么左孩子指向的是<code>左孩子的结点地址。</code></li>
 <li>左孩子<code>状态为1</code>，那么左孩子指向的是<code>该结点的前驱地址。</code></li>
 <li>右孩子<code>状态为0</code>，那么右孩子指向的是<code>左孩子的结点地址。</code></li>
 <li>右孩子<code>状态为1</code>，那么右孩子指向的是<code>该结点的后继地址。</code></li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-08-15442407258724.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>这么说可能有点晕，我们看看这张图，我们看到</p>
<ul>
 <li>现在 H 结点的左右子树地址为 NULL</li>
 <li>那么我们让 H 结点的 Ltag 为 1</li>
 <li>H 结点的<code>左孩子指针域</code>存储的就是<code>H 结点的前驱</code></li>
 <li>那么我们让 H 结点的 Rtag 为 1</li>
 <li>H 结点的<code>右孩子指针域</code>存储的就是<code>H 结点的后继</code></li>
 <li>因为 H 结点的后继是没有的，所以存储为 NULL。</li>
</ul>
<p>这样我们就最大限度上利用了存储空间，提高了搜索结点时的效率。即用空间换时间。下节我们主要讲讲线索二叉树的代码。</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_056_tree_threaded_binary_tree_01</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Sat, 8 Dec 2018 04:02:31 GMT</pubDate></item><item><title><![CDATA[数据结构之树-二叉树的建立及遍历算法（二）-学习笔记-55]]></title><link>https://www.bliner.me/archives/Data_structure_course_055_tree_traversing_binary_tree_02</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%BB%BA%E7%AB%8B%E5%8F%8A%E9%81%8D%E5%8E%86%E7%AE%97%E6%B3%95%EF%BC%88%E4%BA%8C%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-55&amp;url=/archives/Data_structure_course_055_tree_traversing_binary_tree_02" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>上一节，我们详细说明了二叉树的 4 种遍历方式，根据根的位置不同，我们分为前序遍历、中序遍历和后序遍历以及最常用的层序遍历。今天我们就来实战一下， 看看如何建立一棵树，并且按照我们需要的方式遍历树上的结点。</p>
<h1 id="%E9%9C%80%E6%B1%82">需求</h1>
<ul>
 <li>根据输入建立一棵二叉树</li>
 <li>输出其前序遍历、中序遍历和后序遍历结果</li>
</ul>
<h1 id="%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90">代码分析</h1>
<p>首先要构建一棵树，因为是二叉树，所以结构体中包含左右两棵子树</p>
<ul>
 <li>存放数据的<code>data</code></li>
 <li>存放左右子树的<code>指针域</code></li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> LEN sizeof(struct BiTNode)</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span>{</span>
    <span class="hljs-type">char</span> data;   <span class="hljs-comment">//用于存放结点数据</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Lchild</span>;</span>   <span class="hljs-comment">//存放左孩子地址</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Rchild</span>;</span>   <span class="hljs-comment">//存放右孩子地址</span>
};
</code></pre>
<!-- more -->
<p>接着，我们开始创建树，我们使用的是返回树根节点地址的方式</p>
<ul>
 <li>先创建一个临时结点</li>
 <li>在创建一个临时 char 变量，用于存放用户输入的内容</li>
 <li>scanf 让用户输入内容，将值赋值给 char 变量</li>
 <li>如果输入的是#号，则表示结点为空</li>
 <li>如果不为空，则将这个结点看成根，将输入的值赋予根结点</li>
 <li>接着将这个根的左右子树通过递归获取到</li>
 <li>将所有的叶子结点都输入为#后，即所有结点都是 NULL ，没有左右子树，树创建完成。</li>
</ul>
<pre><code class="hljs language-c">
<span class="hljs-keyword">struct</span> BiTNode *<span class="hljs-title function_">Create</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span>{
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">tempTree</span>;</span>
    <span class="hljs-type">char</span> tempC;
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>,&amp;tempC);
    <span class="hljs-keyword">if</span>(tempC==<span class="hljs-string">'#'</span>){   <span class="hljs-comment">//如果结点为空，那么就赋值为 NULL</span>
        tempTree=<span class="hljs-literal">NULL</span>;
    }<span class="hljs-keyword">else</span>{
    <span class="hljs-comment">//如果结点不为空，那就生成动态存储空间</span>
        tempTree=(<span class="hljs-keyword">struct</span> BiTNode *)<span class="hljs-built_in">malloc</span>(LEN);
        <span class="hljs-comment">//我们默认输入的时候按照前序遍历输入，所以先存储结点值，然后左右子树遍历</span>
        tempTree-&gt;data=tempC;
        tempTree-&gt;Lchild=Create();  <span class="hljs-comment">//左子树递归</span>
        tempTree-&gt;Rchild=Create();  <span class="hljs-comment">//右子树递归</span>
    }
    <span class="hljs-keyword">return</span> tempTree;  <span class="hljs-comment">//返回根结点地址</span>
}
</code></pre>
<p>树的遍历，我们使用递归的思路，其实很好遍历</p>
<ul>
 <li>前序遍历是，根在前面，所以是中左右，所以先输出 data，再递归左右子树</li>
 <li>中序遍历是，根在中间，所以是左中右，所以先递归左子树，再输出 data，再递归右子树</li>
 <li>后序遍历是，根在后面，所以是左右中，所以先递归左右子树，再输出 data。</li>
 <li>我们判断条件也非常简单，只要结点不为 NULL，就执行！</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">void</span> <span class="hljs-title function_">PreVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
        PreVisitTree(Temptree-&gt;Lchild);
        PreVisitTree(Temptree-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">InVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        InVisitTree(Temptree-&gt;Lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
        InVisitTree(Temptree-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">PostVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        PostVisitTree(Temptree-&gt;Lchild);
        PostVisitTree(Temptree-&gt;Rchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
    }
}
</code></pre>
<p>主调 Main 函数</p>
<ul>
 <li>首先创建一个树指针，用于存放生成好的树。</li>
 <li>然后使用前序、中序、后续遍历的函数，分别调用这棵树。</li>
 <li>输出的结果就是这棵树的前序、中序、后续遍历了。</li>
</ul>
<pre><code class="hljs language-c"><span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> * <span class="hljs-title">tree</span>;</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入结点：\n"</span>);
    tree=Create();
    PreVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    InVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    PostVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}
</code></pre>
<h1 id="%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81">完整代码</h1>
<pre><code class="hljs language-c"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">define</span> LEN sizeof(struct BiTNode)</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span>{</span>
    <span class="hljs-type">char</span> data;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Lchild</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">Rchild</span>;</span>
};

<span class="hljs-keyword">struct</span> BiTNode *<span class="hljs-title function_">Create</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span>{
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> *<span class="hljs-title">tempTree</span>;</span>
    <span class="hljs-type">char</span> tempC;
    <span class="hljs-built_in">scanf</span>(<span class="hljs-string">"%c"</span>,&amp;tempC);
    <span class="hljs-keyword">if</span>(tempC==<span class="hljs-string">'#'</span>){
        tempTree=<span class="hljs-literal">NULL</span>;
    }<span class="hljs-keyword">else</span>{
        tempTree=(<span class="hljs-keyword">struct</span> BiTNode *)<span class="hljs-built_in">malloc</span>(LEN);
        tempTree-&gt;data=tempC;
        tempTree-&gt;Lchild=Create();
        tempTree-&gt;Rchild=Create();
    }
    <span class="hljs-keyword">return</span> tempTree;
}

<span class="hljs-type">void</span> <span class="hljs-title function_">PreVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
        PreVisitTree(Temptree-&gt;Lchild);
        PreVisitTree(Temptree-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">InVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        InVisitTree(Temptree-&gt;Lchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
        InVisitTree(Temptree-&gt;Rchild);
    }
}

<span class="hljs-type">void</span> <span class="hljs-title function_">PostVisitTree</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> BiTNode *Temptree)</span>{
    <span class="hljs-keyword">if</span>(Temptree){
        PostVisitTree(Temptree-&gt;Lchild);
        PostVisitTree(Temptree-&gt;Rchild);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%c"</span>,Temptree-&gt;data);
    }
}

<span class="hljs-type">int</span> <span class="hljs-title function_">main</span><span class="hljs-params">()</span>{
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BiTNode</span> * <span class="hljs-title">tree</span>;</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"请输入前序结点：\n"</span>);
    tree=Create();
    PreVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    InVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
    PostVisitTree(tree);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"\n"</span>);
}
</code></pre>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<blockquote>
 <p><strong>输出结果</strong>
  <br>
  请输入前序结点：
  <br>
  ABDH##I##E#J##CF#K##G##
  <br>
  ABDHIEJCFKG
  <br>
  HDIBEJAFKCG
  <br>
  HIDJEBKFGCA</p>
</blockquote>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_055_tree_traversing_binary_tree_02</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Fri, 7 Dec 2018 01:01:11 GMT</pubDate></item><item><title><![CDATA[数据结构之树-超详细二叉树的遍历（一）-学习笔记-54]]></title><link>https://www.bliner.me/archives/Data_sturcture_course_054_tree_traversing_binary_tree_01</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E8%B6%85%E8%AF%A6%E7%BB%86%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%81%8D%E5%8E%86%EF%BC%88%E4%B8%80%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-54&amp;url=/archives/Data_sturcture_course_054_tree_traversing_binary_tree_01" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>这节课是二叉树的考试重点，通过什么样的方式遍历二叉树，这里需要把内容搞清楚，不要混淆。</p>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E9%81%8D%E5%8E%86">二叉树遍历</h1>
<p>二叉树遍历(traversing binary tree)，指的是</p>
<ul>
 <li>从根结点出发，按<code>照某种次序</code></li>
 <li><code>依次访问</code>二叉树中的所有结点</li>
 <li>使得每个结点都被访问一次，且<code>仅有一次</code>。</li>
</ul>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E8%B7%9F%E7%BA%BF%E6%80%A7%E7%BB%93%E6%9E%84%E7%9A%84%E4%B8%8D%E5%90%8C">二叉树跟线性结构的不同</h1>
<p>线性结构的便利，最多是</p>
<ul>
 <li>顺序</li>
 <li>循环</li>
 <li>双向</li>
 <li>都是简单的遍历方式</li>
</ul>
<p>树就不一样了</p>
<ul>
 <li>树的结点之间不存在唯一的前驱和后继这样的关系</li>
 <li>一个结点后面会跟着左子树和右子树两个结点，不是唯一的</li>
 <li>所以下一个结点的选择是不同的</li>
</ul>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%81%8D%E5%8E%86%E6%96%B9%E5%BC%8F">二叉树的遍历方式</h1>
<p>如果都是从左到右的话，遍历方式分为 4 种</p>
<ul>
 <li>前序遍历</li>
 <li>中序遍历</li>
 <li>后序遍历</li>
 <li>层序遍历</li>
</ul>
<!-- more -->
<h2 id="%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86">前序遍历</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果二叉树为空，则返回，否则</p>
<ul>
 <li>先访问根节点</li>
 <li>然后前序遍历左子树</li>
 <li>接着前序遍历右子树</li>
</ul>
<p>遍历的技巧</p>
<ul>
 <li>每个结点都看成一个根</li>
 <li>按照根、左子树、右子树顺序输出</li>
 <li>然后返回上一层</li>
 <li>再进行根、左子树、右子树的顺序输出</li>
</ul>
<blockquote>
 <p><strong>上面的答案是</strong>
  <br>
  ABDHIEJCFKG</p>
</blockquote>
<p>超详细解题思路</p>
<ul>
 <li>A 是 ABC 的根，输出 A，走向左子树 B</li>
 <li>B 是 BDE 的根，输出 B，走向左子树 D</li>
 <li>D 是 DHI 的跟，输出 D，走向左子树 H</li>
 <li>H 本身就是根，没有左右子树</li>
 <li>DHI 的根和左子树输出完了，接着输出右子树 I</li>
 <li>I 本身就是根，没有左右子树</li>
 <li>DHI 的根和左右子树都输出完了，接着输出 BDE</li>
 <li>BDE 的根 B 输出了、D 左子树也输出了，所以输出右子树 E</li>
 <li>E 是 EJ 的根，输出 E，走向左子树，结果没有，走向右子树 J</li>
 <li>J 本身就是根，没有左右子树</li>
 <li>BDE 全部输出完了，ABC 已经输出了根 A，左子树 B，开始输出右子树 C</li>
 <li>C 是 CFG 的根，输出 C，走向左子树 F</li>
 <li>F 是 FK 的根，输出 F，走向左子树，结果没有，走向右子树 K</li>
 <li>K 本身就是跟，没有左右子树</li>
 <li>FK 都输出完了，CFG 的 C 根已经输出了，F 左子树也输出了，开始输出 G</li>
 <li>G 本身就是根，没有左右子树</li>
 <li>全部输出完毕</li>
 <li>答案是 ABDHIEJCFKG</li>
</ul>
<h2 id="%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86">中序遍历</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果二叉树为空，则返回，否则</p>
<ul>
 <li>从根结点开始，并不是输出根节点，而是从根结点开始，根据一下规则</li>
 <li>先中序遍历左子树</li>
 <li>接着访问根节点</li>
 <li>最后中序遍历右子树</li>
</ul>
<p>遍历的技巧</p>
<ul>
 <li>每个结点都看成一个根</li>
 <li>先放问左子树，如果坐子树还有左子树</li>
 <li>那就把它当成根，继续向下</li>
 <li>按照左子树、根、右子树顺序遍历</li>
</ul>
<blockquote>
 <p><strong>上面的答案是</strong>
  <br>
  HDIBEJAFKCG</p>
</blockquote>
<p>超详细解答</p>
<ul>
 <li>B 是 ABC 的左子树， 访问 B</li>
 <li>D 是 BDE 的左子树，访问 D</li>
 <li>H 是 DHI 的左子树，本身就是根，直接输出 H，然后访问根 D</li>
 <li>D 是 DHI 的根，已经输出了左子树 H，接着输出根 D，然后访问右子树 I</li>
 <li>I 是 DHI 的右子树，本身就是根，直接输出 I，然后 BDE 的左子树和根都输出了</li>
 <li>E 是 BDE 的右子树，E 是 EJ 的根，EJ 没有左子树，输出 EJ 的根 E</li>
 <li>J 是 EJ 的右子树，左子树没有，根已经输出了，输出右子树 J</li>
 <li>A 是 ABC 的根，左子树 B 已经全部输出，接着输出根 A，访问右子树 C</li>
 <li>F 是 CFG 的左子树，F 是 FK 的根</li>
 <li>F 是 FK 的跟，FK 没有左子树，输出根 F</li>
 <li>K 是 FK 的右子树，FK 没有左子树，根已经输出，输出右子树 K</li>
 <li>C 是 CFG 的根，左子树 F 已经全部输出，输出根 C</li>
 <li>G 是 CFG 的右子树，本身就是根，没有左右子树，直接输出</li>
 <li>全部输出完毕
  <ul>
   <li>答案是 HDIBEJAFKCG</li>
  </ul></li>
</ul>
<blockquote>
 <p><strong>1秒解题小技巧</strong>
  <br>
  想想一根垂直的线在树的左侧
  <br>
  从<code>左向右扫描，扫到谁就输出谁</code></p>
</blockquote>
<h2 id="%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86">后序遍历</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果二叉树为空，则返回，否则</p>
<ul>
 <li>从左到右，先叶子后结点</li>
 <li>遍历左右子树</li>
 <li>最后访问根节点</li>
</ul>
<blockquote>
 <p><strong>上面的答案是</strong>
  <br>
  HIDJEBKFGCA</p>
</blockquote>
<p>超详细解答</p>
<ul>
 <li>先找到最左侧的叶子 H 输出</li>
 <li>H 是 DHI 的左子树，接着输出右子树 I</li>
 <li>D 是 DHI 的根，左子树、右子树都输出了，接着输出根 D</li>
 <li>D 是 BDE 的左子树，接着找到 E，E 是结点，向下找</li>
 <li>J 是 EJ 的根，EJ 没有左子树，所以输出右子树 J</li>
 <li>E 是 EJ 的根，EJ 没有左子树，右子树 J 已经输出，现在输出根 E</li>
 <li>B 是 BED 的根，D左子树 和 E右子树都已经输出，现在输出根 B</li>
 <li>C 是 ABC 的右子树，右子树 C 是 CFG 的根，左子树是 F</li>
 <li>F 是 FK 的根，FK 没有左子树，输出右子树 K</li>
 <li>F 是 FK 的跟，FK 没有左子树，右子树 K 已经 输出，现在输出根 F</li>
 <li>F 是 CFG 的左子树，开始输出右子树 G</li>
 <li>G 本身就是根，没有左右子树，直接输出 G</li>
 <li>C 是 CFG 的根，左子树 F 已经输出，G 右子树也已经输出，现在输出 C</li>
 <li>A 是 ABC 的根，左子树 B 和右子树 C 都已经输出，现在开始输出根 A
  <ul>
   <li>全部输出完毕</li>
   <li>答案是 HIDJEBKFGCA</li>
  </ul></li>
</ul>
<h2 id="%E5%B1%82%E5%BA%8F%E4%BE%BF%E5%88%A9">层序便利</h2>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15441023697960.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果二叉树为空，则返回，否则</p>
<ul>
 <li>从第一层，也就是树的根节点开始</li>
 <li>从上到下，从左到右</li>
 <li>按照顺序对结点进行访问</li>
</ul>
<blockquote>
 <p><strong>上面的答案是</strong>
  <br>
  ABCDEFGHIJK</p>
</blockquote>
<p>这个不用讲了，最简单了！</p>
<h1 id="%E6%80%BB%E7%BB%93">总结</h1>
<ul>
 <li>前序：根左右</li>
 <li>中序：左根有</li>
 <li>后续：左右根</li>
</ul>
<p>就是根在不同的位置，进行不同的遍历。
 <br>
 遍历的不同方式决定你如何使用树中的各个结点，我们下节课再见。</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_sturcture_course_054_tree_traversing_binary_tree_01</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 6 Dec 2018 13:03:00 GMT</pubDate></item><item><title><![CDATA[数据结构之树-二叉树的存储结构-学习笔记-53]]></title><link>https://www.bliner.me/archives/Data_structure_course_053_tree_binary_tree_stroage_sturctuce</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-53&amp;url=/archives/Data_structure_course_053_tree_binary_tree_stroage_sturctuce" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>前面，我们学习了二叉树的五大特性，那么既然说了这么久的二叉树，是时候讲一讲二叉树的数据结构该如何定义了。</p>
<h1 id="%E5%9B%9E%E9%A1%BE">回顾</h1>
<blockquote>
 <p><strong>如果你忘了之前的内容，点击下面的链接回顾：</strong>
  <br>
  <a href="https://www.bliner.me/2018/12/Data_structure_course_047_tree_tree_storage_structure_01/">数据结构之树-树的存储结构（一）-学习笔记-47</a>
  <br>
  <a href="https://www.bliner.me/2018/12/Data_structure_course_048_tree_tree_storage_structure_02/">数据结构之树-树的存储结构（二）-学习笔记-47</a></p>
</blockquote>
<h1 id="%E6%80%9D%E8%80%83">思考</h1>
<p>根据我们之前讨论树的存储结构，我们可以直到，树和结点里面包含的元素可以很灵活，我们甚至可以将链式存储结构跟顺序存储结构结合在一起使用。因为这样表达树这个数据结构才方便。但是</p>
<ul>
 <li>二叉树是一种特殊的树，由于它的特殊性，使得</li>
 <li>顺序存储结构和链式存储结构都可以轻松的实现二叉树</li>
</ul>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%A1%BA%E5%BA%8F%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">二叉树的顺序存储结构</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15440804407197.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<!-- more -->
<p>二叉树的顺序存储结构就是利用一维数组</p>
<ul>
 <li>数组中存放二叉树的各个结点</li>
 <li>各个结点的存储位置能体现结点之间的逻辑关系</li>
</ul>
<p>上面的图中是一棵完全二叉树，我们将它存放进一维数组</p>
<table>
 <thead>
  <tr>
   <th>元素</th>
   <th>A</th>
   <th>B</th>
   <th>C</th>
   <th>D</th>
   <th>E</th>
   <th>F</th>
   <th>G</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>下标</td>
   <td>1</td>
   <td>2</td>
   <td>3</td>
   <td>4</td>
   <td>5</td>
   <td>6</td>
   <td>7</td>
  </tr>
 </tbody>
</table>
<p>我们发现，使用层序法排列的完全二叉树</p>
<ul>
 <li>元素是 ABCDEFG</li>
 <li>下标是 1234567</li>
 <li>完美！（下标0存储的是结点个数）</li>
 <li>我们完全可以通过上面的一维数组，使用层序法，重新画出这课二叉树。</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15440806935336.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们在看一下这张图，图中并不是完全二叉树，更不是满二叉树</p>
<ul>
 <li>如果遇到空结点，我们使用<code>^</code>，代替即可</li>
</ul>
<table>
 <thead>
  <tr>
   <th>元素</th>
   <th>A</th>
   <th>B</th>
   <th>C</th>
   <th>D</th>
   <th>^</th>
   <th>F</th>
   <th>G</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>下标</td>
   <td>1</td>
   <td>2</td>
   <td>3</td>
   <td>4</td>
   <td>5</td>
   <td>6</td>
   <td>7</td>
  </tr>
 </tbody>
</table>
<p>还记得斜二叉树嘛？</p>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439763070779.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>它的存储可能会是</p>
<table>
 <thead>
  <tr>
   <th>元素</th>
   <th>A</th>
   <th>B</th>
   <th>^</th>
   <th>C</th>
   <th>^</th>
   <th>^</th>
   <th>^</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>下标</td>
   <td>1</td>
   <td>2</td>
   <td>3</td>
   <td>4</td>
   <td>5</td>
   <td>6</td>
   <td>7</td>
  </tr>
 </tbody>
</table>
<p>这里我们就发现不对了！</p>
<ul>
 <li>数组长度有8个</li>
 <li>因为是斜树，浪费了一半多的存储空间</li>
 <li>所以，顺序存储结构之后，链式存储应运而生。</li>
</ul>
<h1 id="%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%93%BE%E5%BC%8F%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84">二叉树的链式存储结构</h1>
<p>在说明链式存储结构前，我们先想象，如果是链式结构</p>
<ul>
 <li>一个存数据，两个存左子树和右子树最棒了</li>
 <li>我们称这样的链表，叫做<code>二叉链表</code>。</li>
</ul>
<table>
 <thead>
  <tr>
   <th>左子树指针域</th>
   <th>数据域</th>
   <th>右子树指针域</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Lchild</td>
   <td>Data</td>
   <td>Rchild</td>
  </tr>
 </tbody>
</table>
<pre><code class="hljs language-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BTNode</span>{</span>
<span class="hljs-type">int</span> data;  <span class="hljs-comment">//存放数据</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BTNode</span> *<span class="hljs-title">Lchild</span>;</span>  <span class="hljs-comment">//存放左子树结点位置</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BTNode</span> *<span class="hljs-title">Rchild</span>;</span>  <span class="hljs-comment">//存放右子树结点位置</span>
};
</code></pre>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-06-15440814157951.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如图所示，我们就可以使用链表构建一棵树了，非常简单！
 <br>
 我们之后基本上都是用链式存储结构构建二叉树。</p>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_053_tree_binary_tree_stroage_sturctuce</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 6 Dec 2018 03:18:53 GMT</pubDate></item><item><title><![CDATA[数据结构之树-二叉树的性质（二）-学习笔记-52]]></title><link>https://www.bliner.me/archives/Data_structure_course_052_tree_binary_tree_property_02</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%80%A7%E8%B4%A8%EF%BC%88%E4%BA%8C%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-52&amp;url=/archives/Data_structure_course_052_tree_binary_tree_property_02" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>我们上节课我们讲了三条性质，从层数、层节点数、深度、总结点数、以及度的概念了解了二叉树的一些有趣的性质，今天我们继续看看，二叉树还有什么有趣的特性。</p>
<h1 id="%E4%BB%8E%E8%8A%82%E7%82%B9%E6%95%B0%E5%88%A4%E6%96%AD%E6%B7%B1%E5%BA%A6%EF%BC%8C%E4%BB%8E%E6%B7%B1%E5%BA%A6%E5%88%A4%E6%96%AD%E8%8A%82%E7%82%B9%E6%95%B0">从节点数判断深度，从深度判断节点数</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439778449301.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>预习一下取整符号：</p>
<ul>
 <li>$\left \lfloor 这是向下取整符号 \right \rfloor$</li>
 <li>$\left \lfloor 2.4 \right \rfloor=2$ 向下取整到2</li>
 <li>$\left \lceil 2.4 \right \rceil=3$ 向上取整到3</li>
</ul>
<p>具有 n 个结点的<code>完全二叉树</code>的深度 K 为$\left \lfloor \log_{2}n \right \rfloor+1$</p>
<ul>
 <li>n 表示完全二叉数的结点数，上面的图中 n=5</li>
 <li>上图的完全二叉树的深度为：$\left \lfloor \log_{2}5 \right \rfloor+1$</li>
 <li>$2^n=5$然后 n 再加 1。</li>
 <li>$n \approx 2.3$，再加上向下取整$\left \lfloor 2.3 \right \rfloor=2$</li>
 <li>得到$2+1=3$，这棵完全二叉树有 3 层</li>
</ul>
<!-- more -->
<p>我们再看看<code>满二叉树</code>如何通过结点数判断深度</p>
<ul>
 <li>之前我们直到深度为 k 的满二叉树的节点数为$n=2^{k}-1$个。</li>
 <li>我们倒推一下$n=2^{k}-1$，得到满二叉树的深度为 $k=\log_2(n+1)$。</li>
</ul>
<p>还记得完全二叉树的叶子吗？只会出现在最下面两层，所以我们可以推出</p>
<ul>
 <li>完全二叉树的倒数第二层一定是满二叉树，就像上面的图。</li>
 <li>如果完全二叉树的倒数第二层没有满，就开始最后一层的内容，肯定就不是完全二叉树了，所以</li>
 <li><code>完全二叉树</code>的倒数第二层一定是<code>满二叉树</code>，完全二叉树倒数第二层的满二叉树的结点数为$n=2^{(k-1)}-1$。上图的计算就是</li>
 <li>$n=2^{3-1}-1=3$，所以倒数第二层的回推总点数为 3</li>
</ul>
<p>综上，我们知道了</p>
<ul>
 <li><code>完全二叉树</code>倒数第二层满二叉树的最大节点数，$n=2^{(k-1)}-1$</li>
 <li><code>完全二叉树</code>深度为 k 的最大结点树为$n=2^{k}-1$</li>
 <li>所以，<code>完全二叉树</code>结点 n 的取值范围是：$2^{(k-1)}-1 &lt; n \leq 2^{k}-1$</li>
 <li>所以，$2^{(k-1)} &lt; n \leq 2^{k}$</li>
 <li>左右取对数，$k-1 \leq log_2n &lt; k$</li>
 <li>k 也要取整，具有 n 个结点的<code>完全二叉树</code>的深度 K 为$\left \lfloor \log_{2}n \right \rfloor+1$</li>
</ul>
<h1 id="%E6%8C%89%E5%B1%82%E5%BA%8F%E7%BC%96%E5%8F%B7%E7%9A%84%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%A7%E8%B4%A8">按层序编号的完全二叉树的一些性质</h1>
<p>如果有一个 n 个结点的<code>完全二叉树</code></p>
<ul>
 <li>深度为 $\left \lfloor \log_{2}n \right \rfloor+1$ 的结点按层序编号</li>
 <li>对于任一结点 i ($1 \leq i \leq n$)</li>
 <li>对于上面的这棵完全二叉树，都有一下性质成立</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439778449301.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<h2 id="%E7%BB%93%E7%82%B9-i-%E7%9A%84%E5%BA%8F%E5%8F%B7">结点 i 的序号</h2>
<ul>
 <li>如果 i=1 , 则结点 i 是二叉树的根，根无双亲。</li>
 <li>如果 i&gt;1，则双亲是结点$\left \lfloor\frac{i}{2} \right \rfloor $</li>
 <li>例如 i=4，双亲结点是$\left \lfloor\frac{4}{2} \right \rfloor =2$</li>
 <li>例如 i=3，双亲结点是$\left \lfloor\frac{3}{2} \right \rfloor =\left \lfloor 1.5 \right \rfloor=1$</li>
 <li>如果2i&gt;n，并且结点 i 为叶子结点，则结点 i 没有左孩子</li>
 <li>例如i=3，$ 2 \times 3=6 &gt; 5$，结点 3 并有左孩子</li>
 <li>结点 i 的左孩子是 2i</li>
 <li>例如i=2，$ 2 \times 2=4 $，结点 2 左孩子为4</li>
 <li>如果2i+1&gt;n，则结点 i 没有右孩子</li>
 <li>例如i=3，$ 2 \times 3=6 $，结点 3 没有右孩子</li>
 <li>结点 i 的右孩子都是 2i+1</li>
</ul>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_052_tree_binary_tree_property_02</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Thu, 6 Dec 2018 00:32:23 GMT</pubDate></item><item><title><![CDATA[数据结构之树-二叉树的性质（一）-学习笔记-51]]></title><link>https://www.bliner.me/archives/Data_structure_course_051_tree_binary_tree_property_01</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%80%A7%E8%B4%A8%EF%BC%88%E4%B8%80%EF%BC%89-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-51&amp;url=/archives/Data_structure_course_051_tree_binary_tree_property_01" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>我们介绍了树，介绍了树中使用最多的二叉树，以及一些特殊的二叉树。今天我们就来看看二叉树为什么很好的体现了树的特性，二叉树有哪些有趣的特点呢？</p>
<h1 id="%E6%AF%8F%E4%B8%80%E5%B1%82%E6%9C%80%E5%A4%A7%E7%9A%84%E8%8A%82%E7%82%B9%E6%95%B0">每一层最大的节点数</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439764826207.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<ul>
 <li>在二叉树的第 i 层，至多有$2^{i-1} (i \geq 1)$ 个结点</li>
 <li>上面这张图中</li>
 <li>第一层$2^0=1$个结点</li>
 <li>第二层$2^1=2$个结点</li>
 <li>第三层$2^2=4$个结点</li>
 <li>第四层$2^3=8$个结点</li>
</ul>
<!-- more -->
<h1 id="%E4%BB%8E%E6%B7%B1%E5%BA%A6%E5%88%A4%E6%96%AD%E8%8A%82%E7%82%B9%E6%95%B0">从深度判断节点数</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439764826207.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<ul>
 <li>深度为 k 的二叉树，至多有$2^{k}-1 (k \geq 1)$ 个结点。</li>
 <li>也就是 K 层二叉树，满了也就是$2^{k}-1 (k \geq 1)$ 个结点。</li>
 <li>一层$2^1-1=1$个结点</li>
 <li>二层$2^2-1=3$个结点</li>
 <li>三层$2^3-1=7$个结点</li>
 <li>四层$2^4-1=15$个结点</li>
</ul>
<h1 id="%E6%A0%91%E4%B8%AD%E5%BA%A6%E7%9A%84%E5%85%B3%E7%B3%BB">树中度的关系</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439719254815.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>我们先回顾一下度的概念</p>
<ul>
 <li>度是每个结点包含的孩子数</li>
 <li>度为2表示这个结点既有左子树，也有右子树</li>
 <li>度为1表示这个结点可能只有左子树、或者只有右子树</li>
 <li>度为0表示这个结点没有孩子，也称之为终端结点</li>
</ul>
<p>上面的二叉树中</p>
<ul>
 <li>度为2：A、C 和 D，共3个，记作 n2</li>
 <li>度为1：B、H 、E，共3个，记作 n1</li>
 <li>度为0：G、J、I、F，共4个，记作 n0</li>
 <li>结点的总数就是n2+n1+n0=3+3+4=10个结点</li>
</ul>
<p>我们又发现</p>
<ul>
 <li>结点和结点之间的连接数，也就是那条黑线，总是为<code>总节点数-1</code></li>
 <li>并且这个<code>总节点数-1</code>总是等于 $n1+2\times n^{2}$</li>
 <li>所以$n-1=n1+2\times n2$</li>
 <li>所以$n0+n1+n2-1=n1+n2+n2$</li>
 <li>所以$n0=n2+1$</li>
 <li>所以，度为0的结点数等于度为2的结点数+1</li>
</ul>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_051_tree_binary_tree_property_01</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Wed, 5 Dec 2018 03:31:06 GMT</pubDate></item><item><title><![CDATA[数据结构之树-特殊的二叉树-学习笔记-50]]></title><link>https://www.bliner.me/archives/Data_structure_course_050_tree_special_binary_tree</link><description><![CDATA[<img src="https://www.bliner.me/plugins/feed/assets/telemetry.gif?title=%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%91-%E7%89%B9%E6%AE%8A%E7%9A%84%E4%BA%8C%E5%8F%89%E6%A0%91-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-50&amp;url=/archives/Data_structure_course_050_tree_special_binary_tree" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E5%BC%95%E5%85%A5">引入</h1>
<p>我们介绍了什么是二叉树、以及二叉树一些与众不同的特点，例如只能有两个孩子，两个孩子还分为左子树和右子树，那么今天我们来看看特殊的二叉树。</p>
<h1 id="%E6%96%9C%E6%A0%91">斜树</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439763070779.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt="">
 <br>
 ¡¡™
 <br>
 顾名思义，斜树一定要倾斜。也就是说，一棵树</p>
<ul>
 <li>只有左子树</li>
 <li>只有右子树</li>
</ul>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439763534415.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>这样就<code>不是斜树了。</code></p>
<!-- more -->
<h1 id="%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91">满二叉树</h1>
<p><img src="https://www.bliner.me/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fimage.bliner.me%2F2018-12-05-15439764826207.jpg%3FimageView2%2F0%2Fq%2F100%257Cwatermark%2F1%2Fimage%2FaHR0cDovL2ltYWdlLmJsaW5lci5tZS9ibGluZXJ3YXRlcm1hcmsucG5n%2Fdissolve%2F40%2Fgravity%2FSouthWest%2Fdx%2F10%2Fdy%2F10%257Cimageslim&amp;size=m" alt=""></p>
<p>如果有一棵树</p>
<ul>
 <li>所有分支的结点</li>
 <li><code>都存在左子树和右子树</code></li>
 <li>并且所有<code>叶子都在同一层上</code></li>
 <li>我们就称之为<code>满二叉树</code></li>
</ul>
<h2 id="%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E7%89%B9%E7%82%B9">满二叉树的特点</h2>
<ul>
 <li><code>叶子只能出现在最下一层</code>，如果最下一层不是叶子，那证明所有叶子并不是在同一层上，不符合满二叉树条件</li>
 <li><code>非叶子结点的度一定是 2</code>，度是每个结点的子树数量，既然是二叉树，不是叶子结点度一定是2</li>
 <li><code>在同样深度的二叉树中，满二叉树的结点一定最多</code>，这基本是废话，层数相同，满二叉树的每个结点都有左子树和右子树，结点一定是满二叉树最多。</li>
</ul>
<h1 id="%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91">完全二叉树</h1>
<p>如果存在一棵树</p>
<ul>
 <li>具有 n 个结点的二叉树按层序编号。</li>
 <li>编号为 i ($1 \leq i \leq n$) 的结点，与同样深度的满二叉树中的 i 结点</li>
 <li>如果 i 和 i 位置完全相同，我们就称这棵二叉树为完全二叉树</li>
</ul>
<p>我们来看看</p>
<table>
 <thead>
  <tr>
   <th>完全二叉树</th>
   <th>满二叉树</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td></td>
  </tr>
 </tbody>
</table>
<p>你可以这么理解</p>
<ul>
 <li>完全二叉树的结点可能不如满二叉树多</li>
 <li>但是完全二叉树的所有结点序号一定能在满二叉树中找到</li>
 <li>完全二叉树是即将完成的满二叉树…中间『停止施工』了而已…</li>
</ul>
<h2 id="%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E7%89%B9%E7%82%B9">完全二叉树的特点</h2>
<ul>
 <li><code>叶子结点只能出现在最下两层</code>，因为每个结点的编号是一层一层的写，要满足完全二叉树的所有编号跟满二叉树编号相同，必然不能突破两层，因为超过两层，第三层的哪几个结点的序号，肯定就找不到了。</li>
 <li><code>最下层的叶子一定集中在左侧连续位置</code>，必须的啊，因为新的一层是从左向右编号，也在一定是在左侧连续位置啊，右边还没编号呢…</li>
 <li><code>倒数第二层，如果有叶子结点，一定都在右部连续位置</code>，也是必须的啊，倒数第二层的叶子，肯定是最后一层叶子没占的那些地方啊！</li>
 <li><code>如果结点的度为1，那该结点只有左孩子</code>，也是废话，从左向右编号，结点只有1个孩子，那肯定是左孩子啊….</li>
 <li><code>同样结点的二叉树，完全二叉树深度最小</code>，必须的啊，深度是层数，完全二叉树必须补完一层再来下一层，肯定是完全二叉树深度最小。</li>
</ul>
<blockquote>
 <p>**注意：**满二叉树一定是完全二叉树，完全二叉树不一定是满二叉树。</p>
</blockquote>
<h1 id="%E4%B8%80%E4%BA%9B%E5%8F%8D%E4%BE%8B">一些反例</h1>
<table>
 <thead>
  <tr>
   <th>图示</th>
   <th>原因</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td></td>
   <td>5号结点没有左子树，竟然出现了右子树，错误！</td>
  </tr>
  <tr>
   <td></td>
   <td>6号7号应该是3号的左右子树，第三层没有补全就写了第四层，错误！</td>
  </tr>
  <tr>
   <td></td>
   <td>5号下面的左右子树没有，就直接蹦到后面去了，这不是完全二叉树，错误！</td>
  </tr>
 </tbody>
</table>
<h1 id="%E5%B0%BE%E5%B7%B4">尾巴</h1>
<p>这是我的个人学习笔记，主要是应付考研复习使用，充斥着一些吐槽和个人观点，并不严谨，欢迎大家参考、指正。</p>]]></description><guid isPermaLink="false">/archives/Data_structure_course_050_tree_special_binary_tree</guid><dc:creator>Bliner</dc:creator><category>笔记&amp;教程</category><pubDate>Wed, 5 Dec 2018 02:15:49 GMT</pubDate></item></channel></rss>