数据结构&算法-4.非线性结构(黑马学习笔记)

 

4.1树的基本概念

树的定义:

        由一个或多个(n≥0)结点组成的有限集合T,有且仅有一个结点称为跟(root),当n>1是,其余的结点分为m(m≥0)个互不相交的有限集合T1,T2,...Tm.每个集合本身又是棵树,被称作这个根的子树。

树的结构特点:

  • 非线性结构,由一个直接前驱,但可能有多个直接后继(1:n)

  • 树的定义具有递归性,树中还有树

  • 树可以为空,即节点个数为0

若干术语:

  • 根:根结点(没有前驱)

  • 叶子:终端结点(没有后继)

  • 森林:指m棵不相交的树的集合

  • 有序树:结点各子树从左至右有序,不能互换(左为第一)

  • 无序树:结点各子树可互换位置

  • 双亲:上层的那个结点(直接前驱)parent

  • 孩子:下层结点的子树(直接后继)child

  • 兄弟:同一双亲下的同层结点(孩子之间互称为兄弟)sibling

  • 堂兄弟:双亲位于同一层的结点(但并非同一双亲)cousin

  • 祖先:从根到该结点所经分支的所有结点

  • 子孙:该结点下层子树中的任一结点

  • 结点:树的数据元素

  • 结点的度:结点挂接的子树数(有几个直接后继就是几度)

  • 结点的层次:从根到该结点的层数(根结点算第一层)

  • 终端结点:度为0的结点,即叶子

  • 分支节点:除树根以外的结点(也称为内部结点)

  • 树的度:所有结点度中的最大值(Max(各结点的度))

  • 树的深度(或高度):所有结点中最大的层数(Max(各结点的层次))

4.2树的表示法

4.2.1图形表示法

4.2.2广义表表示法

4.2.3左孩子有兄弟表示法

4.2 二叉树的基本概念

定义

        n(n≥0)个结点的有限集合,有一个根结点以及两颗互不相交的、分别称为左子树和右子树的二叉树组成。

逻辑结构

一对二(1:2)

基本特征

每个结点最多只有两颗子树(不存在度大于2的结点)

左子树和右子树次序不能颠倒(有序树)

基本形态

二叉树的性质

  • 性质1: 在二叉树的第i层上至多有₂i-1个结点(i>0)

  • 性质2: 深度为k的二叉树至多有₂k-1个结点(k>0)

  • 性质3: 对于任何一棵二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1 (即n0=n2+1)

  • 性质4: 具有n个结点的完全二叉树的深度必为[log2n](向下取整)+1 ,(如 log2 (15) 点击 15 log / 2 log =)

  • 性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)

满二叉树 一棵深度为k,结点数量为₂k-1个结点的二叉树 特点是每层都充满了结点

完全二叉树 除最后一层外,每一层上的节点数均达到最大值,在最后一层上只缺少右边的若干结点。

左孩子右兄弟表示法:

4.5 二叉树的递归遍历*

按某条搜索路线遍访每个结点且不重复(又称周游)

1.1 利用树递归特性可以将二叉树进行递归遍历
1.2 先序遍历 先根 再左 再右
1.3 中序遍历 先左 再根 再右
1.4 后序遍历 先左 再右 再根
1.5 利用代码实现二叉树递归遍历

 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 ​
 struct BinaryNode
 {
     char ch; //数据域
     struct BinaryNode * lChild; //左孩子节点
     struct BinaryNode * rChild; //右孩子节点
 };
 ​
 //递归遍历函数
 void recursion(struct BinaryNode * root)
 {
     if (root == NULL)
     {
         return;
     }
 ​
     //先序遍历  先根 再左  再右
     
     printf("%c ", root->ch);
 ​
     recursion(root->lChild);
 ​
     recursion(root->rChild);
 ​
 }
 ​
 void test01()
 {
     struct BinaryNode nodeA = { 'A', NULL, NULL };
     struct BinaryNode nodeB = { 'B', NULL, NULL };
     struct BinaryNode nodeC = { 'C', NULL, NULL };
     struct BinaryNode nodeD = { 'D', NULL, NULL };
     struct BinaryNode nodeE = { 'E', NULL, NULL };
     struct BinaryNode nodeF = { 'F', NULL, NULL };
     struct BinaryNode nodeG = { 'G', NULL, NULL };
     struct BinaryNode nodeH = { 'H', NULL, NULL };
 ​
     //建立结点之间的关系
 ​
     nodeA.lChild = &nodeB;
     nodeA.rChild = &nodeF;
 ​
     nodeB.rChild = &nodeC;
 ​
     nodeC.lChild = &nodeD;
     nodeC.rChild = &nodeE;
 ​
     nodeF.rChild = &nodeG;
     
     nodeG.lChild = &nodeH;
 ​
     //递归遍历
     recursion(&nodeA);
 ​
 }
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }
4.6 二叉树编程

1.1 利用递归特性 求出二叉树中的叶子的数量

1.2 求出二叉树的高度

1.3 利用递归特性 拷贝出二叉树

 //二叉树编程
 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 ​
 struct BinaryNode
 {
     char ch; //数据域
     struct BinaryNode * lChild; //左孩子节点
     struct BinaryNode * rChild; //右孩子节点
 };
 //统计叶子数量
 void calculateLeafNum(struct BinaryNode* root, int * num)
 {
     //递归的结束条件
     if (root == NULL)
     {
         return;
     }
 ​
     if (root->lChild == NULL && root->rChild == NULL)
     {
         (*num)++;
     }
 ​
     calculateLeafNum(root->lChild, num);
     calculateLeafNum(root->rChild, num);
 }
 ​
 //求树的高度 
 int getTreeHeight(struct BinaryNode * root)
 {
     if (root == NULL)
     {
         return 0;
     }
     //求出左子树的高度 
     int lHeight = getTreeHeight(root->lChild);
     int rHeight = getTreeHeight(root->rChild);
     //取左子树 和 右子树中最大值 +1 
     int height = lHeight > rHeight ? lHeight + 1 : rHeight + 1;
     return height;
 }
 ​
 struct BinaryNode * copyBinaryTree(struct BinaryNode * root)
 {
     if (root == NULL)
     {
         return NULL;
     }
     //先拷贝 左子树 
     struct BinaryNode * lChild = copyBinaryTree(root->lChild);
     //再拷贝 右子树
     struct BinaryNode * rChild = copyBinaryTree(root->rChild);
     //创建新节点 
     struct BinaryNode * newNode = malloc(sizeof(struct BinaryNode));
     newNode->lChild = lChild;
     newNode->rChild = rChild;
     newNode->ch = root->ch;
     //返回给用户
     return newNode;
 }
 //遍历树
 void showBinaryTree(struct BinaryNode * root)
 {
     if (root == NULL)
     {
         return;
     }
     printf("%c ", root->ch);
     showBinaryTree(root->lChild);
     showBinaryTree(root->rChild);
 }
 //释放树
 void freeTree(struct BinaryNode * root)
 {
     if (root == NULL)
     {
         return;
     }
     //先释放左子树 
     freeTree(root->lChild);
     //再释放右子树
     freeTree(root->rChild);
     printf("%c 被释放了\n", root->ch);
     //释放根节点
     free(root);
 }
 ​
 void test01()
 {
     struct BinaryNode nodeA = { 'A', NULL, NULL };
     struct BinaryNode nodeB = { 'B', NULL, NULL };
     struct BinaryNode nodeC = { 'C', NULL, NULL };
     struct BinaryNode nodeD = { 'D', NULL, NULL };
     struct BinaryNode nodeE = { 'E', NULL, NULL };
     struct BinaryNode nodeF = { 'F', NULL, NULL };
     struct BinaryNode nodeG = { 'G', NULL, NULL };
     struct BinaryNode nodeH = { 'H', NULL, NULL };
     
     //建立结点之间的关系
     nodeA.lChild = &nodeB;
     nodeA.rChild = &nodeF;
     nodeB.rChild = &nodeC;
     nodeC.lChild = &nodeD;
     nodeC.rChild = &nodeE;
     nodeF.rChild = &nodeG;
     nodeG.lChild = &nodeH;
     
     //1、求树中的叶子的数量
     int num = 0;
     calculateLeafNum(&nodeA, &num);
     printf("叶子的数量为:%d\n", num);
     
     //2、求树的高度/深度
     int height = getTreeHeight(&nodeA);
     printf("树的高度为: %d\n", height);
     
     //3、拷贝二叉树
     struct BinaryNode * newTree = copyBinaryTree(&nodeA);
     
     showBinaryTree(newTree);
     printf("\n");
     
     //4、释放二叉树
     freeTree(newTree);
 }
 ​
 int main(){
     test01();
     system("pause");
     return EXIT_SUCCESS;
 }
4.7 二叉树的非递归遍历

1.1 利用栈容器实现二叉树的非递归遍历

1.2 首先将每个节点都设置一个标志,默认标志为假,根据节点的的状态进行如下流程

执行上述流程,可以得到先序遍历的结果,如果想得到其他二叉树遍历结果,修改2.4步骤即可。

 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 ​
 #define  MAX 1024
 //栈的结构体
 struct SStack
 {
     void * data[MAX]; //数组
 ​
     //栈的元素个数
     int m_Size;
 };
 ​
 typedef void * seqStack;
 ​
 //初始化栈
 seqStack init_SeqStack()
 {
     struct SStack * stack = malloc(sizeof(struct SStack));
 ​
     if (stack == NULL)
     {
         return NULL;
     }
 ​
     //清空数组中的每个元素
     memset(stack->data, 0, sizeof(void*)*MAX);
 ​
     stack->m_Size = 0;
 ​
     return stack;
 }
 //入栈
 void push_SeqStack(seqStack stack, void * data)
 {
     if (stack == NULL)
     {
         return;
     }
     if (data == NULL)
     {
         return;
     }
     //判断是否已经栈满 ,如果满了 不可以再入栈了
     struct SStack * myStack = stack;
     if (myStack->m_Size == MAX)
     {
         return;
     }
 ​
     myStack->data[myStack->m_Size] = data; //入栈  尾插
 ​
     myStack->m_Size++; //更新栈大小
 }
 ​
 //出栈
 void pop_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return;
     }
 ​
     //如果是空栈 不执行出栈
     struct SStack * myStack = stack;
     if (myStack->m_Size <= 0)
     {
         return;
     }
 ​
     //执行出栈
     myStack->data[myStack->m_Size - 1] = NULL;
     //更新栈的大小
     myStack->m_Size--;
 }
 //获取栈顶元素
 void * top_SeqStack(seqStack stack)
 {
 ​
     if (stack == NULL)
     {
         return NULL;
     }
 ​
     struct SStack * myStack = stack;
 ​
     //如果是空栈   返回 NULL
     if (myStack->m_Size == 0)
     {
         return NULL;
     }
     return myStack->data[myStack->m_Size - 1];
 }
 ​
 //栈的大小
 int size_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return -1;
     }
 ​
     struct SStack * myStack = stack;
 ​
     return myStack->m_Size;
 }
 //判断栈是否为空
 int isEmpty_SeqStack(seqStack stack)
 {
     if (stack == NULL)
     {
         return -1; //真
     }
 ​
     struct SStack * myStack = stack;
     if (myStack->m_Size <= 0)
     {
         return 1; //真
     }
 ​
     return  0; //返回假 代表不是空栈
 }
 //销毁栈
 void destroy_SeqStack(seqStack stack)
 {
 ​
     if (stack == NULL)
     {
         return;
     }
 ​
     free(stack);
     stack = NULL;
 }
 ​
 struct BinaryNode
 {
     char ch; //数据域
     struct BinaryNode * lChild; //左孩子节点
     struct BinaryNode * rChild; //右孩子节点
     //标示
     int flag;
 };
 /*
     1、将根节点 压入栈中
     2、只要 栈size> 0  执行循环
         2.1 拿出栈顶元素
         2.2 如果栈顶元素的标志位 真    直接输出  执行下一次循环
         2.3 如果不是真 该flag的标志位真
         2.4 将  右子节点  和 左子节点  和 根 入栈
         2.5 执行下一次循环
 */
 ​
 void nonRecursion(struct BinaryNode * root)
 {
     //初始栈
     seqStack myStack = init_SeqStack();
 ​
     push_SeqStack(myStack, root);
 ​
 ​
     while (size_SeqStack(myStack)>0)
     {
         //获取栈顶元素
         struct BinaryNode * topNode = top_SeqStack(myStack);
 ​
         //弹出栈顶
         pop_SeqStack(myStack);
 ​
         //如果栈顶元素的标志位 真    直接输出  执行下一次循环
         if (topNode->flag == 1)
         {
             printf("%c ", topNode->ch);
             continue;
         }
         //如果不是真 该flag的标志位真
         topNode->flag = 1;
         //将  右子节点  和 左子节点  和 根 入栈
         if (topNode->rChild != NULL)
         {
             push_SeqStack(myStack, topNode->rChild);
         } 
 ​
         if (topNode->lChild != NULL)
         {
             push_SeqStack(myStack, topNode->lChild);
         }
 ​
         push_SeqStack(myStack,topNode);
     }
 ​
     //栈销毁掉
     destroy_SeqStack(myStack);
     myStack = NULL;
 }
 ​
 void test01()
 {
     struct BinaryNode nodeA = { 'A', NULL, NULL, 0 };
     struct BinaryNode nodeB = { 'B', NULL, NULL, 0 };
     struct BinaryNode nodeC = { 'C', NULL, NULL, 0 };
     struct BinaryNode nodeD = { 'D', NULL, NULL, 0 };
     struct BinaryNode nodeE = { 'E', NULL, NULL, 0 };
     struct BinaryNode nodeF = { 'F', NULL, NULL, 0 };
     struct BinaryNode nodeG = { 'G', NULL, NULL, 0 };
     struct BinaryNode nodeH = { 'H', NULL, NULL, 0 };
 ​
     //建立结点之间的关系
 ​
     nodeA.lChild = &nodeB;
     nodeA.rChild = &nodeF;
 ​
     nodeB.rChild = &nodeC;
 ​
     nodeC.lChild = &nodeD;
     nodeC.rChild = &nodeE;
 ​
     nodeF.rChild = &nodeG;
 ​
     nodeG.lChild = &nodeH;
 ​
     //执行非递归遍历
     nonRecursion(&nodeA);
 }
 ​
 int main(){
 ​
     test01();
 ​
     system("pause");
     return EXIT_SUCCESS;
 }
4.8 哈夫曼树

        给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

4.9 图的遍历

        图的遍历指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可视为一种特殊的图的遍历。图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。图的遍历比树的遍历要复杂得多,因为图的任一顶点都可能和其余的顶点相邻接,所以在访问某个顶点后,可能沿着某条路径搜索又回到该顶点上。为避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的顶点,为此可以设一个辅助数组来标记顶点是否被访问过。图的遍历算法主要有两种:广度优先遍历(BFS)深度优先遍历(DFS)

广度优先遍历(BFS)

类似于二叉树的层序遍历算法。基本思想是:先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1,w2,...,wi,然后依次访问w1,w2,...,wi的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,访问它们所有未被访问过的邻接顶点,直到图中所有顶点都被访问过为止。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为始点,重复上述过程,直到图中所有顶点都被访问到为止。

深度优先遍历(DFS)

        类似于树的先序遍历,尽可能深的搜索一个图。基本思想是:首先访问图中某一起始顶点v,然后由v出发,访问与v邻接且未被访问的任一顶点w1,再访问与w1邻接且未被访问的任一顶点w2......重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该顶点开始继续上述搜索过程,直到图中所有顶点均被访问过为止。

4.10 最短路径
4.11 邻接矩阵

        指用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息(即各顶点之间的邻接关系),存储顶点之间邻接关系的二维数组称为邻接矩阵。

4.12 十字链表

十字链表是有向图的一种链式存储结构,只适用于存储有向图。在十字链表中,对应于有向图中的每条弧有一个结点,对应于每个顶点也有一个结点。

4.13 堆

        堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个结点的值总是不大于或不小于其父结点的值

  • 堆总是一棵完全二叉树

根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。堆是非线性数据结构,相当于一维数组,有两个直接后继

4.14 二叉排序树

性质:

  • 若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。

  • 若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。

  • 它的左、右树又分为⼆叉排序树

显然,二叉排序树与二叉树一样,也是通过递归的形式定义的。因此,它的操作也都是基于递归的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值