2020-05-04

定义:树是**n(n>=0)**个结点的有限集。n=0时称为空树。
注意
1.n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点。
2.m>0时,子树的个数没有限制,但它们一定是互不相交的。
非空:
(1)有且仅有一个特定的称为根(Root)的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、…、Tn,其中每一个集合本身又是一棵树,并且称为根的子树。
概念:
结点的层次从根开始定义起,根为第一层,根的孩子为第二层。若某结点在第n层,则其子树的根就在第n+1层。其双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度或高度。如果将树种结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。森林是m(m>=0)课互不相交的树的集合。
结点之间的关系:
结点的子树的跟称为该结点的孩子,相应地,该结点称为孩子的双亲。同一个双亲的孩子之间互称兄弟,结点的祖先是从根到该结点所经分支上的所有结点。

基本操作

  1. 查找类
  2. 插入类
  3. 删除类
    查找类
    Root(T) 求树的根结点
    Value(T,cur_e) 当前结点的元素值
    Parent(T,cur_e) 求当前结点的双亲结点
    LeftChild(T,cur_e) 求当前结点的最左孩子
    RightSibling(T,cur_e) 求当前结点的右兄弟
    TreeEmpty(T) 判断树是否为空树
    TreeDepth(T) 求树的深度
    TraverseTree(T,Visit()) 遍历
    插入类
    InitTree(&T) 初始化置空树
    CreateTree(&T,definition) 按定义构造树
    Assign(T,cur_e,vaule) 给当前结点赋值
    InsertChild(&T,&p,i,c) 将以c为根的树插入为结点p的第i个子树
    删除类
    ChearTree(&T)将树清空
    DestroyTree(&T)销毁树的结构
    DeleteTree(&T,&p,i)删除结点p的第i个子树

二叉树

定义:
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。(在某个阶段都是两种结果的情形)
性质
1.性质1:在二叉树的第i层上至多有2∧i-1个结点(i>=1)。
2.性质2:深度为k的二叉树至多有2∧k -1个结点(k>=1)。
3.性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
4.性质4:具有n个结点的完全二叉树的深度为[log2n]+1 ([x]表示不大于x的最大整数。
5.性质5:若对一棵有n个结点的完全二叉树(其深度为[log2n]+1) 的结点按层序编号(从第1层到[log2n]+1层,每层从左到右),对任一节点i(1≦i≦n)有:
若i=1,则结点i是二叉树的根,无双亲;
若i>1, 则其双亲是结点[i/2]。
若2i>n, 则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i。
若2i+1>n, 则结点i无右孩子;否则其右孩子是结点2i+1。
特点:
每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。左子树和右子树是有顺序的,次序不能任意颠倒。即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
存储结构
1.二叉树的顺序存储结构:二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现结点之间的逻辑关系。*顺序存储结构一般只用于完全二叉树。2.二叉链表(链式存储结构)二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。
**二叉树的遍历:**是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点呗访问一次且仅被访问一次。
1.前序遍历:规则是若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
2.中序遍历:规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
3.后序遍历:规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。

前序遍历算法:
/二叉树的前序遍历递归算法/
void PreOrder(BiTree T)
{
if(T){
visit(T>=data)
访问结点
PreOrder(T->lchild);
再先序遍历左子树
PreOrder(T->rchild);
最后先序遍历右子树


中序与后序类似

==二叉树的建立:建立二叉树,也是利用了递归的原理。只不过在原来应该是打印结点的地方,改成了生成结点,给结点赋值的操作而已。
void CreateBiTree(BiTree *T)
{
TElemTypech;scanf("%c",&ch);if(ch
’#’)*T=NULL;
else{*T(BiTree)malloc(sizeof(BiTNode));
if(!*T){
exit(OVERFLOW);
(*T)->data=ch; 生成根结点CreateBiTree(&(*T)->lchild); 构造左子树CreateBiTree(&(*T)->rchild); 构造右子树}
}
线索二叉树
对于一个有n个结点的儿茶链表,每个结点有指向左右孩子的两个指针域,所以一共是2n个指针域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n-1(n-1)=n+1个空指针域。
线索二叉树:指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。
线索二叉树,等于是把一棵二叉树转变成了一个双向链表。
对二叉树以某种次序遍历使其变为线索二叉树的过程称作是线索化。
森林转换为二叉树
1.把每个树转换为二叉树。
2.第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。
二叉树转换为树
1.加线。若某结点的右孩子存在,则将做左孩子的n各右孩子结点都作为此结点的孩子。将该结点与这些右孩子结点用线连接起来。
2.去线。删除原二叉树中所有结点与其右孩子结点的连线。
3.层次调整。使之结构层次分明。
树与森林的遍历
树的遍历分为两种方式
1.一种是先根遍历树,即先访问树的根结点,然后依次先根遍历根的每棵子树。
2.另一种是后跟遍历,即先依次后根遍历每棵子树,然后再访问根结点。
森林的遍历也分为两种方式:
1.前序遍历:先访问森林中第一棵树的根结点,然后再依次县根遍历根的每棵子树,再依次用同样方式遍历除去第一棵树的剩余树构成的森林。
2.后序遍历:是先访问森林中第一棵树,后跟遍历的方式遍历每棵子树,然后再访问根结点,再依次同样方式遍历除去第一棵树的剩余树构成的森林。

赫夫曼树及其应用

1、路径和路径长度  
在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度  
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度 
 树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。其中带权路径长度WPL最小的二叉树称作赫夫曼树。

好的,以下是构造程序的代码: ```python class AccountBook: def __init__(self): self.records = [] self.income = 0 self.expense = 0 self.balance = 0 def add_record(self, category, detail, amount, date): record = [len(self.records)+1, category, detail, amount, date] self.records.append(record) if category == '收入': self.income += amount else: self.expense += amount self.balance = self.income - self.expense def print_records(self): for record in self.records: print(record) print('总收入:', self.income) print('总支出:', self.expense) print('结余:', self.balance) book = AccountBook() book.add_record('收入', '生活费', 3000, '2020-04-01') book.add_record('支出', '午餐', 12.5, '2020-04-30') book.add_record('支出', '买书', 25, '2020-04-30') book.add_record('支出', '晚餐', 16, '2020-04-30') book.add_record('收入', '生活费', 3500, '2020-05-01') book.add_record('支出', '峨嵋山旅游', 500, '2020-05-01') book.print_records() ``` 运行程序后,输出结果如下: ``` [1, '收入', '生活费', 3000, '2020-04-01'] [2, '支出', '午餐', 12.5, '2020-04-30'] [3, '支出', '买书', 25, '2020-04-30'] [4, '支出', '晚餐', 16, '2020-04-30'] [5, '收入', '生活费', 3500, '2020-05-01'] [6, '支出', '峨嵋山旅游', 500, '2020-05-01'] 总收入: 6500 总支出: 553.5 结余: 5946.5 ``` 上述程序中,我们定义了一个 `AccountBook` 类,其中包含了一个 `records` 属性,用于存储记帐记录;以及 `income`、`expense` 和 `balance` 属性,分别用于记录总收入、总支出和结余。 在 `AccountBook` 类中,我们还定义了一个 `add_record` 方法,用于添加新的记帐记录。这个方法接受四个参数:`category`(类别)、`detail`(明细)、`amount`(金额)和 `date`(日期)。在添加新的记帐记录时,我们先将其存储到 `records` 属性中,然后根据类别更新总收入和总支出,最后计算结余。 最后,我们还定义了一个 `print_records` 方法,用于将所有的记帐记录打印出来,并输出总收入、总支出和结余。 在程序的最后,我们创建一个 `AccountBook` 对象,并添加了一些测试数据。最后调用 `print_records` 方法,将所有的记帐记录打印出来,并输出总收入、总支出和结余。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值