常用算法冒泡、插入、选择、快速和二叉树详解

<p class="postTitle"> 


<div class="postBody">
<div id="cnblogs_post_body" class="blogpost-body">

  同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。

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

定义

  在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。

算法复杂度

  算法复杂度分为时间复杂度和空间复杂度。其作用: 时间复杂度是指执行算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。(算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度)。

时间复杂度

  1. 一般情况下,算法的基本操作重复执行的次数是模块n的某一个函数f(n),因此,算法的时间复杂度记做:T(n)=O(f(n))

分析:随着模块n的增大,算法执行的时间的增长率和 f(n) 的增长率成正比,所以 f(n) 越小,算法的时间复杂度越低,算法的效率越高。

  2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出 T(n) 的同数量级(它的同数量级有以下:1,log(2)n,n,n log(2)n ,n的平方,n的三次方,2的n次方,n!),找出后,f(n) = 该数量级,若 T(n)/f(n) 求极限可得到一常数c,则时间复杂度T(n) = O(f(n))

  例:算法:

  则有 T(n) = n 的平方+n的三次方,根据上面括号里的同数量级,我们可以确定 n的三次方 为T(n)的同数量级

  则有 f(n) = n的三次方,然后根据 T(n)/f(n) 求极限可得到常数c

  则该算法的时间复杂度:T(n) = O(n^3) 注:n^3即是n的3次方。

  3.在pascal中比较容易理解,容易计算的方法是:看看有几重for循环,只有一重则时间复杂度为O(n),二重则为O(n^2),依此类推,如果有二分则为O(logn),二分例如、二分查找,如果一个for循环套一个二分,那么时间复杂度则为O(nlogn)。

 常用排序

<table border="1" cellspacing="0" cellpadding="0">
<tr>
<td align="left" valign="top">

名称

复杂度

说明

备注

冒泡排序

</td>
<td valign="top" width="84">

O(N*N)

</td>
<td valign="top" width="306">

将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮

</td>
<td valign="top" width="126">

</td>

</tr>
<tr>
<td valign="top" width="91">

插入排序

Insertion sort

</td>
<td valign="top" width="84">

O(N*N)

</td>
<td valign="top" width="306">

逐一取出元素,在已经排序的元素序列中从后向前扫描,放到适当的位置

</td>
<td valign="top" width="126">

起初,已经排序的元素序列为空

</td>

</tr>
<tr>
<td valign="top" width="91">

选择排序

</td>
<td valign="top" width="84">

O(N*N)

</td>
<td valign="top" width="306">

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此递归。

</td>
<td valign="top" width="126">

</td>

</tr>
<tr>
<td valign="top" width="91">

快速排序

Quick Sort

</td>
<td valign="top" width="84">

O(n *log2(n))

</td>
<td valign="top" width="306">

先选择中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(递归)。

</td>
<td valign="top" width="126">

</td>

</tr>
<tr>
<td valign="top" width="91">

堆排序HeapSort

</td>
<td valign="top" width="84">

O(n *log2(n))

</td>
<td valign="top" width="306">

利用堆(heaps)这种数据结构来构造的一种排序算法。堆是一个近似完全二叉树结构,并同时满足堆属性:即子节点的键值或索引总是小于(或者大于)它的父节点。

</td>
<td valign="top" width="126">

近似完全二叉树

</td>

</tr>
<tr>
<td valign="top" width="91">

希尔排序

SHELL

</td>
<td valign="top" width="84">

O(n1+)

0<£<1

</td>
<td valign="top" width="306">

选择一个步长(Step),然后按间隔为步长的单元进行排序.递归,步长逐渐变小,直至为1.

</td>
<td valign="top" width="126">

</td>

</tr>
<tr>
<td valign="top" width="91">

箱排序Bin Sort

</td>
<td valign="top" width="84">

O(n)

</td>
<td valign="top" width="306">

设置若干个箱子,把关键字等于 k 的记录全都装入到第k 个箱子里 ( 分配 ) ,然后按序号依次将各非空的箱子首尾连接起来 ( 收集 ) 。

</td>
<td rowspan="2" valign="top" width="126">

分配排序的一种:通过" 分配 " 和 " 收集 " 过程来实现排序。

</td>

</tr>

冒泡排序

  冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。

  冒泡排序流程至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。

  由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。

[] arr={9,8,7,6,5,4,3,1"排序前数组为:"(+" "( i=0;i       ( j=0;j         (arr[j]>arr[j+1 temp==arr[j+1+1]="排序后的数组为:"(+" "

插入排序

  有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法--插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。

  插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。

  1、将指针指向某个元素,假设该元素左侧的元素全部有序,将该元素抽取出来,然后按照从右往左的顺序分别与其左边的元素比较,遇到比其大的元素便将元素右移,直到找到比该元素小的元素或者找到最左面发现其左侧的元素都比它大,停止;

  2、此时会出现一个空位,将该元素放入到空位中,此时该元素左侧的元素都比它小,右侧的元素都比它大;

  3、指针向后移动一位,重复上述过程。每操作一轮,左侧有序元素都增加一个,右侧无序元素都减少一个。

  需要两层循环,第一层循环index表示上述例子中的指针,即遍历从坐标为1开始的每一个元素;第二层循环从leftindex=index-1开始,leftindex--向左遍历,将每一个元素与i处的元素比较,直到j处的元素小于i出的元素或者leftindex<0;遍历从i到j的每一个元素使其右移,最后将index处的元素放到leftindex处的空位处。

</span><span style="color: #0000ff"&gt;public</span> InsertSort(<span style="color: #0000ff"&gt;int</span><span style="color: #000000"&gt;[] array){ </span><span style="color: #0000ff"&gt;this</span>.array =<span style="color: #000000"&gt; array; </span><span style="color: #0000ff"&gt;this</span>.length =<span style="color: #000000"&gt; array.length; } </span><span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; display(){ </span><span style="color: #0000ff"&gt;for</span>(<span style="color: #0000ff"&gt;int</span><span style="color: #000000"&gt; a: array){ System.out.print(a</span>+" "<span style="color: #000000"&gt;); } System.out.println(); } </span><span style="color: #008000"&gt;/**</span><span style="color: #008000"&gt; * 插入排序方法 </span><span style="color: #008000"&gt;*/</span> <span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; doInsertSort(){ </span><span style="color: #0000ff"&gt;for</span>(<span style="color: #0000ff"&gt;int</span> index = 1; index<length; index++){<span style="color: #008000"&gt;//</span><span style="color: #008000"&gt;外层向右的index,即作为比较对象的数据的index</span> <span style="color: #0000ff"&gt;int</span> temp = array[index];<span style="color: #008000"&gt;//</span><span style="color: #008000"&gt;用作比较的数据</span> <span style="color: #0000ff"&gt;int</span> leftindex = index-1<span style="color: #000000"&gt;; </span><span style="color: #0000ff"&gt;while</span>(leftindex>=0 &amp;&amp; array[leftindex]>temp){<span style="color: #008000"&gt;//</span><span style="color: #008000"&gt;当比到最左边或者遇到比temp小的数据时,结束循环</span> array[leftindex+1] =<span style="color: #000000"&gt; array[leftindex]; leftindex</span>--<span style="color: #000000"&gt;; } array[leftindex</span>+1] = temp;<span style="color: #008000"&gt;//</span><span style="color: #008000"&gt;把temp放到空位上</span>

<span style="color: #000000"> }
}

</span><span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;static</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; main(String[] args){
    </span><span style="color: #0000ff"&gt;int</span>[] array = {38,65,97,76,13,27,49<span style="color: #000000"&gt;};
    InsertSort is </span>= <span style="color: #0000ff"&gt;new</span><span style="color: #000000"&gt; InsertSort(array);
    System.out.println(</span>"排序前的数据为:"<span style="color: #000000"&gt;);
    is.display();
    is.doInsertSort();
    System.out.println(</span>"排序后的数据为:"<span style="color: #000000"&gt;);
    is.display();
}

}

选择排序

  选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

  1、从第一个元素开始,分别与后面的元素向比较,找到最小的元素与第一个元素交换位置;

  2、从第二个元素开始,分别与后面的元素相比较,找到剩余元素中最小的元素,与第二个元素交换;

  3、重复上述步骤,直到所有的元素都排成由小到大为止。

  需要两次循环,第一层循环i表示每轮指针指向的位置,将最小值min初始化为第i个元素,第二层循环从j=i+1开始,分别与min比较,如果小于min,则更新min的值,内层循环结束后;交换min元素和第i个元素的位置。以此类推进行下一轮循环,直到i=length时停止循环。当i=length时,说明小的元素已经全部移到了左面,因此无需进行内层循环了。

</span><span style="color: #0000ff"&gt;public</span> ChooseSort(<span style="color: #0000ff"&gt;int</span><span style="color: #000000"&gt;[] array){ </span><span style="color: #0000ff"&gt;this</span>.array =<span style="color: #000000"&gt; array; </span><span style="color: #0000ff"&gt;this</span>.length =<span style="color: #000000"&gt; array.length; } </span><span style="color: #008000"&gt;/**</span><span style="color: #008000"&gt; * 打印数组中的所有元素 </span><span style="color: #008000"&gt;*/</span> <span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; display(){ </span><span style="color: #0000ff"&gt;for</span>(<span style="color: #0000ff"&gt;int</span><span style="color: #000000"&gt; i: array){ System.out.print(i</span>+" "<span style="color: #000000"&gt;); } System.out.println(); } </span><span style="color: #008000"&gt;/**</span><span style="color: #008000"&gt; * 选择排序算法 </span><span style="color: #008000"&gt;*/</span> <span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; chooseSort(){ </span><span style="color: #0000ff"&gt;for</span>(<span style="color: #0000ff"&gt;int</span> i=0; i<length-1; i++<span style="color: #000000"&gt;){ </span><span style="color: #0000ff"&gt;int</span> minIndex =<span style="color: #000000"&gt; i; </span><span style="color: #0000ff"&gt;for</span>(<span style="color: #0000ff"&gt;int</span> j=minIndex+1;j<length;j++<span style="color: #000000"&gt;){ </span><span style="color: #0000ff"&gt;if</span>(array[j]<<span style="color: #000000"&gt;array[minIndex]){ minIndex </span>=<span style="color: #000000"&gt; j; } } </span><span style="color: #0000ff"&gt;int</span> temp =<span style="color: #000000"&gt; array[i]; array[i] </span>=<span style="color: #000000"&gt; array[minIndex]; array[minIndex] </span>=<span style="color: #000000"&gt; temp; } } </span><span style="color: #0000ff"&gt;public</span> <span style="color: #0000ff"&gt;static</span> <span style="color: #0000ff"&gt;void</span><span style="color: #000000"&gt; main(String[] args){ </span><span style="color: #0000ff"&gt;int</span>[] array={100,45,36,21,17,7<span style="color: #000000"&gt;}; ChooseSort cs </span>= <span style="color: #0000ff"&gt;new</span><span style="color: #000000"&gt; ChooseSort(array); System.out.println(</span>"排序前的数据为:"<span style="color: #000000"&gt;); cs.display(); cs.chooseSort(); System.out.println(</span>"排序后的数据为:"<span style="color: #000000"&gt;); cs.display(); }

} 

快速排序

设要排序的是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动  

注:在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的。要注意的是,排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。 

</td>
<td align="left" valign="top" width="78">
<div class="para">0

</td>
<td align="left" valign="top" width="78">
<div class="para">1

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">4

</td>
<td align="left" valign="top" width="78">
<div class="para">5

</td>

</tr>
<tr>
<td align="left" valign="top" width="78">
<div class="para">数据

</td>
<td align="left" valign="top" width="78">
<div class="para">6

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">7

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">8

</td>
<td align="left" valign="top" width="78">
<div class="para">9

</td>

</tr>

</td>
<td align="left" valign="top" width="78">
<div class="para">0

</td>
<td align="left" valign="top" width="78">
<div class="para">1

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">3</td>
<td align="left" valign="top" width="78">
<div class="para">4

</td>
<td align="left" valign="top" width="78">
<div class="para">5

</td>

</tr>
<tr>
<td align="left" valign="top" width="78">
<div class="para">数据

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">7

</td>
<td align="left" valign="top" width="78">
<div class="para">6

</td>
<td align="left" valign="top" width="78">
<div class="para">8

</td>
<td align="left" valign="top" width="78">
<div class="para">9

</td>

</tr>

</td>
<td align="left" valign="top" width="78">
<div class="para">0

</td>
<td align="left" valign="top" width="78">
<div class="para">1

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">4

</td>
<td align="left" valign="top" width="78">
<div class="para">5

</td>

</tr>
<tr>
<td align="left" valign="top" width="78">
<div class="para">数据

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">6

</td>
<td align="left" valign="top" width="78">
<div class="para">7

</td>
<td align="left" valign="top" width="78">
<div class="para">8

</td>
<td align="left" valign="top" width="78">
<div class="para">9

</td>

</tr>

</td>
<td align="left" valign="top" width="78">
<div class="para">0

</td>
<td align="left" valign="top" width="78">
<div class="para">1

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">4

</td>
<td align="left" valign="top" width="78">
<div class="para">5

</td>

</tr>
<tr>
<td align="left" valign="top" width="78">
<div class="para">数据

</td>
<td align="left" valign="top" width="78">
<div class="para">3

</td>
<td align="left" valign="top" width="78">
<div class="para">2

</td>
<td align="left" valign="top" width="78">
<div class="para">6

</td>
<td align="left" valign="top" width="78">
<div class="para">7

</td>
<td align="left" valign="top" width="78">
<div class="para">8

</td>
<td align="left" valign="top" width="78">
<div class="para">9

</td>

</tr>

<span style="color: #008000">/**<span style="color: #008000">

  • 划分、递归、快排

  • <span style="color: #808080">@author<span style="color: #008000"> bjh

  • <span style="color: #008000">*/
    <span style="color: #0000ff">public <span style="color: #0000ff">class<span style="color: #000000"> QuickSort {

    <span style="color: #008000">/*<span style="color: #008000">待排序、划分数组<span style="color: #008000">/
    <span style="color: #0000ff">private <span style="color: #0000ff">int<span style="color: #000000">[] array;
    <span style="color: #008000">/*<span style="color: #008000">数组长度<span style="color: #008000">/
    <span style="color: #0000ff">private <span style="color: #0000ff">int<span style="color: #000000"> length;

    <span style="color: #0000ff">public QuickSort(<span style="color: #0000ff">int<span style="color: #000000">[] array){
    <span style="color: #0000ff">this.array =<span style="color: #000000"> array;
    <span style="color: #0000ff">this.length =<span style="color: #000000"> array.length;
    }

    <span style="color: #008000">/**<span style="color: #008000">

    • 打印元素
      <span style="color: #008000">*/
      <span style="color: #0000ff">public <span style="color: #0000ff">void<span style="color: #000000"> printArray(){
      <span style="color: #0000ff">for(<span style="color: #0000ff">int i=0; i<length; i++<span style="color: #000000">){
      System.out.print(array[i]+" "<span style="color: #000000">);
      }
      System.out.println();
      }

    <span style="color: #008000">/**<span style="color: #008000">

    • 划分
    • <span style="color: #808080">@return<span style="color: #008000"> 划分的分界点
      <span style="color: #008000">*/
      <span style="color: #0000ff">public <span style="color: #0000ff">int partition(<span style="color: #0000ff">int left,<span style="color: #0000ff">int right,<span style="color: #0000ff">int<span style="color: #000000"> pivot){
      <span style="color: #008000">//<span style="color: #008000">左指针的起点,left-1是由于在后面的循环中,每循环一次左指针都要右移,
      <span style="color: #008000">//<span style="color: #008000">这样可以确保左指针从左边第一个元素开始,不然是从第二个开始
      <span style="color: #0000ff">int leftpoint = left-1<span style="color: #000000">;
      <span style="color: #008000">//<span style="color: #008000">右指针的起点,right+1是由于后面的循环中,每循环一次右指针都要左移,
      <span style="color: #008000">//<span style="color: #008000">这样可以确保右指针从最右边开始,不然是从倒数第二个开始
      <span style="color: #0000ff">int rightpoint = right+1<span style="color: #000000">;
      <span style="color: #0000ff">while(<span style="color: #0000ff">true<span style="color: #000000">){
      <span style="color: #008000">//<span style="color: #008000">找到左边大于pivot的数据,或者走到了最右边仍然没有找到比pivot大的数据
      <span style="color: #0000ff">while(leftpoint<right && array[++leftpoint]<<span style="color: #000000">pivot);
      <span style="color: #008000">//<span style="color: #008000">找到右边小于pivot的数据,或者走到了最左边仍然没有找到比pivot小的数据
      <span style="color: #0000ff">while(rightpoint>left && array[--rightpoint]><span style="color: #000000">pivot);
      <span style="color: #008000">//<span style="color: #008000">左指针和右指针重叠或相交
      <span style="color: #0000ff">if(leftpoint >=<span style="color: #000000"> rightpoint){
      <span style="color: #0000ff">break<span style="color: #000000">;
      }<span style="color: #0000ff">else<span style="color: #000000">{
      <span style="color: #008000">//<span style="color: #008000">交换左边大的和右边小的数据
      <span style="color: #000000"> swap(leftpoint,rightpoint);
      }
      }
      <span style="color: #008000">//<span style="color: #008000">返回分界点,即右边子数组中最左边的点
      <span style="color: #0000ff">return<span style="color: #000000"> leftpoint;
      }

    <span style="color: #008000">/**<span style="color: #008000">

    • 交换数据
      <span style="color: #008000">*/
      <span style="color: #0000ff">public <span style="color: #0000ff">void swap(<span style="color: #0000ff">int leftpoint,<span style="color: #0000ff">int<span style="color: #000000"> rightpoint){
      <span style="color: #0000ff">int temp =<span style="color: #000000"> array[leftpoint];
      array[leftpoint] =<span style="color: #000000"> array[rightpoint];
      array[rightpoint] =<span style="color: #000000"> temp;
      }

    <span style="color: #0000ff">public <span style="color: #0000ff">static <span style="color: #0000ff">void<span style="color: #000000"> main(String args[]){
    <span style="color: #0000ff">int[] array = {99,78,26,82,9,81,22,100,30,20,85<span style="color: #000000">};
    QuickSort qs = <span style="color: #0000ff">new<span style="color: #000000"> QuickSort(array);
    System.out.println("划分前的数据为:"<span style="color: #000000">);
    qs.printArray();
    <span style="color: #0000ff">int bound = qs.partition(0,array.length-1,50<span style="color: #000000">);
    System.out.println("划分后的数据为:"<span style="color: #000000">);
    qs.printArray();
    System.out.println("划分的分界点为:" + array[bound] + ",分界点的坐标为:" +<span style="color: #000000"> bound);
    }

}

二叉树遍历

树的特征和定义

树(Tree)是元素的集合。我们先以比较直观的方式介绍树。下面的数据结构是一个树:

树有多个节点(node),用以储存元素。某些节点之间存在一定的关系,用连线表示,连线称为边(edge)。边的上端节点称为父节点,下端称为子节点。树像是一个不断分叉的树根。

每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent)。比如说,3,5是6的子节点,6是3,5的父节点;1,7是3的子节点,3是1,7的父节点。树有一个没有父节点的节点,称为根节点(root),如图中的6。没有子节点的节点称为叶节点(leaf),比如图中的1,5节点。从图中还可以看到,上面的树总共有4个层次,6位于第一层,9位于第四层。树中节点的最大层次被称为深度。也就是说,该树的深度(depth)为4。

如果我们从节点3开始向下看,而忽略其它部分。那么我们看到的是一个以节点3为根节点的树:

三角形代表一棵树

再进一步,如果我们定义孤立的一个节点也是一棵树的话,原来的树就可以表示为根节点和子树(subtree)的关系:

上述观察实际上给了我们一种严格的定义树的方法:

1. 树是元素的集合。

2. 该集合可以为空。这时树中没有元素,我们称树为空树 (empty tree)。

3. 如果该集合不为空,那么该集合有一个根节点,以及0个或者多个子树。根节点与它的子树的根节点用一个边(edge)相连。

上面的第三点是以递归的方式来定义树,也就是在定义树的过程中使用了树自身(子树)。由于树的递归特征,许多树相关的操作也可以方便的使用递归实现。我们将在后面看到。

树的实现

树的示意图已经给出了树的一种内存实现方式: 每个节点储存元素和多个指向子节点的指针。然而,子节点数目是不确定的。一个父节点可能有大量的子节点,而另一个父节点可能只有一个子节点,而树的增删节点操作会让子节点的数目发生进一步的变化。这种不确定性就可能带来大量的内存相关操作,并且容易造成内存的浪费。

一种经典的实现方式如下:

树的内存实现

拥有同一父节点的两个节点互为兄弟节点(sibling)。上图的实现方式中,每个节点包含有一个指针指向第一个子节点,并有另一个指针指向它的下一个兄弟节点。这样,我们就可以用统一的、确定的结构来表示每个节点。

计算机的文件系统是树的结构,比如中所介绍的。在UNIX的文件系统中,每个文件(文件夹同样是一种文件),都可以看做是一个节点。非文件夹的文件被储存在叶节点。文件夹中有指向父节点和子节点的指针(在UNIX中,文件夹还包含一个指向自身的指针,这与我们上面见到的树有所区别)。在git中,也有类似的树状结构,用以表达整个文件系统的版本变化 (参考)。

 二叉树: 

二叉树是由n(n≥0)个结点组成的有限集合、每个结点最多有两个子树的有序树。它或者是空集,或者是由一个根和称为左、右子树的两个不相交的二叉树组成。

特点:

(1)二叉树是有序树,即使只有一个子树,也必须区分左、右子树;

(2)二叉树的每个结点的度不能大于2,只能取0、1、2三者之一;

(3)二叉树中所有结点的形态有5种:空结点、无左右子树的结点、只有左子树的结点、只有右子树的结点和具有左右子树的结点。

二叉树(binary)是一种特殊的树。二叉树的每个节点最多只能有2个子节点:

二叉树

由于二叉树的子节点数目确定,所以可以直接采用上图方式在内存中实现。每个节点有一个左子节点(left children)和右子节点(right children)。左子节点是左子树的根节点,右子节点是右子树的根节点。

如果我们给二叉树加一个额外的条件,就可以得到一种被称作二叉搜索树(binary search tree)的特殊二叉树。二叉搜索树要求:每个节点都不比它左子树的任意元素小,而且不比它的右子树的任意元素大。

(如果我们假设树中没有重复的元素,那么上述要求可以写成:每个节点比它左子树的任意节点大,而且比它右子树的任意节点小)

二叉搜索树,注意树中元素的大小

二叉搜索树可以方便的实现搜索算法。在搜索元素x的时候,我们可以将x和根节点比较:

1. 如果x等于根节点,那么找到x,停止搜索 (终止条件)

2. 如果x小于根节点,那么搜索左子树

3. 如果x大于根节点,那么搜索右子树

二叉搜索树所需要进行的操作次数最多与树的深度相等。n个节点的二叉搜索树的深度最多为n,最少为log(n)。

<h3 dir="ltr">二叉树的遍历
<blockquote dir="ltr">
<p dir="ltr">遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。


<blockquote dir="ltr">
<p dir="ltr">前序遍历:根节点->左子树->右子树


<p dir="ltr">中序遍历:左子树->根节点->右子树


<p dir="ltr">后序遍历:左子树->右子树->根节点

 二叉树的类型

——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越