(第五章、第四章)数据结构总复习+错题+一些不清楚的点。
文章目录
第五章 搜索树(只学了二叉排序树)
慕课:
设二叉排序树中关键字由1到1000整数构成,现要查找关键字为363的结点,下述关键字序列中,不可能在二叉排序上查找的序列是
做法:画二叉排序树,然后看,所有右孩子不能高于父节点深处左孩子的。(因为是查找序列,所以不会有两个分支,而是歪歪扭扭的长线)
二叉排序树的创建(手画)以及查找算法
-
!!!!注意这里与二叉排序树不一样,不一样!!!!!
(这个给了排好的有序序列才能画)画二分查找判定树:手画的时候,就是一直找mid,第一个mid是根节点,左侧不包含根节点再找一个mid是左子树,右侧不包含根节点再找一个mid是右子树。一次类推,如果左侧没了就是NULL。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
画二叉排序树:是根据定义,左侧的一定是比自己小的,右侧的一定是比自己大的。
首先自己手动搞了一个二叉排序树~~(被自己菜死了)~~
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<queue>
using namespace std;
typedef int Datatype;
struct BTNode
{
Datatype data;
struct BTNode* leftchild;
struct BTNode* rightchild;
};
typedef BTNode* Bintree;
Bintree BT_creat(int* sortArr)
{
int i, count = -1;
queue<Bintree>lqueue;
Bintree bintree=NULL;
Bintree parent=NULL;
for (i = 0; i < 15; i++)
{
Bintree tempNode = new struct BTNode();
tempNode->data = sortArr[i];
tempNode->leftchild = NULL;
tempNode->rightchild = NULL;
if (tempNode->data != 0)
lqueue.push(tempNode);
else tempNode = NULL;
count++;
if (count == 0)bintree = tempNode;
else
{
parent = lqueue.front();
if (count % 2 == 1)parent->leftchild = tempNode;
else
{
parent->rightchild = tempNode;
lqueue.pop();
}
}
}
return(bintree);
}
void BT_search(Bintree bintree, int key)//顺便统计了一下查找次数
{
Bintree p = bintree;
int count = 1;
while (p)
{
if (key == p->data)
{
printf("it exist! it is %d times\n",count);
return;
}
if (key > p->data)
{
p = p->rightchild;
count++;
}
else
{
p = p->leftchild;
count++;
}
}
printf("it is not exist!,it is %d times\n",count);
}
int main()
{
int sortArr[] = { 36,10,56,4,18,48,63,0,0,0,27,0,50,0,82 };//找了现成的二叉排序树来创建,0表示结点是NULL;
Bintree bintree = BT_creat(sortArr);
BT_search(bintree, 82);
BT_search(bintree, 2);
BT_search(bintree, 51);
return 0;
}
第四章,树和二叉树
-
慕课:
- 在问组成形态的时候,只看形状。
- 线索二叉树,只要线索为1,就说明指向的是前驱或后继,而不是对应的子树。
1.二叉树的基本概念
- 二叉树被定义为结点的有限集合,分为左右子树,这里是递归定义。
- (1)完全二叉树:除了最后一层,其他都为满的。
(2)满二叉树:除了叶子结点(度为0)外,每个结点都有两个子节点(即度为2)
(3)扩充二叉树:将不满的补满,补上的结点叫做扩充结点(外部结点),原有的叫做内部结点。
2.二叉树的数学性质
- 第i层上最多有2i 个结点(i>=0)
- 深度为k的二叉树最多有20+21+22+…+2k个结点,即2k+1-1
- 对于任何一棵二叉树,如果叶子结点的个数为n0,度为2的个数为n2,那么必有n0=n2+1;
- 推导可以由分支数的两种表示情况来表示:(1)结点总数-1是分支数(2)度为1的会引出1个,度为2的会引出两个。- 若i=0的结点是根,则(i-1)/2为父节点。(按照整形的除法取整)
- 2i+1,2i+2找孩子,不过不能大于n-1。n-1就是最后一个结点的序号了,n在这里表示了结点的数量。
3.二叉树的先序、中序、后序遍历(递归)
- 先序:根、左、右。如果采用递归算法的话,先判断空,空则返回,然后访问该结点(输出数值),然后递归左子树,递归右子树。(相当于一直访问左,再慢慢回头右)
- 中序:左、根、右。如果采用递归算法的话,先判断空,空则返回,然后递归左子树,访问结点,递归右子树。(相当于先找到最左,再访问,再去看右,右也要先找到最左,再访问,再去看右,以此类推。。)
- 后序:左、右、根。如果采用递归算法的话,先判断空,空则返回,然后递归左子树、递归右子树、访问结点。
- 根据先序和中序画出二叉树。
4.二叉树的先序、中序、后序的非递归遍历以及层次遍历。
- c时间复杂度都为O(n)
先用先序遍历创建了一个二叉树:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<queue>
#include<stack>
using namespace std;
typedef int Datatype;
struct BNODE
{
Datatype data;
struct BNODE* leftchild;
struct BNODE* rightchild;
};
typedef struct BNODE* Btree;
Btree PreorderBuild()//先序循环创建
{
Btree bt;
char ch;
scanf("%c",&ch);
getchar();//吸收掉空格与回车
if (ch == '@') bt = NULL;//@为空
else
{
bt = (Btree)malloc(sizeof(struct BNODE));
bt->data = ch;
bt->leftchild=PreorderBuild();
bt->rightchild=PreorderBuild();
}
return bt;
}
/*测试用例:
A B @ D G I @ @ J @ @ H @ @ C E @ @ F @ @
*/
int main()
{
Btree bt=PreorderBuild();
PreorderVisit(bt);
InorderVisit(bt);
PostorderVisit(bt);
CengCiVisit(bt);
return 0;
}
-
先序:
void PreorderVisit(Btree bt)//非递归先序遍历 { Btree p = bt; stack<Btree>lstack; if (p == NULL)return; lstack.push(p); while (!lstack.empty()) { p = lstack.top(); lstack.pop(); while (p) { printf("%c ", p->data); if (p->rightchild != NULL)lstack.push(p->rightchild); p = p->leftchild; } } printf("\n"); }
-
中序:
一路向左,左不动就去访问右,再把右当作起始的结点进行遍历。
void InorderVisit(Btree bt)//非递归中序遍历 { Btree p=bt; stack<Btree>lstack; if (p == NULL)return; lstack.push(p); p = p->leftchild; while (!lstack.empty() || p != NULL) { while (p) { lstack.push(p); p = p->leftchild; } p = lstack.top(); lstack.pop(); printf("%c ", p->data); p = p->rightchild; } printf("\n"); }
-
后序:
- 能左就左,不能左就右,直到左右都不行的时候才能访问该节点,然后回退。左退出的要去右,右退出的才能回到上一层
void PostorderVisit(Btree bt)//非递归后序遍历 { Btree p = bt; stack<Btree>lstack; if (p == NULL)return; while (!lstack.empty() || p != NULL) { while (p != NULL) { lstack.push(p); p = p->leftchild ? p->leftchild : p->rightchild; } p = lstack.top(); lstack.pop(); printf("%c ", p->data); if (!lstack.empty() && lstack.top()->leftchild == p) p = lstack.top()->rightchild; else p = NULL; } printf("\n"); }
-
层次遍历
相对见到很多,以此入队出队访问即可。
void CengCiVisit(Btree bt)//层次遍历 { Btree p = bt; queue<Btree>lqueue; if (p == NULL)return; lqueue.push(p); while (!lqueue.empty()) { p = lqueue.front(); lqueue.pop(); printf("%c ", p->data); if (p->leftchild != NULL) lqueue.push(p->leftchild); if (p->rightchild != NULL) lqueue.push(p->rightchild); } printf("\n"); }
-
输出:
输入:A B @ D G I @ @ J @ @ H @ @ C E @ @ F @ @
先序:A B D G I J H C E F
中序:B I G J D H A E C F
后序:I J G H D B E F C A
层次:A B C D E F G H I J
5.二叉树转化为树、森林c
1.树->二叉树
(1)加线:将兄弟结点相连。
(2)去线:只保留最左孩子的结点,其余孩子的结点的连线都去掉。(兄弟结点不变)
(3)调整:按照左右顺序进行调整。
2.二叉树->树
(1)加线:对于每一个左孩子结点,将它的父节点与与它的右孩子的结点,右孩子的右孩子的结点,直到不能再右为止,相连。
(2)去线:将每个结点与它的右孩子的结点的连线去掉。
(3)调整。
森林的话同理来搞就行。
6.根据先序和中序来画出二叉树
先序定根节点,中序定左右孩子。先序第一个一定是根节点,然后看先序的第二个与中序关系,中序排序中,左为在该节点的左侧部分,右为在该结点的右侧部分。
7.哈夫曼树与哈夫曼编码的手动画
找权值最小的两个结点,成为兄弟结点。然后在权值相加凝聚成一个新结点,再与后续的找,成。。。
最终数据结点都会是叶子结点。
求WPL(外部带权路径长度):权值*到达的路径长度相加。
哈夫曼编码:画出哈夫曼树后,左0右1。
注意:画的时候要充分进行比较,一定要确保两个结点时序列当前的最小的两个结点,不一定非得是新结点和序列中结点的结合。
951)]
森林的话同理来搞就行。
6.根据先序和中序来画出二叉树
先序定根节点,中序定左右孩子。先序第一个一定是根节点,然后看先序的第二个与中序关系,中序排序中,左为在该节点的左侧部分,右为在该结点的右侧部分。
7.哈夫曼树与哈夫曼编码的手动画(注意左小右次小!!!)
找权值最小的两个结点,成为兄弟结点。然后在权值相加凝聚成一个新结点,再与后续的找,成。。。
最终数据结点都会是叶子结点。
求WPL(外部带权路径长度):权值*到达的路径长度相加。
哈夫曼编码:画出哈夫曼树后,左0右1。
注意:画的时候要充分进行比较,一定要确保两个结点时序列当前的最小的两个结点,不一定非得是新结点和序列中结点的结合。