五.二叉树

本文深入探讨了二叉树的基本概念,包括分类、术语和性质,并详细介绍了二叉查找树(BST)、二叉堆以及它们的操作如查找、插入和删除。特别地,讲解了前序、中序、后序和层序遍历的递归与非递归实现。此外,还提到了AVL树和红黑树作为平衡二叉查找树的例子,以及二叉堆在优先级队列中的应用。

一种数据结构:定义,构建,操作

我们就希望一种数据结构能同时具备数组查找快的优点以及链表插入和删除快的优点,于是 树 诞生了。

一.基本概念:

  分类:普通二叉树,满二叉树,完全二叉树。二叉查找树(平衡二叉查找树(AVL树),红黑树)。二叉堆。优先队列

      平衡二叉树和平衡二叉查找树得区分一下

1.定义:按定义可分为:空树和非空树

2.术语:根节点,子节点,叶节点,兄弟节点。节点的深度,高度,层数。树的高度 

  层次,高度,树的深度

  树的高度:是二叉树中节点的最大层次数。层数=高度,深度=高度-1

3.性质:已知层数(高度)算节点个数,已知节点个数算层数(高度):总共有l层,则共有n=2l-1个节点;总共有n个节点,则共有l=log2n+1层

      (1)总共 层的二叉树,最多有2i-1个节点(高度为h的二叉树中,最多有2h-1个节点)

      (2)二叉树第i层最多有2i-1个节点

      (3)一棵具有n个节点的完全二叉树,高度为h=log2n+1  (log2n向下取整)

      也就是说:有n个节点的满二叉树或者完全二叉树,其高度或者层数位h=log2n+1

    (4)父节点序号为p,则左子节点:l=2*p+1,右子节点r=2*p+2。子节点序号为i,父节点:(i-1)/2

4.二叉树一般用于实现二叉查找树和二叉堆。所以一般用二叉查找树(BST)或二叉堆

  二叉查找树是二叉树中最常用的一种类型。顾名思义,二叉查找树是为了实现快速查找而生的。不过,它不仅仅支持快速查找一个数据,还支持快速插入、删除一个数据。

5.操作:查找节点,插入节点,删除节点。遍历

4.效率

遍历可能不如其他操作快,但是在大型数据库中,遍历是很少使用的操作,它更常用于程序中的辅助算法来解析算术或其它表达式。

二.二叉树的遍历

分类:深度优先遍历:前序,中序,后续。广度优先遍历:层序遍历

1.递归遍历:前序,中序,后序

 1         //前序遍历
 2     public static void preOrder(TreeNode root) {
 3         if(root==null)
 4             return;
 5         System.out.println(root.data);
 6         preOrder(root.leftChild);
 7         preOrder(root.rightChild);
 8     }
 9     
10     //中序遍历
11     public static void inOrder(TreeNode root) {
12         if(root==null)
13             return;
14         inOrder(root.leftChild);
15         System.out.println(root.data);
16         inOrder(root.rightChild);
17     }
18     
19     //后序遍历
20     public static void postOrder(TreeNode root) {
21         if(root==null)
22             return;
23         postOrder(root.leftChild);
24         postOrder(root.rightChild);
25         System.out.println(root.data);
26     }       
View Code

2.非递归遍历

 1     //前序
 2     public static void preOrderTraversalWithStack(TreeNode root) {
 3         Stack<TreeNode> stack=new Stack<>();
 4         TreeNode current=root;
 5         while(current!=null || !stack.isEmpty()) {
 6             while(current!=null) {
 7                 System.out.println(current.data);
 8                 stack.push(current);
 9                 current=current.leftChild;
10             }
11             
12             if(!stack.isEmpty()) {
13                 current=stack.pop();
14                 current=current.rightChild;
15             }
16         }
17     }
18     
19     //中序
20     public static void inOrder(TreeNode root) {
21         Stack<TreeNode> stack=new Stack<>();
22         TreeNode current=root;
23         while(current!=null || !stack.isEmpty()) {    // ||,不是&&
24             while(current!=null) {
25                 stack.push(current);
26                 current=current.leftChild;
27             }
28             
29             if(!stack.isEmpty()) {
30                 current=stack.pop();
31                 System.out.println(current.data);
32                 current=current.rightChild;
33             }    
34         }
35     }
36     
37     //后序
38     public static void postOrder(TreeNode root){
39         Stack<TreeNode> stack  = new Stack<TreeNode>();
40         TreeNode current = root;
41         TreeNode lastVisit = root;        //记录最后一个访问的节点,用于检验是不是根节点的右节点
42         while(current != null || !stack.empty()){
43             while(current != null){
44                 stack.push(current);
45                 current=current.leftChild;
46             }
47 
48             current = stack.peek();
49             //两种情况:1.右子节点为空,直接访问 2.右子节点不为空,但已经访问过了,直接访问
50             if(current.rightChild == null || current.rightChild== lastVisit){       //current.rightChild== lastVisit
51                 System.out.println(current.data);                                    //此时说明:右子树已经访问完毕
52                 stack.pop();
53                 lastVisit=current;
54                 current = null;
55             }else {
56                 current=current.rightChild;
57             }
58         }
59     }
View Code

3.层序遍历

 1     public static void levelOrder(TreeNode root) {
 2         LinkedList<TreeNode> queue=new LinkedList<>();
 3         queue.addLast(root);
 4         while(!queue.isEmpty()) {
 5             TreeNode node=queue.removeFirst();
 6             System.out.println(node.data);
 7             if(node.leftChild!=null)
 8                 queue.addLast(node.leftChild);
 9             if(node.rightChild!=null)
10                 queue.addLast(node.rightChild);
11         }
12     }
View Code

三.二叉查找树(BST)

1.定义

2.BST的删除操作比价复杂

3.操作:查找,插入简单,删除比较复杂

(1)查插

 1     //查找
 2     public Node search(int data) {
 3         Node current=root;
 4         while(current != null) {
 5             if(data==current.data)
 6                 return current;
 7             else if(data<current.data)
 8                 current=current.left;
 9             else 
10                 current=current.right;
11         }
12         return null;    
13     }
14     
15     //插入
16     public boolean insert(int data) {
17         Node node=new Node(data);
18         if(root==null) {            //要区分:空树 和 非空树
19             root=node;
20             return true;
21         }
22         
23         Node current=root;
24         while(current != null) {
25             if(data==current.data)
26                 return false;
27             else if(data<current.data) {
28                 if(current.left==null) {
29                     current.left=node;
30                     return true;                //插完直接返回
31                 }
32                 current=current.left;
33             }
34             else {
35                 if(current.right==null) {
36                     current.right=node;
37                     return true;
38                 }
39                 current=current.right;
40             }
41         }
42         return true;                            //恒返回 true
43     }
View Code

(2)删除

3.AVL树:平衡二叉查找树

(1)AVL树是特殊的平衡二叉树,也是特殊的二叉查找树。它是平衡二叉树和二叉查找树的结合

(1)定义:左右子树的高度差不超过1

平衡二叉查找树又称AVL树,它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1

AVL树是最先发明的自平衡二叉查找树算法。AVL树算法的发明是为了平衡二叉查找树

4.红黑树

(1)红黑树是平衡二叉树的一种。平衡二叉树追求完全平衡,条件比价苛刻。红黑树放弃了追求完全平衡,追求大致平衡

   红黑树是一种不严格的平衡二叉树

三.二叉堆

(1)定义:二叉堆本质是一个完全二叉树。分为:最大堆,最小堆

(2)用途:实现优先级队列堆排序

(2)操作:1.插入节点  2.删除节点  3.构建二叉堆

    这几种操作都依赖于二叉堆的自我调整:“上浮”调整,“下沉”调整

    掌握:“上浮”调整,“下沉”调整,构建堆

(3)二叉堆基于数组实现:父节点索引parent,则左子节点:2*parent+1,右子节点:2*parent+2

(4)“上浮”,“下沉”代码

 1 package com.midiyu.tree;
 2 
 3 import java.util.Arrays;
 4 
 5 public class BinaryHeap {
 6     //“上浮”调整
 7     public static void upAdjust(int[] array) {
 8         int childIndex=array.length-1;
 9         int parentIndex=(childIndex-1)/2;
10         int temp=array[childIndex];
11 
12         while(childIndex>0 && temp<array[parentIndex]) {
13             array[childIndex]=array[parentIndex];
14             childIndex=parentIndex;
15             parentIndex=(parentIndex-1)/2;
16         }
17         array[childIndex]=temp;
18     }
19 
20     //“下沉”调整
21     public static void downAdjust(int[] array,int parentIndex) {
22         int childIndex=2*parentIndex+1;
23         int temp=array[parentIndex];
24         while(childIndex<array.length) {
25             if(childIndex+1<array.length && array[childIndex+1]<array[childIndex])
26                 childIndex++;
27             if(temp<=array[childIndex])
28                 break;
29             array[parentIndex]=array[childIndex];
30             parentIndex=childIndex;
31             childIndex=2*childIndex+1;
32         }
33         array[parentIndex]=temp;
34     }
35 
36     //构建堆
37     public static void buildHeap(int[] array) {
38         for(int i=(array.length-2)/2;i>=0;i--)    //最后一个非叶子节点的索引:(最后一个叶子节点索引-1)/2
39             downAdjust(array, i);
40     }
41 
42     public static void main(String[] args) {
43         int[] array = new int[] {1,3,2,6,5,7,8,9,10,0};
44         upAdjust(array);
45         System.out.println(Arrays.toString(array));
46 
47         array = new int[] {7,1,3,10,5,2,8,9,6};
48         buildHeap(array);
49         System.out.println(Arrays.toString(array));
50     }
51 
52 }
View Code

四.优先级队列

二叉堆是实现堆排序优先级队列的基础

1.还是在队尾入队,队头出队。但先进不一定先出,而是根据优先级出队

  最小优先级队列:当前最小元素优先出队

  最大优先级队列:当前最大元素优先出队

2.实现:用最大堆来实现最大优先级队列。每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点

    入队:“上浮”,出队:“下沉”

3.实现代码

 1 package com.midiyu.tree;
 2 
 3 public class PriorithQueue {
 4     int[] array;
 5     int size;
 6     
 7     public PriorithQueue() {
 8         this.array=new int[32];
 9     }
10     
11     //入队
12     public void enQueue(int key) {
13 //        if(size>=array.length)
14 //            resize();
15         array[size++]=key;
16         upAdjust();
17     }
18     
19     //出队
20     public int deQueue() throws Exception {
21         if(size<=0)
22             throw new Exception("队已经空了");
23         int head=array[0];
24         array[0]=array[--size];
25         downAdjust();
26         return head;
27     }
28     
29     //“上浮”调整
30     public void upAdjust() {
31         int childIndex=size-1;
32         int parentIndex=(childIndex-1)/2;
33         int temp=array[childIndex];
34         while(childIndex>0 && array[childIndex]>array[parentIndex]) {
35             array[childIndex]=array[parentIndex];
36             childIndex=parentIndex;
37             parentIndex=(parentIndex-1)/2;
38         }
39         array[childIndex]=temp;
40     }
41     
42     //“下沉”调整
43     public void downAdjust() {
44         int parentIndex=0;
45         int childIndex=0;
46         int temp=array[parentIndex];
47         while(childIndex<size) {
48             if(childIndex+1<size && array[childIndex+1]>array[childIndex])
49                 childIndex++;
50             if(temp>=array[childIndex])
51                 break;
52             array[parentIndex]=array[childIndex];
53             parentIndex=childIndex;
54             childIndex=2*childIndex+1;
55         }
56         array[parentIndex]=temp;
57     }
58     
59     
60     
61     public static void main(String[] args) throws Exception {
62         PriorithQueue priorithQueue=new PriorithQueue();
63         priorithQueue.enQueue(3);
64         priorithQueue.enQueue(5);
65         priorithQueue.enQueue(10);
66         priorithQueue.enQueue(2);
67         priorithQueue.enQueue(7);
68         System.out.println("出队元素:"+priorithQueue.deQueue());
69         System.out.println("出队元素:"+priorithQueue.deQueue());
70     }
71 
72 }
View Code
内容概要:本文系统阐述了企业新闻发稿在生成式引擎优化(GEO)时代下的全渠道策略与效果评估体系,涵盖当前企业传播面临的预算、资源、内容与效果评估四大挑战,并深入分析2025年新闻发稿行业大趋势,包括AI驱动的智能化转型、精准化传播、首发内容价值提升、内容资产化及数据可视化。文章重点解析央媒、地方官媒、综合门户和自媒体四类媒体资源的特性、传播优势与发稿策略,提出基于内容适配性、时间节奏、话题设计的策略制定方法,并构建涵盖品牌价值、销售转化与GEO优化的多维评估框架。此外,结合“传声港”工具实操指南,提供AI智能投放、效果监测、自媒体管理与舆情应对的全流程解决方案,并针对科技、消费、B2B、区域品牌四大行业推出定制化发稿方案。; 适合人群:企业市场/公关负责人、品牌传播管理者、数字营销从业者及中小企业决策者,具备一定媒体传播经验并希望提升发稿效率与ROI的专业人士。; 使用场景及目标:①制定科学的新闻发稿策略,实现从“流量思维”向“价值思维”转型;②构建央媒定调、门户扩散、自媒体互动的立体化传播矩阵;③利用AI工具实现精准投放与GEO优化,提升品牌在AI搜索中的权威性与可见性;④通过数据驱动评估体系量化品牌影响力与销售转化效果。; 阅读建议:建议结合文中提供的实操清单、案例分析与工具指南进行系统学习,重点关注媒体适配性策略与GEO评估指标,在实际发稿中分阶段试点“AI+全渠道”组合策略,并定期复盘优化,以实现品牌传播的长期复利效应。
以下是使用C语言在Windows XP/Win7系统下,借助VC++ 6.0或C-free开发环境,实现二叉树先序构造及先序/中序/后序遍历、计算树深度、统计结点/叶结点/度为1结点个数的代码: ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树节点结构 typedef struct BiTNode { char data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; // 先序构造二叉树 BiTree CreateBiTree() { char ch; scanf("%c", &ch); if (ch == '#') { return NULL; } else { BiTree T = (BiTree)malloc(sizeof(BiTNode)); T->data = ch; T->lchild = CreateBiTree(); T->rchild = CreateBiTree(); return T; } } // 先序遍历 void PreOrderTraverse(BiTree T) { if (T) { printf("%c ", T->data); PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } } // 中序遍历 void InOrderTraverse(BiTree T) { if (T) { InOrderTraverse(T->lchild); printf("%c ", T->data); InOrderTraverse(T->rchild); } } // 后序遍历 void PostOrderTraverse(BiTree T) { if (T) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); printf("%c ", T->data); } } // 计算树的深度 int TreeDepth(BiTree T) { if (!T) { return 0; } else { int leftDepth = TreeDepth(T->lchild); int rightDepth = TreeDepth(T->rchild); return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1; } } // 统计结点个数 int NodeCount(BiTree T) { if (!T) { return 0; } else { return NodeCount(T->lchild) + NodeCount(T->rchild) + 1; } } // 统计叶结点个数 int LeafCount(BiTree T) { if (!T) { return 0; } if (!T->lchild && !T->rchild) { return 1; } else { return LeafCount(T->lchild) + LeafCount(T->rchild); } } // 统计度为1的结点个数 int DegreeOneCount(BiTree T) { if (!T) { return 0; } if ((T->lchild &&!T->rchild) || (!T->lchild && T->rchild)) { return DegreeOneCount(T->lchild) + DegreeOneCount(T->rchild) + 1; } else { return DegreeOneCount(T->lchild) + DegreeOneCount(T->rchild); } } int main() { printf("请输入先序序列(空节点用#表示):\n"); BiTree T = CreateBiTree(); printf("先序遍历结果:"); PreOrderTraverse(T); printf("\n"); printf("中序遍历结果:"); InOrderTraverse(T); printf("\n"); printf("后序遍历结果:"); PostOrderTraverse(T); printf("\n"); printf("树的深度:%d\n", TreeDepth(T)); printf("结点个数:%d\n", NodeCount(T)); printf("叶结点个数:%d\n", LeafCount(T)); printf("度为1的结点个数:%d\n", DegreeOneCount(T)); return 0; } ``` ### 代码说明: 1. **二叉树节点结构**:定义了二叉树节点的结构体,包含数据域和左右子树指针。 2. **先序构造二叉树**:通过递归的方式,根据输入的先序序列构造二叉树,空节点用 `#` 表示。 3. **遍历函数**:实现了先序、中序和后序遍历,通过递归访问节点。 4. **计算树的深度**:递归计算左右子树的深度,取最大值加1。 5. **统计结点个数**:递归统计左右子树的节点数并加1。 6. **统计叶结点个数**:递归统计左右子树的叶节点数。 7. **统计度为1的结点个数**:递归统计左右子树中度为1的节点数。 ### 编译和运行: 将上述代码保存为 `.c` 文件,在VC++ 6.0或C-free中打开并编译运行。运行时,按照提示输入先序序列,程序将输出各种遍历结果和统计信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值