树
哈夫曼树
题目描述
对输入的英文大写字母进行统计概率 然后构建哈夫曼树,输出是按照概率降序排序输出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)