数据结构学习笔记

正在学习,未完待续······


逻辑结构:集合结构,线性结构,树结构,图结构
物理结构:顺序存储结构,链式存储结构

一、线性表

二、栈和队列

1.栈

2.队列

三、字符串

四、树

1.二叉树性质和存储结构

1.性质:
在二叉树的第i层上至少有一个结点,至多有2的i-1次方个结点
深度为k的二叉树至少有k个结点,至多有2的k次方-1个结点
在这里插入图片描述

2.满二叉树和完全二叉树:
对满二叉树结点位置编号:自上而下,自左而右,每个结点位置都有元素
在满二叉树中,从最后一个结点开始连续去掉任意个结点,即为完全二叉树
完全二叉树的任意结点,若其右子树最大层次为i,则其左子树的最大层次必为i或i+1
在这里插入图片描述

在这里插入图片描述

3.存储结构:
顺序存储:(缺点:尤其对于右单支树会很浪费空间,因此适合满二叉树和完全二叉树)
在这里插入图片描述

链式存储:(左指针域,数据元素,右指针域)

在n个结点的二叉链表中,必有2n个指针域,除根结点外,每个结点只有一个双亲,所以共有n-1个指针域存放指针,因此有n+1个空指针域

三叉链表(左指针域,数据元素,双亲指针,右指针域)

2.遍历二叉树和线索二叉树

1.DLR先序遍历、LDR中序遍历、LRD后序遍历

typedef struct Ttree
{
    char data;
	struct Ttree* PL;
	struct Ttree* PR;
}Ttree;
void pretraversal(BTtree tree)
{
	if (tree!=NULL)
	{
		cout << tree->data << endl;
		if (tree->PL!=NULL)
		   pretraversal(tree->PL);
		if (tree->PR!=NULL)
		   pretraversal(tree->PR);   
	}
}
void intraversal(BTtree tree)
{
	if (tree!=NULL)
	{
		if (tree->PL!=NULL)
		   intraversal(tree->PL);
		cout << tree->data << endl;
		if (tree->PR!=NULL)
		   intraversal(tree->PR);   
	}
}
void posttraversal(BTtree tree)
{
	if (tree!=NULL)
	{
		if (tree->PL!=NULL)
		   posttraversal(tree->PL);
		if (tree->PR!=NULL)
		   posttraversal(tree->PR);
		   cout << tree->data << endl;   
	}
} 

三种遍历如果去掉输出部分在本质上是相同的,都是按照相同的路径,只不过访问结点数据的时机不同,其时间复杂度和空间复杂度均为O(n)

在这里插入图片描述

可以通过前序和中序或中序和后序来确定唯一的二叉树结构

中序遍历的栈算法:

根结点入栈,访问其左子树,遇到根结点入栈,直到其左子树为空,根结点出栈,然后访问其右子树

void InorderTraverse(BiTree T)
{
	BiTree p;
	InitStackEmpty(S);
	p=T;
	if(p)
	{
		Push(S,p);
		p=p->lchild;
	}
	else
	{
		Pop(S,q);
		cout << q->data;
		p=q->rchild;
	}
}

二叉树的层次遍历:(队列实现)

根结点入队,然后出队,如果它有孩子,则同时令左右孩子依次入队,以此类推,不断从一端出队同时孩子另一端入队

void LevelOrder(BTNode *b)
{
	BTNode *p;
	SqQueue *qu;
	InitQueue(qu);
	enQueue(qu,b);
	while (!QueueEmpty(qu))
	{
		deQueue(qu,p);
		cout << p->data;
		if (p->lchild!=NULL)
		    enQueue(qu,p->lchild)
		if (p->rchild!=NULL)
		    enQueue(qu,p->rchild)    
	}
}

2.遍历算法的应用
二叉树的建立:

//键盘输入时空结点不能省略,用'#'表示
void CreatBiTree(BiTree &T)
{
	cin >> ch;
	if (ch=='#')
	    T=NULL;
	else
	{
		if (!(T=(BiTNode *)malloc(sizeof(BiTNode))))
		    exit(OVERFLOW);
		T->data=ch;
		CreatBitree(T->lchild);
		CreatBitree(T->rchild);    
	}    
}

复制二叉树:

void Copy(BiTree T, BiTree &NewT)
{
	if (T==NULL)
		NewT=NULL;
	else
	{
		NewT=new BiTNode;
		NewT->data=T->data;
		Copy(T->lchild, NewT->lchild);
		Copy(T->rchild, NewT->rchild);
	}	
}

计算二叉树深度:

int Depth(BiTree T)
{
	if (T==NULL)
		return 0;
	else
	{
		m=Depth(T->lchild);
		n=Depth(T->rchild);
		if (m>n)
		    return (m+1);
		else
		    return (n+1);    
	}	
}

计算二叉树结点总数:
(左子树结点数+右子树结点数+1)

int NodeCount(BiTree T)
{
	if (T==NULL)
		return 0;
	else
		return NodeCount(T->lchild)+(T->rchild)+1;	
}

计算叶子结点数:

int LeadCount(BiTree T)
{
	if (T==NULL)
		return 0;
	if (T->lchild==NULL&&T->rchild==NULL)
	    return 1;	
	else
		return NodeCount(T->lchild)+(T->rchild);	
}

3.线索二叉树:

利用二叉树中n+1个空指针域,若结点的左指针域为空则将指针指向其前驱,若右空则指向其后继,这些指针称为线索,同时增加两个标志域,若为0表示指针指向孩子,为1则指向前驱或后继(这里的前驱或后继为先序,中序,后序序列中各结点的前驱和后继)
在这里插入图片描述

3.树与森林

1.树的存储:
双亲表示法:
在这里插入图片描述

孩子表示法:
在这里插入图片描述

孩子兄弟表示法(二叉链表表示法)

从根节点开始其左指针域指向从左往右第一个孩子,右指针域指向其兄弟
该方法用于实现树和二叉树的转换
在这里插入图片描述

森林传化二叉树:
先把每棵树转化为二叉树,在将其他树的根结点当作第一个树根结点的兄弟

2.树的遍历:
先根遍历:若树不空,先访问根结点,然后依次先根遍历各棵子树
后根遍历:若树不空,先依次后根遍历各棵子树,然后访问根结点
层次遍历:若树不空,则自上而下自左而右访问各个结点
3.森林的遍历:
先序遍历:依次从左至右对森林中每一棵树进行先根遍历
中序遍历:依次从左至右对森林中每一棵树进行后根遍历

4.哈夫曼树及应用

树的带权路径长度:树中所有叶子结点的带权路径长度之和,记作WPL

1.哈夫曼算法:每次选取权值最小的两个结点构造树

包含n棵树的森林要经过n-1次合并才能形成哈夫曼树,共产生n-1个新结点
包含n个叶子结点的哈夫曼树共有2n-1个结点

void CreatHuffmanTree(HuffmanTree HT, int n)
{//构造哈夫曼树
	if (n<=1)
	    return;
	m=2*n-1;//数组共2n-1个元素    
	HT=new HTNode[m+1];//0号单元未用,HT[m]表示根结点 
	for (i=1;i<=m;i++)//将2n-1个元素的lch,rch,parent置为0
	{
		HT[i].lch=0;
		HT[i].rch=0;
		HT[i].parent=0;
	}   
	for (i=1;i<=n;++i)输入前n个元素的weight值
	    cin >> HT[i].weight;//初始化结束,下面开始建立哈夫曼树
	for (i=n+1;i<=m;i++)//合并产生n-1个结点--构造树
	{
		Select(HT,i-1,s1,s2);//在HT[k](1<=k<=i-1)中选择两个其双亲域为0,且权值最小的结点,并返回它们在HT中的序号s1和s2
		HT[s1].parent=i;
		HT[s2].parent=i;//从F中删除s1,s2
		HT[i].lch=s1;
		HT[i].rch=s2;//s1,s2分别作为i的左右孩子
		HT[i].weight=HT[s1].weight+HT[s2].weight;//i的权值为左右孩子权值之和
	}    
}

2.哈夫曼编码

统计字符集中每个字符在电文中出现的平均概率(概率越大要求编码越短)
利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短
哈夫曼树的每个结点的左分支标0,右分支标1,把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码

在这里插入图片描述
为什么哈夫曼编码能够保证是前缀编码?

因为没有一片叶子是另一片叶子的祖先,所以每个叶结点的编码就不可能是其他叶结点编码的前缀

为什么哈夫曼编码能够保证字符编码总长最短?

因为哈夫曼树的带权路径长度最短,故字符编码的总长最短

void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中 
	HC=new char *[n+1];//分配n个字符编码的头指针矢量 
	cd=new char [n];//分配临时存放编码的动态数组空间 
	cd[n-1]='\0';//编码结束符 
	for (i=1;i<=n;++i)//逐个字符求哈夫曼编码 
	{
		start=n-1;
		c=i;
		f=HT[i].parent;
		while (f!=0)//从叶子结点开始向上回溯,直到根结点 
		{
			--start;//回溯一次start向前指一个位置 
			if (HT[f].lchild==c)
			    cd[start]='0';//结点c是f的左孩子,则生成代码0 
			else
			    cd[start]='1';//结点c是f的右孩子,则生成代码1
			c=f;
			f=HT[f].parent;	//继续向上回溯    
		}//求出第i个字符的编码 
		HC[i]=new char [n-start];//为第i个字符串编码分配空间 
		strcpy(HC[i],&cd[start]);//将求得的编码从临时空间cd复制到HC的当前行中 
	}
	delete cd;//释放临时空间 
}

文件的编码和解码:

编码:
输入各字符及其权值
构造哈夫曼树–HT[i]
进行哈夫曼编码–HC[i]
查HC[i],得到各字符的哈夫曼编码

解码:
构造哈夫曼树
依次读入二进制码
读入0,则走向左孩子;读入1,则走向右孩子
一旦到达某叶子时,即可译出字符
然后再从根出发继续译码,直到结束

5.树的操作

五.图

1.定义和基本术语

1.G=(V,E) //Graph= (Vertex, Edge)

V: 顶点(数据元素)的有穷非空集合;
E: 边的有穷集合

2.有向图:弧
无向图:边
完全图:任意两个点都有一条边相连
稀疏图:有很少边或弧的图(e<nlogn)
稠密图:有较多边或弧的图
网:边/弧带权的图
邻接:有边/弧相连的两个顶点的关系

(Vi,Vj):互为邻接点
<Vi,Vj>:Vi邻接到Vj,Vj邻接于Vi

关联(依附):边/弧与顶点之间的关系

(Vi,Vj)/<Vi,Vj>:该边/弧关联于Vi和Vj

3.顶点的度:与该顶点相关联的边的数目,记作TD(v)

在有向图中,顶点的度等于该顶点的入度与出度之和
顶点v的入度是以v为终点的有向边的条数,记作ID(v)
顶点v的出度是以v为始点的有向边的条数,记作OD(v)

如果一个图中只有一个顶点的入度为0,其余顶点入度都为1,则该图形状一定为有向树

2.图的存储结构

图没有顺序存储结构,但可以借助二维数组来表示元素间的关系:数组表示法(邻接矩阵)
链式存储结构:多重链表:邻接表、邻接多重表、十字链表

邻接矩阵:(一个一维数组存储顶点信息,一个二维数组存储邻接矩阵)
1.无向图邻接矩阵:如果i, j之间有边,则二维数组a[i][j]=1,否则=0
特点:为对称矩阵,且对角线上均为0(完全图的邻接矩阵对角元素为0,其余均为1)
2.有向图邻接矩阵:第i行含义:出度边,第i列含义:入度边
3.网(有权图)的邻接矩阵:有弧即记录权值,无弧记为无穷大
4.无向图的算法实现:

#define MVNum 100
typedef char VerTexType;
typedef int ArcType;
typedef struct
{
	VerTexType ves[MVNum];
	ArcType arcs[MVNum][MVNum];
	int vexnum,arcnum;
}AMGraph;

Status CreateUDN(AMGraph &G)
{
	cin >> G.vexnum >> G.arcnum;
	for (int i=0;i<G.vexnum;i++)
	    cin >> G.ves[i];
	for (int i=0;i<G.vexnum;i++)
	    for (int j=0;j<G.vexnum;j++)
		    G.arcs[i][j]=MaxInt;//权值均初始化为无穷大
	int v1, v2, w;	    
	for (int k=0;k<G.arcnum;k++)
	    cin >> v1 >> v2 >> w;//输入一条边所依附的两个bian顶点以及边的权值 
	i=LocateVex(G,v1); 
	j=LocateVex(G,v2);//确定v1,v2在顶点表中的位置	
	G.arcs[i][j]=w;
	G.arcs[j][i]=w;	//对称边权值相等 
	return OK;	    
} 

邻接表表示法:(链式)
按照编号顺序将顶点数据存储在一维数组里,关联同一顶点的边(或以该顶点为尾的弧)用线性链表存储
无向图:邻接表不唯一,如果有n个顶点,e条边,则邻接表需要n个头结点(数组元素),2e个表结点(每条边被存储两次),适宜于存储稀疏图
有向图:顶点Vi的出度为第i个链表中结点个数(不包括头结点),入度需要遍历整个结点域

3.图的遍历

4.图的应用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你好!关于学习数据结构的C语言笔记,我可以给你一些基本的指导和概念。数据结构是计算机科学中非常重要的一门课程,它涉及存储和组织数据的方法。C语言是一种常用的编程语言,很适合用于实现各种数据结构。 下面是一些数据结构的基本概念,你可以在学习笔记中包含它们: 1. 数组(Array):一种线性数据结构,可以存储相同类型的元素。在C语言中,数组是通过索引访问的。 2. 链表(Linked List):也是一种线性数据结构,但不需要连续的内存空间。链表由节点组成,每个节点包含数据和指向下一个节点的指针。 3. 栈(Stack):一种后进先出(LIFO)的数据结构,类似于装满物品的箱子。在C语言中,可以使用数组或链表来实现栈。 4. 队列(Queue):一种先进先出(FIFO)的数据结构,类似于排队等候的队伍。同样可以使用数组或链表来实现队列。 5. 树(Tree):一种非线性数据结构,由节点和边组成。每个节点可以有多个子节点。二叉树是一种特殊的树结构,每个节点最多有两个子节点。 6. 图(Graph):另一种非线性数据结构,由节点和边组成。图可以用来表示各种实际问题,如社交网络和地图。 这只是数据结构中的一些基本概念,还有其他更高级的数据结构,如堆、哈希表和二叉搜索树等。在学习笔记中,你可以介绍每个数据结构的定义、操作以及适合使用它们的场景。 希望这些信息对你有所帮助!如果你有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cloooocker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值