数据结构笔记

哈夫曼树

题目描述
对输入的英文大写字母进行统计概率 然后构建哈夫曼树,输出是按照概率降序排序输出Huffman编码。
输入大写字母个数 n
第一个字母 第二个字母 第三个字母 … 第n个字母。
输出
字母1 出现次数 Huffman编码
字母2 出现次数 Huffman编码
字母3 出现次数 Huffman编码

字母n 出现次数 Huffman编码
样例输入 复制
10
I I U U U I U N U U
样例输出 复制
U 6 1
I 3 01
N 1 00

1.获取权重 ----每个字母出现次数  
2.定义Node data father,leftchild=0,rightchild=1  weight 
3.两者相比大权重放 右边 
4.n个字母,数组就是2n-1
5.构造哈曼夫树

定义部分

//int INT_MAX = 2147483647 ;
const int N = 100;
int n;
int a[26];
pair<int,int> mini;
Node data father,leftchild=0,rightchild=1  weight
//定义Huffman节点
struct Node{
    char data = ' ';
    int fat=0;
    int lc=0,rc=0;
    int weight=INT_MAX;
    int flag = 0;//用过的节点
    string s = "";
};
Node node[N];
//统计字母出现次数

创建哈夫曼树

void MakeHuffmanTree()
{
    //找权重最小的字母的两个下标
    for(int i = n;i < 2*n-1;i++)
    {
        Find_Min(i);//找到权重最小的两个点
        //两个孩子的父亲赋值为i
        node[mini.first].fat = i;
        node[mini.second].fat = i;
        //父亲的权重(两个孩子相加),父亲左右孩子
        node[i].weight = node[mini.first].weight + node[mini.second].weight;
        node[i].lc = mini.first;
        node[i].rc = mini.second;
    }
    //新建父亲,两个孩子复制父亲下标,孩子的父亲fat
    //父亲拥有两个孩子下标,权重较大者放在 rl,
    //赋值weight,lc,rc
     
}

统计字母出现次数

void Count()
{
    int j = 0;
    char t;
    cin >> n;
    for(int i = 0;i < n;i++)
    {
        cin >> t;
        a[t-'A']++;
    }
    for(int i = 0;i<26;i++)
    {
        if(a[i]!=0)
        {
            node[j].data = char(i+'A');
            node[j].weight = a[i];
            j++;
        }
    }  
    n = j;//不同字母个数
}

找到最小和第二个小的节点

void Find_Min(int final)
{
//  cout << "final="<<final<<node[0].flag << endl;
    int min_data = INT_MAX;
    for(int i = 0;i < final;i++)
    {
        if(node[i].flag == 0 && node[i].weight < min_data)
        {
            min_data = node[i].weight;
            mini.first = i;
        }
    }//最小
     
    min_data = INT_MAX;
    for(int i = 0;i < final;i++)
    {
        if(node[i].flag == 0 && i!=mini.first && node[i].weight < min_data)
        {
            min_data = node[i].weight;
            mini.second = i;
        }
    }//倒数第二小
    node[mini.first].flag = 1;
    node[mini.second].flag = 1;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

堆排序

大根堆(根最大)开始,将大根依次,从层序树最后开始,向前,交换根与层序树最后,直到交换完成
步骤:
(1)换大根
(2)下滤操作
在这里插入图片描述

遍历

题目
在这里插入图片描述
在这里插入图片描述

广度优先BFS
queue<int> q;//bfs
const int N = 1e4+10;
int a[N][N];//存放图
int re[N]; //标记点选择过
int n;

//广度优先
void bfs(int k)
{
//初始化所有节点未被选择过
	for(int i = 0;i < n;i ++) re[i] = 0;
	//存入节点0
	q.push(k);
	re[k] = 1;
	while(!q.empty()){
		int j = q.front(); q.pop();
		cout << j << " ";
		for(int i = 0;i < n;i ++){
			if(a[j][i] == 1 && re[i]==0){
				q.push(i);
				re[i] = 1;
			}
		}
	} 
} 
深度优先DFS
void dfs(int i){//深度优先 

	if(re[i] == 1){
		return ;
	}
	re[i] = 1;
	cout << i << " ";
	for(int j = 0;j < n;j++){
		if(a[i][j]==1 && re[j] == 0){
			dfs(j);
		}
	}
}

主函数

int main()
{
	cin >> n;
	for(int i = 0;i < n;i ++){
		for(int j = 0;j < n;j++){
			cin >> a[i][j];
		}
	}
	cout << "DFS" << endl; 
	for(int i = 0;i < n;i ++) 
	{
			//初始标记矩阵 
		for(int j = 0;j < n;j ++) re[j] = 0;	
		dfs(i);
		cout << endl;
	}
	cout << "WFS" << endl; 
	for(int i = 0;i < n;i ++){
		bfs(i);
		cout << endl;
	}
	return 0;
}

最短路径问题

在这里插入图片描述
在这里插入图片描述

广度优先BFS

在这里插入图片描述
思路:广度优先搜索,应用队列一层一层的解决,一层找完找下一层
某一层最先找到也就是这层回到起点的路径最短
在这里插入图片描述

1.输入迷宫 1为墙 0 为没走过
2.迷宫入口进入队列,标记该点走过 = 2
while(true)
{
 2.弹出队列首,从该店开始,找4个方向都看一遍,
 满足要求的(不是墙,没走过),全部入队,标记为上一层+1
 3.为出口,break;
}
4.为出口,break;
5.队列为空,没找到,return false;


//找到路径
for(层数)
{
从出口开始,找每个方向,层数刚好小于1的,
为上一条路,保存到数组中
}

//打印路径

定义部分

const int N = 1e4+10;
//迷宫 
int map[N][N];
//入口出口坐标
int inx,iny,outx,outy; 
//迷宫宽度
int n,m;
//定义节点
typedef struct node{
	int x,y;
}node;
//存放节点的队列
queue<node> q;

//路线标记

//方向标记
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1}; 

while部分

//入口入队
	node now,f;
	now.x = inx;
	now.y = iny;
	q.push(now);//入口进队
	
	while(1){
		//弹出队首 ,f表示当前点
		f = q.front();
		q.pop();
		//检查当前点四个方向
		for(int i = 0;i < 4;i++){
			now.x = f.x + dx[i];
			now.y = f.y + dy[i];//改变方向
			
			//>1,走过   ==0是墙
			if(map[now.x][now.y] > 1 || map[now.x][now.y] == 0) continue; 
			//不满足以上条件,该点入队
				q.push(now);
				//记录路线
				map[now.x][now.y] = map[f.x][f.y] + 1;
			//为出口,直接退出
			if(now.x == outx && now.y == outy)  break;
		}
		
		 //队列空,没有路,退出函数
		if(q.empty()) return false;
		 //为出口,直接退出
		if(now.x == outx && now.y == outy)  break; 
	} 

找回路线部分

	int k = map[outx][outy];
	//找回路线
	node way[k];//存放路线数组
	node cur,pp;
	pp.x = outx;
	pp.y = outy;//出口进入数组
	for(int w = k-1;w >= 2;w --){
		way[w] = pp;
		for(int i = 0;i < 4;i++){
			cur.x = pp.x + dx[i];
			cur.y = pp.y + dy[i];//改变方向 
			if(map[cur.x][cur.y] == w){
				break;//找到路 
			}
		}
		pp = cur;
	}

输出路线部分

//输出路线
	cout << inx-1 << " " << iny-1 <<endl;
	for(int i = 2;i < k;i++){
		cout << way[i].x-1 <<" " << way[i].y-1 <<endl;
	}

总体代码

bool dfs(){
	//入口入队
	node now,f;
	now.x = inx;
	now.y = iny;
	q.push(now);//入口进队
	
	while(1){
		//弹出队首 ,f表示当前点
		f = q.front();
		q.pop();
		//检查当前点四个方向
		for(int i = 0;i < 4;i++){
			now.x = f.x + dx[i];
			now.y = f.y + dy[i];//改变方向
			
			//>1,走过   ==0是墙
			if(map[now.x][now.y] > 1 || map[now.x][now.y] == 0) continue; 
			//不满足以上条件,该点入队
				q.push(now);
				//记录路线
				map[now.x][now.y] = map[f.x][f.y] + 1;
			//为出口,直接退出
			if(now.x == outx && now.y == outy)  break;
		}
		
		 //队列空,没有路,退出函数
		if(q.empty()) return false;
		 //为出口,直接退出
		if(now.x == outx && now.y == outy)  break; 
	} 
	
		int k = map[outx][outy];
	//找回路线
	node way[k];//存放路线数组
	node cur,pp;
	pp.x = outx;
	pp.y = outy;//出口进入数组
	for(int w = k-1;w >= 2;w --){
		way[w] = pp;
		for(int i = 0;i < 4;i++){
			cur.x = pp.x + dx[i];
			cur.y = pp.y + dy[i];//改变方向 
			if(map[cur.x][cur.y] == w){
				break;//找到路 
			}
		}
		pp = cur;
	}
	//输出路线
	cout << inx-1 << " " << iny-1 <<endl;
	for(int i = 2;i < k;i++){
		cout << way[i].x-1 <<" " << way[i].y-1 <<endl;
	}
	return true;
}

主函数

int main(){
	//输入迷宫 
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			cin >> map[i][j];//输入
		if(map[i][j] == 3){//入口标记
			inx = i;
			iny = j;
			map[i][j] = 2;
		}else if(map[i][j]==4){//出口标记
			outx = i;
			outy = j;
			map[i][j] = 1;
		}
		}
	}
//搜索
	dfs();

	return 0;
}
Dijkstra算法

Dijkstra算法
一个点到各个顶点的最短路径
…待补充,还没写

最小生成树

视频讲解
树:没有环,连接所有顶点,n个节点n-1条边

Kruskal算法

1.按照weight从小到大排序
2.回帖路(形成环,舍弃;没有,不舍弃)
(判断一个图是否有环)
3.直到已经选择了n-1条边

在这里插入图片描述
题目:
在这里插入图片描述
在这里插入图片描述
解题思路:
定义部分

const int N = 1e4+10;
int a[N][N];
int sel[N];
int n;

typedef struct S{
	int x,y;//边的两个顶点 
	int w;//边的权重 
}S;//存放边 

S side[N];//结构体数组,存放边和权重 

int b[N][N]; //加边时候的临时图 

主体思路

void Kruskal(){
	//按照权重排序
	int SideLen = WSort();//(1)

	//输出第一步 
		for(int k = 0;k < n;k++){
			for(int j = 0;j < n;j++){
				cout << b[k][j] << " ";
		}
		cout << endl;
      }
      cout << endl;   
	//选出n-1条边
	int si = 0,i = 0;
	while(si < n-1 ){  
		//加入边
		b[side[i].x][side[i].y] = b[side[i].y][side[i].x] = side[i].w; 			
//		判断边是否形成环 
		if(Check_Circle()){//有环 ,这条边舍弃
			b[side[i].x][side[i].y] = b[side[i].y][side[i].x] = 0;
			i++;//3.i++应该放在后面 !!!!!!!!!!!
			continue; 
		}
		//没有环,加入此边,输出步骤
		for(int k = 0;k < n;k++){
			for(int j = 0;j < n;j++){
				cout << b[k][j] << " ";
		}
		cout << endl;
	  }
	  cout << endl;
	  si++;//加入了边 
	  i++;
	} 
} 

(1)按照weight从小到大排序

//按照权重排序
int WSort(){
	//取出权重
	int k = 0;
	for(int i = 0;i < n;i++){
		for(int j = i;j<n;j++)
		{
			if(a[i][j]>0){//图中权重大于0,代表有边
				side[k].x = i;
				side[k].y = j;
				side[k].w = a[i][j];
				k++;
			}
		}
	} 

	//排序 部分-使用的冒泡排序
	for(int i = 0;i < k;i++){//1.边的个数 k 写成n 
		for(int j = 0;j < k-1-i;j++){
			if(side[j].w > side[j+1].w)
			{
				S t = side[j];
				side[j] = side[j+1];
				side[j+1] = t; 
			}
		}
 }
 	return k;//边的条数 
}

判断环(判断一个图是否有环)
无向图
在这里插入图片描述
在这里插入图片描述

//判断当前图是否有环
bool Check_Circle(){
	queue<int> q;
	//得到图  b[][]
	//初始化部分
	int re[n];//标记节点是否访问过 
	int s[n];//求出度 
	for(int i = 0;i < n;i ++) 
	{
	 re[i] = 0;
	 s[i] = 0;//初始化全部节点度为0 
	 }
	
	//求出度 
	for(int i = 0;i < n;i++){
		for(int j = 0;j < n;j++){
			if(b[i][j] > 0){//权重大于0,有边
				s[i]++;
			}
		}
	} 

 	//度<=1节点如队 
	for(int i = 0;i < n;i++){//循环遍历度数组
		if(s[i]<=1) {
			q.push(i); 
			re[i] = 1; 

		}
	}
	//队列不空
	int k;

	 while(!q.empty()){
	 		k = q.front(); q.pop();//弹出队首
	
	 //队首相邻元素节点度减1 
	 	for(int i = 0;i < n;i++){
	 		if(b[k][i] > 0){//在图中>0,k-i相邻 ,节点度减去1 
	 			s[i]--;
	 			if(s[i] == 1){//2.放错位置 
			 		q.push(i); //相邻节点度变为1,入队
			 		re[i] = 1;
			    }
			 }	 
		 }
	 }
	 
	 for(int i = 0;i < n;i++){
	 	if(re[i] == 0){//如果有节点未访问,说明图存在环
	 		return true;
		 }
	 }
	 return false;//没有环 
} 

有向图判断环
在这里插入图片描述

//判断当前图是否有环
bool Check_Circle(){
	queue<int> q;
	//得到图  b[][]
	
	//初始化 
	int re[n];//标记节点是否访问 
	int s[n];//求出度 
	for(int i = 0;i < n;i ++) {
		re[i] = 0;
	 	s[i] = 0;//初始化全部节点度为0 
	}
	
	//求出度 
	for(int i = 0;i < n;i++){
		for(int j = 0;j < n;j++){
			if(b[i][j] > 0){
				s[j]++;//入度 
			}
		}
	} 
 	//度<=1节点如队 
	for(int i = 0;i < n;i++){
		if(s[i] == 0) {//入度为0进队 
			q.push(i); 
			re[i] = 1; 
		}
	}
	//队列不空
	int k;

	 while(!q.empty()){
	 		k = q.front(); q.pop();//弹出队首
	
	 //队首相邻元素节点度减1 
	 	for(int i = 0;i < n;i++){
	 		if(b[k][i] > 0){//k->i存在指向,节点(入)度减去1 
	 			s[i]--;
	 			if(s[i] == 0){//2.放错位置 
			 		q.push(i); //入度为0,进入 
			 		re[i] = 1;
			    }
			 }	 
		 }
	 }
	 
	 for(int i = 0;i < n;i++){
	 	if(re[i] == 0){//存在没有访问过的点 
	 		return true;
		 }
	 }
	 return false;//没有环 
} 
Prim算法

在这里插入图片描述

(1)任取一点(放到已选区域)
(2)找到连接已选区域 与 未选区域 相连的边 中权值最小的边
(3)把相连的点放入已选区域
(4)重复(2)
在这里插入图片描述

void Prim(){
	int m[n][n];
	//输出最开始的部分
	for(int i = 0;i < n;i++){
		for(int j = 0;j < n;j++){
			m[i][j] = 0;
			cout << m[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;

	sel[0] = 1;//已经选0 ,放在已选区域
	//选出n-1条边 
	for(int k = 1;k < n;k ++){
		//选出下一个点 
		int minWeight = 1e5+10;
		int h1 = -1,h2 = -1;
		for(int i = 0;i < n;i ++){
			//被选过 
			if(sel[i] == 1){
				for(int j = 0;j < n;j++){
					if(a[i][j] > 0 && sel[j] == 0 && a[i][j] < minWeight){
						//有边         未被选过的点          
						minWeight = a[i][j];//存权重
						h1 = i;//存点
						h2 = j;
					}
				} 
			}
		}
		m[h1][h2] = m[h2][h1] = minWeight;//权重?步数 !!!!
		sel[h2] = 1;//标记选过(放入已选区域)
		//单步输出步骤
		 for(int i = 0;i < n;i++){
			for(int j = 0;j < n;j++){
				cout << m[i][j] << " ";
			}
			cout << endl;
	 	}
	 	cout << endl;
	}
}

拓扑排序(判断图中是否存在环)

在这里插入图片描述
(1)删除入度为0的节点
(2)删相连的边
(3)重复(1)

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值