二叉树和堆排序

二叉树

满二叉树

国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,第i层上的结点数为:2(i-1),且结点总数是(2k) -1 ,则它就是满二叉树。

国内二叉树

国外(国际)定义:如果一棵二叉树的结点要么是叶子结点,要么它有两个孩子结点,这样的树就是满二叉树。

国外二叉树(但是不符合国内的定义)
完全二叉树

判断完全二叉树
完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树(我的理解是完全二叉树:就是满二叉树去掉最下层最右边的一些节点)

完全二叉树定义
完全二叉树(Complete Binary Tree)
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
完全二叉树是由【满二叉树】而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
一棵二叉树至多只有最下面的一层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树。

二叉排序树

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树
定义:
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
查找:


若根结点的关键字值等于查找的关键字,成功。
否则,若小于根结点的关键字值,递归查左子树。
若大于根结点的关键字值,递归查右子树。
若子树为空,查找不成功。

完全二叉树的存储

1.链表
链表中每一个节点,包含两个指针leftchild,rightchild,分别指向左右子树
2.数组
利用平衡二叉树平衡的特性,使用数组来存储完全二叉树,a[n]的左右子节点分别为a[2n+1],a[2n+2]

  • [1,2,3,4,5] ➔a[0] ,左:a[1] 右:a[2]
  • a[1],左:a[3] 右:a[4]

数组的下标是从0开始的,二叉树是从1开始的。

二叉排序树插入
  1. 将待插入节点添加到数组的最后,即平衡二叉树的最后一个叶子节点。
  2. 从最后一个子树的根节点a(n/2)开始,调整所有子树,使其保持大顶堆特性(a[n] >= a[2n+1], a[n] >= a[2n+2])
    (这是从下往上调整,堆排序是从上往下调整)

堆排序

定义

实质上是满足如下性质的完全二叉树:
一个数组a[N]共N个元素, 设2k+1 < N(K为所有的根节点),如果a[k] >= a[2k + 1] 且a[k] >= a[2k+2],则该堆为大顶堆。

实现思路
  1. 将a[0 - n-1]建立为一个大顶堆,此时a[0]为数组里的最大值(共有n个元素)
  2. 将首尾元素a[0]与a[n-1]交换,这样a[n-1]为堆a[0 – n-1]的最大值,同时a[0-(n-2)]为无序树
  3. 调整a[0 – (n-2)]为大顶堆,再次交换首尾元素
  4. ...重复步骤3直到最后一个元素,得到一个升序数组a[0 - n-1]
建堆
  1. 一个有n个节点的完全二叉树,有n/2个父节点。
    从最后一个父节点a[n/2-1]开始,将每一个以该父节点为顶点的树调整为大顶堆(从下往上依次调整)。

调整过程,因为被调整树除了根节点(即a[0])外,其余父节点均大于子节点(因为建堆的时候,就是建的一个大顶堆),所以可以采取向下调整一次即可(不用从下往上调整,因为第二次调整的时候,第二层就存在最大值了)。

排序
  1. 将当前大顶堆,最大元素根节点与最后一个元素交换
  2. 交换后,最大的元素在数组的最后,同时调整前面的n-1个元素为大根堆
  3. 重复步骤2,直到最后一个元素,此时,数组为升序数组。

下面是一个数组的排序的实现过程:

数组堆排序实现过程

需要注意的是:
第一步:图中树1,是在交换了7和5过后,所以要调整子树,但是子树是节点5,所以不需要调整。
第二步:图中树2,是在交换了7和3过后,所以也要调整子树,这时子树的父节点是3,左右子节点分别是6和5,所以必须要调整成树3过后,才算建堆完成。所以说每当子节点和父节点产生了交换过后,都必须要调整其下面的子树。

总结

  1. 建堆的时候,从最后一个父节点向上调整到根节点,但是调整是往下调整的,而且每当子节点和父节点需要交换的时候,交换过后,还要从这个父节点往下调整这颗子树,如图中的树1到树3的过程。

  2. 排序时调整的时候,从根节点向下调整到最后一个需要调整的根节点。这里的调整和建堆时候的调整一样,也是往下调整。

下面是相关堆排序的代码,其实只要直到了原理,写代码就很简单了

/*
 //向下调整
 a:需要调整的数组
 head:开始调整的位置
 length:数组长度
 */
+ (void)ajustHeapWith:(int *)a head:(int)head length:(uint)length
{
    int leftChild = 2 * head + 1;
    int switchChild = leftChild; //交换的子节点
    if (leftChild >= length) {   //当只剩一个元素的时候,满足这个条件,也就是递归的出口了
        return;
    }
    int rightChild = leftChild + 1;
    if (rightChild < length) {
        //如果右子节点存在,且大于左子节点
        if (a[rightChild] > a[leftChild]) {
            switchChild = rightChild;  //就让交换的子节点等于右子节点
        }
    }
    //判断是否要交换
    if (a[switchChild] > a[head]) {
        //交换
        [self swap:&a[switchChild] with:&a[head]];
        //调整一下被交换过的子树
        [self ajustHeapWith:a head:switchChild length:length];
    }
}

//建堆
+ (void)buidHeapWith:(int *)a length:(uint)length
{
    for (int i = length/2 -1; i >= 0; i--) {
        [self ajustHeapWith:a head:i length:length];  //从下往上调整,但是调整是向下调整的
    }

}

+ (void)sortCArray:(int *)a length:(uint)length
{
    //1.建堆
    [self buidHeapWith:a length:length];
    //2.排序
    for (int i = length - 1; i > 0; i--) {
        // 1.交换a[0]与a[i]
        [self swap:&a[0] with:&a[i]];
        // 2.调整堆(移除了最后一个元素,即最大值,所以长度减1.然后从a[0]往下开始调整)
        [self ajustHeapWith:a head:0 length:i];
    }
}
+ (void)testHeapSort
{
    int maxCount = 1000;
    int *a = [self unsortedCArrayWithLenght:maxCount];
    [self logArray:a length:maxCount];
    [self sortCArray:a length:maxCount];
    [self checkAsendingSortArray:a length:maxCount];
    [self logArray:a length:maxCount];
    
    free(a);
}
+ (BOOL)checkAsendingSortArray:(int [])a length:(int)length
{
    for (int i = 0; i < length - 1; i++) {
        if (a[i] > a[i+1]) {
            NSLog(@"位置:%d,%d; 位置:%d,%d",i,a[i],i+1,a[i+1]);
            NSLog(@"非升序数组!!");
            return false;
        }
    }
    NSLog(@"该数组是升序数组!!");
    return true;
}

+ (void)logArray:(int[])a length:(int)length
{
    for (int i = 0; i < length; i++) {
        NSLog(@"%d",a[i]);
    }
}

+ (int *)unsortedCArrayWithLenght:(NSUInteger)length
{
    int *a = (int *) malloc(sizeof(int) * length);
    for (int i = 0; i < length; i++) {
        a[i] = rand()%length;
    }
    return a;
}


+ (void)swap:(int *)a with:(int *)b
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

二叉树遍历

所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。

  • 中序遍历
    LNR:中序遍历(Inorder Traversal)
    ——访问根结点的操作发生在遍历其左右子树之中(间)。
    即中根遍历,左中右

  • 前序遍历/先序遍历
    NLR:前序遍历(Preorder Traversal 亦称(先序遍历))
    ——访问根结点的操作发生在遍历其左右子树之前。
    根在最前面,根左右

  • 后序遍历
    LRN:后序遍历(Postorder Traversal)
    ——访问根结点的操作发生在遍历其左右子树之后。
    根在最后面,左右根


如上图中遍历结果为:

  • 中序遍历 (对于每一个父节点和左右子节点来说都是 “左父右”)
    GDH B E A K C IJ F
  • 前序遍历/先序遍历 (先是根节点和左边的依次所有左子节点,然后从下往上依次所有的右子节点,最后再是根节点右边的子节点,依次往下,如果有子的左节点,就依次往下下去,直到没有了,再从右子节点开始)
    ABDGHECKFIJ (左子节点优先)
  • 后序遍历(写的时候从右往左开始写,先是根节点,然后是右边的子节点,如果右边的子节点还有下一层右边的子节点,就依次往下,直到没有了右边的子节点,再依次往上找左边的子节点,然后才是根节点的左边的子节点,如果有右子节点就右子节点优先)
    GHDEBKJIFCA (右子节点优先)

实例题:

  • 求后序遍历是多少
    前序遍历:GDAFEMHZ
    中序遍历:ADEFGHMZ

结合上面两个遍历,前序遍历确定根节点,中序遍历确定左右子节点,从而画出二叉树图,再写出后序遍历。

如上图,后序遍历为:AEFDHZMG

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,373评论 1 31
  • 数据结构和算法--二叉树的实现 几种二叉树 1、二叉树 和普通的树相比,二叉树有如下特点: 每个结点最多只有两棵子...
    sunhaiyu阅读 6,330评论 0 14
  • B树的定义 一棵m阶的B树满足下列条件: 树中每个结点至多有m个孩子。 除根结点和叶子结点外,其它每个结点至少有m...
    文档随手记阅读 13,005评论 0 25
  • 四、树与二叉树 1. 二叉树的顺序存储结构 二叉树的顺序存储就是用数组存储二叉树。二叉树的每个结点在顺序存储中都有...
    MinoyJet阅读 1,449评论 0 7
  • 一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要使用到,那...
    24K男阅读 6,653评论 5 14