首页 > 技术文章 > 数据结构笔记(树、图、排序、查找部分)

haiyue-csdn 2020-02-03 01:25 原文

 

 

 

 

提纲形式:


    邻接矩阵(key)
        邻接矩阵的存储表示

#define MaxInt 32767
#define MVNumn 100
typedef char 
typedef int ArcType;
typedef struct
{
	char vexs[MVNum];
	int arcs[MVNum][MVNum];
	int vexnum,arcnum;
}AMGraph;


            浪费存储空间
        无向图的邻接矩阵表示

int CreateUDN(AMGraph &G)
{
	cin>>G.vexnum>>G.arcnum;
	for(i=0;i<G.vexnum;i++)
		cin>>G.vexs[i];
	for(i=0;i<G.vexnum;i++)
		for(j=0;j<vexnum;j++)
			arcs[i][j]=MaxInt;
	for(k=0;k<vexnum;k++)
	{
		cin>>v1>>v2>>w;
		i=LocateVex(G,v1);
		j=LocateVex(G.v2);
		G.arcs[i][j]=w;
		G.arcs[j][i]=G.arcs[i][j];
	}
	retutn 1;	
}


            主对角线对称
            顶点i的度等于第i行/列中1的个数
        有向图的邻接矩阵表示

int CreateUDN(AMGraph &G)
{
	cin>>G.vexnum>>G.arcnum;
	for(i=0;i<G.vexnum;i++)
		cin>>G.vexs[i];
	for(i=0;i<G.vexnum;i++)
		for(j=0;j<vexnum;j++)
			arcs[i][j]=MaxInt;
	for(k=0;k<vexnum;k++)
	{
		cin>>v1>>v2>>w;
		i=LocateVex(G,v1);
		j=LocateVex(G.v2);
		G.arcs[i][j]=w;
		//G.arcs[j][i]=G.arcs[i][j];只需要删去此语句即可变为有向
	}
	retutn 1;	
}


            不对称
            行:出度
            列:入度
            度:行+列
    邻接表(key)
        图的邻接表存储表示

#define MVNum 100

typedef struct ArcNode
{
	int adjvex;
	struct ArcNode *nextarc;
	OtherInfo info;
}ArcNode;

typedef struct VNode
{
	VerTexType data;
	ArcNode *firstarc;	
}VNode,AdjList[MVNum];

typedef struct {
	AdjList vertices;
	int vernum,arcnum;
}ALGraph;


            表头结点表
                数据域(顶点名称)
                链域
            边表
                邻接点域(顶点位置)
                数据域(权值)
                链域
        无向图的邻接表表示

int CreateUDG(ALGraph &G)
{
	cin>>G.vexnum>>G.arcnum;
	for(i=0;i<G.vexnum;i++)
	{
		cin>>G.veetices[i].data;
		G.vertices[i].firstarc=NULL; 
	 } 
	for(k=0;k<arcnum;k++)
	{
		cin>>v1>>v2;
		i=LocateVex(G,v1);
		j=LocateVex(G,v2);
		p1=new ArcNode;
		p1->adjvex=j;
		p1->nextarc=G.vertices[i].firstarc;
		G.vertices[i].firstarc=p1;
		p2=new ArcNode;
		p2->adjvex=i;
    	p2->nextarc=G.vertices[j].firstarc;
		G.vertices[j].firstarc=p2; 
	}
	return 1;
}


        有向图的邻接表表示?


        邻接表与逆邻接表
            邻:由顶点指出
            逆:指向该结点的点
    图的遍历
        辅助数组 Visited[N]
    搜索(key)
        深度优先 DFS
            遍历连通图?
            遍历非联通图

void DFSTraverse(Graph G)
{
	for(v=0;v<G.vexnum;v++)
		visited[v]=false;
	for(v=0;v<G.vexnum;v++)	
		if(!visited[v]) DFS(G,v);
}


            邻接矩阵表示的深度优先搜索(key)

void DFS_AM(AMGraph G,int v)
{
	cout<<v;
	visted[v]=true;
	for(w=0;w<G.vexnum;w++)
	{
		if(G.arcs[v][w]!=0 && (!visited[w]))
			DFS_AM(G,w);
	}
}


            邻接表表示的深度优先搜索?
            一条路走到头,再回退
        广度优先 BFS
            算法实现(key)

void BFS(Graph G,int v)
{
	cout<<v;
	visited[v]=true;
	InitQueue(Q);
	EnQueue(Q,v);
	while(!QueueEmpty(Q))
	{
		DeQueue(Q,v);
	    for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
		{
			if(!visited[w])
			{
				cout<<w;
    			visited[w]=true;
				EnQueue(Q,w);
			 } 	
		}
	 } 
}


            一层一层
        权重优先
    图的应用(要求会做题)
        生成树
            由邻接矩阵画DFS生成树
                类似于先序遍历
                    一条路走到头,后面有1不管,到头再回退
            由邻接矩阵画BFS生成树
                类似于层次遍历
                    一层一层
            求最小生成树
                普里姆Prim归并点,稠密
                    对起点u0,初始化closedge[]
                    选择其余n-1个顶点,生成n-1条边
                克鲁斯卡尔Kruskal归并边,稀疏
                图的最小生成树不唯一,存储结构一定才唯一
        求最短路径
            迪杰斯特拉Dijkstra(key)
                单源最短路径
                按路径长度递增次序求解
                步骤
                    初始化
                        寻找v0到v1.. vk的直达路径
                    选择
                        找出一条长度最短的路径
                    更新
                        矢量三角形化简,基于已经求得的点,用两条短的边替代长的边
                实现
                    S[]顶点在S集合就是Ture,不在False
                    D[]源点到顶点路径长度
                    path[]记录前驱,否则为-1
                 O(n^2)
            弗洛伊德Floyd
                任意两点最短路径
        拓扑排序

    二叉树的性质
        每层有2的i-1次方个结点
        至多有2的k次方-1个结点
        n0=n2+1
        完全二叉树
            n个结点,深度为[log2(n)]+1
                 [x]表示不大于x的最大整数
            对于一个结点i
                LC左孩子 2i
                RC右孩子 2i+1
                PA父亲结点 [i/2]
    二叉树的存储结构
        顺序存储

typedef MAXSIZE 100
typedef TElemType SqBiTree[MAXSIZE];
SqBiTree bt;


            完全二叉树按顺序存储
            一般二叉树空位补0
        链式存储

typedef struct BiTNode
{
	TElemType data;
	struct BiTNode *lchild,*rchild; 
}BiTNode,*BiTree;


    遍历二叉树
        先序遍历
        中序遍历
        后序遍历
        遍历的递归算法

void Traverse(BiTree T)
{
	if(T)
	{
		Traverse(T->lchild);
		cout<<T->data;//中序遍历
		Traverse(T->rchild);
	}
}


        遍历的非递归算法

void Traverse(BiTree T)

{

	InitStack(S);

	p=T;

	q=new BiTNode;

	while(p||!StackEmpty(S))

	{

		if(p)

		{

			Push(S,p);

			p=p->lchild;	

		}

		else

		{

			Pop(S,q);

			cout<<q->data;

			p=q->rchild;

		 } 	 

	}

}


        先序遍历建立二叉链表

void CreateBiTree(BiTree &T)

{

	cin>>ch;

	if(ch=='#')T=NULL;

	else

	{

		T=new BiTNode;

		T->data=ch;

		CreateBiTree(T->lchild);

		CreateBiTree(T->rchild); 

	}

}


        区别
            cout的时机不同
    线索化二叉树
        二叉线索存储表示

typedef struct BiThrNode

{

	TElemType data;

	struct BiThrNode *lchild,*rchild;

	int LTag,RTag;

}BiThrNode,*BiThrTree;


        以结点p为根的子树中序线索化
        带头节点的二叉树中序线索化
        遍历中序线索二叉树
    哈夫曼树
        基本概念
            路径
            权
            带权路径长度 WPL
        哈夫曼树的存储表示

typedef struct

{

	int weight;

	int parent,lchild,rchild;

}HTNode,*HuffmanTree;


        构造哈夫曼树(算法 课本P138)
            使权大的结点靠近根
            选取权最小的两个结点/子树,形成一颗二叉树
栈和队列
    表达式求值
链表
串,数组,广义表
    BF O(m*n)
    KMP O(m+n)
线性表 
    初始化
        new
        length=0
    取值
    查找
    插入
数据运算
    插入
    删除
    修改
    查找
    排序
排序
    ASL==ACN(平均比较次数)
    衡量好坏
        时间
            比较次数
            移动次数
            简单排序 先进排序
        空间
        稳定性
    外部排序 内部排序
    插入排序
        直接插入

void InsertSort(SqList &L)

 {int i,j;

  for(i=2;i<=L.length;++i)

    if( L.r[i].key<L.r[i-1].key)//将L.r[i]插入有序子表

      { L.r[0]=L.r[i]; // 复制为哨兵

         L.r[i]=L.r[i-1];

         for(j=i-2; L.r[0].key<L.r[j].key;--j)

            L.r[j+1]=L.r[j]; // 记录后移 

        L.r[j+1]=L.r[0]; //插入到正确位置

      }

 }



•平均情况比较次数和移动次数为n2/4

•时间复杂度为 o(n2)

•空间复杂度为 o(1)

•是一种稳定的排序方法


            最简单
            n个数做n-1趟
            最好情况
                数据基本有序,待排序序列小,效率比较高
            最坏情况
        折半插入

•在对象的初始排列已经按关键码排好序或接近有序时,直接插入排序比折半插入排序执行的关键码比较次数要少

void BInsertSort ( SqList &L )

 { for ( i = 2; i <= L.length ; ++i )

   { L.r[0] = L.r[i]; low = 1 ; high = i-1 ;

      while ( low <= high )      

         { m = ( low + high ) / 2 ; 

            if ( L.r[0].key < L.r[m]. key ) high = m -1 ;

           else low = m + 1; 

          }

      for ( j=i-1; j>=high+1; - - j ) L.r[j+1] = L.r[j];

      L.r[high+1] = L.r[0];

    }

 } // BInsertSort

•时间复杂度为 o(n2)

•空间复杂度为 o(1)

•是一种稳定的排序方法


            无需折半,数据移动次数和直接插入排序一样,只是比较次数减少(key)
            脑子里不用折半,但实际做的时候是折半的
        希尔排序

如何选择最佳d序列,目前尚未解决

最后一个增量值必须为1,无除1以外的公因子

不宜在链式存储结构上实现


            逐趟缩小增量
            类似于分块查找,分割成多个子序列,然后子序列中直接插入排序,然后对整体再进行一次直接插入排序
        交换排序
            快速排序
                先进排序
            冒泡排序
                简单排序
                    o(n2)
        快速排序

•快速排序是递归的,需要有一个栈存放每层递归调用时参数(新的low和high)。

•最大递归调用层次数与递归树的深度一致,因此,要求存储开销为 O(log2n) 。


            平均计算时间最好,是O(nlog2n)
            不稳定
            步骤
                取第一个元素存放在界点r[0]
                 小的放前,大的放后,形成左右两个子表
                对子表重新选择中心元素并递归,直到每个子表的元素只剩一个
        选择排序
            避开稳定性
                可稳定可不稳定
        堆排序

时间效率:O(nlog2n) 

n趟,每趟log2n

空间效率:O(1)

稳 定 性:不稳定

适用于n 较大的情况(先进排序)


            堆的定义
                用树的形式描述堆

                   满足一个这样的条件的完全二叉树:ki<=(或>=)k2i/k2i+1
                        大根堆
                            根结点大于左右孩子,左右孩子没有区别
                        小根堆
                    如果将序列看成一个完全二叉树,非终端结点的值均小于或大于左右子结点的值
                堆是完全二叉树的顺序存储结构
                完全二叉树中最后一个子树的根结点的标号就是n/2取下界
            筛选法调整堆 算法8.7
                调整
                建堆就是不断地调整
                建初堆
                    从最后一颗子树,反复调用筛选法
            过程一定要掌握(不要求代码)
    归并排序
        两两合并
    基数排序(不考)
        不需要关键字之间的比较
        多关键字排序
            高位优先法
            最低位优先法
                不需要分组
        链式基数排序
            知道主次关系
            知道取值范围
查找
    查找效率
        ASL
            求和:pi*ci
    顺序查找
        改进
            哨兵
            查找概率大放前面
        ASL=1/2(n+1)
        O(n)
    折半查找

•设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,k为给定值

•初始时,令low=1,high=n,mid=ë(low+high)/2û

•让k与mid指向的记录比较

–若k==R[mid].key,查找成功

–若k<R[mid].key,则high=mid-1

–若k>R[mid].key,则low=mid+1

重复上述操作,直至low>high时,查找失败

不宜用于链式结构


        非递归算法

int Search_Bin(SSTable ST,KeyType key){

//若找到,则函数值为该元素在表中的位置,否则为0

   low=1;high=ST.length;  while(low<=high){

       mid=(low+high)/2;

       if(key==ST.R[mid].key) return mid; 

       else if(key<ST.R[mid].key) high=mid-1;//前一子表查找

       else low=mid+1;                       //后一子表查找

   }    return 0; //表中不存在待查元素

}   


        递归算法

int Search_Bin (SSTable ST, keyType key, int low, int high) 

{ 

 if(low>high) return 0; //查找不到时返回0 

 mid=(low+high)/2; 

 if(key==ST.elem[mid].key) return mid; 

 else if(key>ST.elem[mid].key) return Search_Bin(ST.key,mid+1,high);

 else if(key<ST.elem[mid].key) return Search_Bin(ST.key,low,mid-1);

} 


        不超过判定树的深度 d = [log2 n]+ 1
        O(log2[n])
        ASL:(求和:比较次数*个数)/总数
        不宜用于链式结构
        折半查找判定树(不会)
        关键:mid+1或mid-1
    ASL不乱用公式
    分块查找
        折半+顺序
            性能介于两者之间
        块之间有序,块内无序
            插入删除比较容易,无需进行大量移动
            只需要移动索性表的数据
    树表的查找
        动态查找表
            表结构在查找中动态生成,如果已存在,就返回值,不存在就插入
        B-,B+树不考
        二叉排序树(key)

平均查找长度和二叉树的形态有关,即,

最好:log2n(形态匀称,与二分查找的判定树相似)

最坏: (n+1)/2(单支树)


            左子树所有小于根,右子树所有大于根
            求二叉排序树的中序遍历
                直接投影法
            常考,非常常用
            插入
                先查找,直至叶子结点为空
                二叉排序树插入的元素一定在叶子结点上
                元素要么比根小,要么比根大
                不同插入次序的元素形成不同的二叉排序树
                    影响查找的性能
                        算ASL
                            假设概率相等,比较一次有几个,两次有几个...
                    平均查找长度和二叉树的形态有关,即,

最好:log2n(形态匀称,与二分查找的判定树相似)

最坏: (n+1)/2(单支树)
            删除(知道过程就ok,代码不要求)
                缺右子树用左子女替代
                缺左子树用右子女替代
                在右子树中序遍历第一个结点替代
                左子树中序遍历最后一个结点
        平衡二叉树
            形状均衡
            左右子树深度之差的绝对值小于等于1
            左右子树都是平衡二叉树
            平衡因子:该结点左子树与右子树的高度差

任一结点的平衡因子只能取:-1、0 或 1;如果树中任意一个结点的平衡因子的绝对值大于1,则这棵二叉树就失去平衡,不再是AVL树;


            需要不断调整
                旋转
                    LL
                    RR
                    LR
                    RL
                保证是一颗二叉排序树即可
            红黑树
                二叉排序树的变种
                    要求没有那么严格,使用更广泛
        哈希表的查找
            记录存储位置与关键字之间的关系
                根据数据的特征进行查找
            根据关键字的特征构造一个哈希函数
            理想状态O(1)
            冲突
                不同的关键码映射到同一个哈希地址
            同义词
                具有相同函数值的两个关键字
            MD5
                不冲突
                不可逆
                密码学
                数字签名
            除留余数法
                Hash(key)=key mod p
                    优于其他类型函数
                如何选取合适的p
                    表长为m,取p≤m且为质数
            必考
            其余方法了解就好
            处理冲突的方法
                开放地址法
                    线性探测法
                        有冲突的时候就寻找下一个哈希地址,数组下标+1
                        Hi=(Hash[key]+di)mod m,mod m可以从最后一个单元转到开头
                        缺点:聚集,降低查找效率
                        一直往后查到空表,查找失败
                    二次探测法
                    伪随机探测法
                链地址法
                    把哈希地址相同的做成一个单链表
                    优于开地址法
            装填因子=记录数/长度,越大越容易冲突,ASL取决于装填因子
            ASL既不是O(1)也不是O(n),与装填因子有关
考试
    选择题都好好做一遍
    一定会考一个链表的算法
    理解算法
    第二章链表非常重要
        背都要背出来
    ppt里凡是有算法都考代码
    方法只考过程
    链表一定画图
    算法:先看文字描述,再数据验证,再看动画
    不会写不要空着
    根据算法步骤理解着背,不要死记硬背
    53 1234 67
    80 3.2
    第三章一般不会考代码 考选择题 90%考递归
    表达式求值要会这个表格
    特殊矩阵不考
    ppt没有的不考
    表达式求值 应用题
    二叉树递归
    概念术语性质
    写算法掌握到代码 
    去ftp下考纲

 

推荐阅读