(第五章、第四章)数据结构总复习+错题+一些不清楚的点。

(第五章、第四章)数据结构总复习+错题+一些不清楚的点。

第五章 搜索树(只学了二叉排序树)

慕课:

‍设二叉排序树中关键字由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. 二叉树被定义为结点的有限集合,分为左右子树,这里是递归定义。
  1. (1)完全二叉树:除了最后一层,其他都为满的。
    (2)满二叉树:除了叶子结点(度为0)外,每个结点都有两个子节点(即度为2)
    (3)扩充二叉树:将不满的补满,补上的结点叫做扩充结点(外部结点),原有的叫做内部结点。
2.二叉树的数学性质
  1. 第i层上最多有2i 个结点(i>=0)
  2. 深度为k的二叉树最多有20+21+22+…+2k个结点,即2k+1-1
  3. 对于任何一棵二叉树,如果叶子结点的个数为n0,度为2的个数为n2,那么必有n0=n2+1;
    - 推导可以由分支数的两种表示情况来表示:(1)结点总数-1是分支数(2)度为1的会引出1个,度为2的会引出两个。
  4. 若i=0的结点是根,则(i-1)/2为父节点。(按照整形的除法取整)
  5. 2i+1,2i+2找孩子,不过不能大于n-1。n-1就是最后一个结点的序号了,n在这里表示了结点的数量。
3.二叉树的先序、中序、后序遍历(递归)
  1. 先序:根、左、右。如果采用递归算法的话,先判断空,空则返回,然后访问该结点(输出数值),然后递归左子树,递归右子树。(相当于一直访问左,再慢慢回头右)
  2. 中序:左、根、右。如果采用递归算法的话,先判断空,空则返回,然后递归左子树,访问结点,递归右子树。(相当于先找到最左,再访问,再去看右,右也要先找到最左,再访问,再去看右,以此类推。。)
  3. 后序:左、右、根。如果采用递归算法的话,先判断空,空则返回,然后递归左子树、递归右子树、访问结点。
  4. 根据先序和中序画出二叉树。
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;
}
  1. 先序:

    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");
    }
    
  2. 中序:

    一路向左,左不动就去访问右,再把右当作起始的结点进行遍历。

    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");
    }
    
  3. 后序:

    • 能左就左,不能左就右,直到左右都不行的时候才能访问该节点,然后回退。左退出的要去右,右退出的才能回到上一层
    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");
    }
    
  4. 层次遍历

    相对见到很多,以此入队出队访问即可。

    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。

  • 注意:画的时候要充分进行比较,一定要确保两个结点时序列当前的最小的两个结点,不一定非得是新结点和序列中结点的结合。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值