五大算法思想(附走台阶问题,兔子问题,求最短路径问题,求最大上升子串长度问题,8皇后问题,http爬虫,图结构A星寻路问题源码)

五大算法思想:

一、分治思想

(1) 快排 分组排序 归并排序 二分查找

二、贪心算法/贪婪算法

1.大的问题归纳成小问题 然后迭代(如A星寻路算法)

  1. 能且只能做当前看来最优的选择 如此反复 试图得到最终最优解

缺陷
1.并非一定得到整体最优解

2.每一步都是局部最优(可能某种情况需要多个解,而其只得的一条)

简单例子:最值思想

在这里插入图片描述

求:从上到下最大和路径

可以看一下有16条路径

在这里插入图片描述

所以一条条路径计算,而在同一个地方,则是看那条路径的和更加的大。

扫描二维码关注公众号,回复: 14588600 查看本文章

比如3这里,两条路径加上3后,一条是16,另一条是19,则取19的,将16舍弃即可:

在这里插入图片描述

如果感觉递归麻烦,可以用循环,从下往上,然后还可以节省空间,用最后一行代码即可。

4种计算方法:

#include<stdio.h>

#define NUM 5
//数组
int count = 0;
int arr[NUM][NUM] = { 0 };
int maxArr[NUM][NUM] = { 0 };//临时数组
int MaxNum(int a, int b);
//初始化数组:
void initArr();
//获取最大路径
int getMaxRoad(int i, int j);
int main()
{
	initArr();
	int num=getMaxRoad(0, 0);
	printf("num:%d\n",num);
	printf("count:%d\n",count);
	while (1)
	{
	}
	return 0;
}
int MaxNum(int a, int b)
{
	return ((a>b) ? a : b);
}
void initArr()
{
	arr[0][0] = 9;
	arr[1][0] = 4; arr[1][1] = 7;
	arr[2][0] = 5; arr[2][1] = 3; arr[2][2] = 1;
	arr[3][0] = 2; arr[3][1] = 4; arr[3][2] = 4; arr[3][3] = 1;
	arr[4][0] = 7; arr[4][1] = 5; arr[4][2] = 3; arr[4][3] = 2; arr[4][4] = 4;
	for (int i = 0; i < NUM; i++)
	{
		for (int j = 0; j < NUM; j++)
		{
			maxArr[i][j] = -1;
		}
	}
}
#if 0
int getMaxRoad(int i, int j)
{
	//方法1.递归 暴力BF
	if (NUM==i)
	{
		return arr[i][j];//越界
	}
  	int n = getMaxRoad(i + 1, j);
	int m = getMaxRoad(i + 1, j+1);
	printf("n: %d \t", n);
	printf("m: %d \t", m);
	printf("arr: %d \n",arr[i][j]);
	count++;
	return arr[i][j]+MaxNum(n,m);
}//count 31次
#endif
#if 0
int getMaxRoad(int i, int j)
{
	//方法2
	if (maxArr[i][j]!=-1)
	{
		return maxArr[i][j];
	}
	count++;//count 15次
	if (NUM==i)
	{
		maxArr[i][j] = arr[i][j];
	}
	else
	{
		int n = getMaxRoad(i + 1, j);
		int m = getMaxRoad(i + 1, j + 1);

		maxArr[i][j]= arr[i][j]+MaxNum(n,m);
		return maxArr[i][j];
	}
}
#endif // 0
#if 0
int getMaxRoad(int i, int j)
{
	//方法3 循环 从下往上算
	//先给最下面一层赋值
	for (int i = 0; i < NUM; i++)
	{
		maxArr[NUM - 1][i] = arr[NUM - 1][i];
	}
	//循环一层层 加 向上赋值
	for (int i = NUM-2; i >=0; i--)
	{
		for (int j = 0; j <=i; j++)
		{
			maxArr[i][j] = arr[i][j] + MaxNum(maxArr[i + 1][j],maxArr[i + 1][j+1]);
		}
	}
	return maxArr[0][0];
}
#endif
int getMaxRoad(int i, int j)
{
	//方法4 循环 从下往上算 空间只用一行
	int temp[NUM];
	//先给最下面一层赋值
	for (int i = 0; i < NUM; i++)
	{
		temp[i] = arr[NUM - 1][i];
	}
	//循环一层层 加 向上赋值
	for (int i = NUM - 2; i >= 0; i--)
	{
		for (int j = 0; j <= i; j++)
		{
			temp[j] = arr[i][j] + MaxNum(temp[j], temp[j + 1]);
		}
	}
	return temp[0];
}

背包问题:非常经典的算法题

有一个体积为V的背包

有n种物品,体积和价值各不相同

求价值最高的组合方式

举例加代码:

#include<stdio.h>
/*
	有一个体积为V的背包
	有n种物品,体积和价值各不相同
	求价值最高的组合方式

	V:20     N:5    
			A	B	C	D	E
W(体积):	3	5	6	7	9
C(价值):	2	8	7	4	1
  现在价值最高的是32
*/
#define	V 20
#define	N 20
struct WuPin
{
	int w;//体积
	int c;//价值
};
WuPin wp[N] = { {3,2},{5,8},{6,7},{7,4},{9,1} };
int MaxNum(int a, int b)
{
	return ((a > b) ? a : b);
}

int main()
{
	int temp[256] = { 0 };//存储各种体积对应的价值
	for (int i = 0; i < N; i++)//种类搭配
	{
		for (int j = wp[i].w; j <= V; j++)//表示体积
		{
			temp[j] = MaxNum(temp[j], temp[j - wp[i].w]+wp[i].c);
		}
	}
	printf("max: %d \n", temp[V]);
	while (1)
	{
	}
	return 0;
}

迪杰斯卡拉:求最短路径

#include<stdio.h>
//代表不连通状态
#define NO 0xFFFFFF
#define MAX 100
struct graph
{
	char vetices[MAX];	//顶点数组
	int vexNum;			//顶点个数
	int arcNum;			//边条数
	int matix[MAX][MAX];//描述边的二维数组 
};
//map 图    in  起点下标   dist存放最短路径的数组
void DJSKLca(graph map, int in, int dist[]);
int main()
{
	graph map =
	{
		"12345",5,7,
		{
		{NO,10,NO,30,100},//1->2:10  1->4:30   1->5:100
		{NO,NO,50,NO,NO}, //2->3:50
		{NO,NO,NO,NO,10}, //3->5:10
		{NO,NO,20,NO,60}, //4->3:20  4->5:60
		{NO,NO,NO,NO,NO}
		}
	};
	int in = 0;
	int dist[MAX]={0};
	DJSKLca(map, in, dist);
	for (int i = 0; i < 10; i++)
	{
		printf(" %d ", dist[i]);
	}
	printf("\n");


	while (1)
	{
	}
	return 0;
}

void DJSKLca(graph map, int in, int dist[])
{
	//1.找到当前入口到其他顶点的路径
	int i = 0;
	int flag[MAX]={0};//标记成功获取的路径
	for ( i = 0; i < map.vexNum; i++)
	{
		flag[i] = 0;
		dist[i] = map.matix[in][i];//存in到i
	}
	//2.扩充顶点
	flag[in] = 1;//进来的时候标记
	dist[in] = 0;
	int min;
	int j=0;
	int k=0;
	for ( i = 1; i < map.vexNum; i++)
	{
		min = NO;
		for ( j = 1; j < map.vexNum; j++)
		{
			if (flag[j]==0 && dist[j] <min)
			{
				min = dist[j];
				k = j;
			}
		}
		flag[k] = 1;
		for (j= 1; j < map.vexNum; j++)
		{
			if (flag[j] == 0 &&(min+map.matix[k][j])< dist[j])
			{
				dist[j] = (min + map.matix[k][j]);
			}
		}
	}
	for (int i = 1; i < map.vexNum; i++)
	{
		printf("最短路径(%c,%c)=%d\n", map.vetices[in], map.vetices[i], dist[i]);
	}
}

三、动态规划(dp):

1.把一个问题分解成若干个子问题

2.将中间结果保存避免重复计算

基本步骤:

​ 1.找出最优解的性质,然后刻画结构特征(找规律)

​ 2.最优解(最好的解决方案 要自己定义) 用循环或递归

​ 3.自上而下或自下而上的方式来计算最优解(局部的

​ 4.通过最优值来构造最优解

走台阶问题:斐波那契数列

​ 有N个台阶

走法:2种

第一种:一次走一阶

第二种:一次走两阶

问:走完N个台阶共有多少种走法

假设N=10:
全一:10步 全二:5步
如:1 2 2 2 2 1
最多10步:

​ 如果N=1: 走法是2种

​ 如果N=2: 走法是3种

​ 如果N=3: 走法是5种

代码(递归和循环两种方法):
#include<iostream>
using namespace std;
int taiJie(int n);

int main()
{
	//台阶问题
	int n;
	while (1)
	{
		cout<<"请输入有多少台阶:";
		cin >>n;
		cout << n << "个台阶有"<< taiJie(n) << "种走法。"<<endl;
	}

	return 0;
}
int taiJie(int n)
{
	if (n<1)
	{
		return 0;
	}
	if (n==1 || 2==n)
	{
		return n;
	}
#if 0
	return taiJie(n - 1) + taiJie(n - 2);//递归
#else
	int temp=0;		//循环解法
	int f1 = 1;
	int f2 = 2;
	for (int i = 3; i <=n; i++)
	{
		temp = f1 + f2;
		f1 = f2;
		f2 = temp;
	}
	return temp;
#endif // 0
}

兔子问题:用斐波那契数列

一对兔子第三个月起,每个月生一对兔子,怎么变得
1 1 2 3 5 8 13
A A AB ABC ABCDE
A兔子 第三个月生B 第四个月生C 第五个月生D
B兔子 第五个月生E
知道规律后,就好做了

代码:
#include<iostream>
using namespace std;
int taiJie(int n);
int tuZi(int n);
int main()
{
    int n;
	while (1)
	{
		cout << "请输入想要知道第几个月有多少对兔子:";
		cin >> n;
		cout << n << "个月有" << tuZi(n) << "对兔子。" << endl;
	}
    return 0;
}
int tuZi(int n)
{
	if (0==n)
	{
		return 1;
	}
	if (1==n||2==n)
	{
		return n;
	}
	return tuZi(n - 1) + tuZi(n - 2);
}

这就是为什么要说走台阶算法和兔子问题的原因,因为两者是有一定关联的。

求最短路径问题:

有矩阵map

有且只有2种走法:往下或往右

求最短路径,假设是从(2,2)走到(5,5)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ahzZU778-1673165251035)(E:\学习资源\学习\数据结构\1673148939590.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mc2waekx-1673165251035)(E:\学习资源\学习\数据结构\1673149388435.png)]

代码:
#include<iostream>
using namespace std;
int map[5][5] =
{
	{0,0,0,0,0},
	{0,3,6,2,1},
	{0,4,3,0,6},
	{0,5,5,4,3},
	{0,9,7,2,8}
};
//最短路径问题
int minNum(int a, int b);
int findminRoad();
int main()
{
	cout << "最小路径值为:" << findminRoad();

	return 0;
}
int minNum(int a, int b)
{
	return ((a < b) ? a : b);
}
int findminRoad()
{
	int dpMap[5][5] = { 0 };
	for (int i = 1; i < 5; i++)//这里是地图原因写1
	{//向下走
		for (int j = 1; j < 5; j++)//向右走
		{
			if (1==i)//只有从左边来的可能
			{
				dpMap[i][j] = map[i][j] + dpMap[i][j - 1];
			}
			else if(1==j)//只有从上面来的可能
			{
				dpMap[i][j] = map[i][j] + dpMap[i-1][j];
			}
			else
			{
				dpMap[i][j] = map[i][j] + minNum(dpMap[i - 1][j],dpMap[i][j - 1]);//哪个小用哪个
			}
		}
	}
	return dpMap[4][4];
}

求最大上升子串长度问题:

一个字符串 (这里将他们搞成整数)

上升串:147 258 369

求串种最长的上升串:

1 4 7 2 5 8 3 6 9

14789 12589 12369 12569 19 189等等

要找之中子串最长的长度

解决问题的思想是什么?自下而上或自上而下,要找前面最长的长度

依次找每个点之前的最长长度

比如:pBuff里面装着整个串

则搞一个pMaxLen数组,存储从第一个字符开始,最大上升字串长度

​ 0 1 2 3 4 5 6 7 8

​ pBuff:1 4 7 2 5 8 2 6 9

​ pMaxLen:0 1 …

代码
#include<iostream>
using namespace std;
void findMaxStrLen();
int main()
{
	findMaxStrLen();


	return 0;
}
void findMaxStrLen()
{
	int n;
	cout << "请输入字符串长度";
	cin >> n;
	int* pBuff = new int[n];
	cout << "请输入字符串";
	for (int i = 0; i < n; i++)
	{
		cin >> pBuff[i];
	}
	//准备统计最大上升字符串长度的临时数组
	int* pMaxLen = new int[n];
	memset(pMaxLen, 0, sizeof(int) * n);

	pMaxLen[1] = 1;//第二个为1
	int maxPos = 1;//下标
	int nTemp;//实时记录最大上升字串长度
	for (int i = 2; i < n; i++)//从第三个开始到串末尾
	{
		nTemp = 1;
		for (int j = 1; j < i; j++)//从i前面的串取寻找
		{
			if (pBuff[j]<pBuff[i])//找到比当前数据小的数据
			{
				if (nTemp<pMaxLen[j])
				{
					nTemp = pMaxLen[j];
				}
			}
			pMaxLen[i] = nTemp + 1;
			cout << " i: " << i; 
			cout << " j: " << j;
			cout <<" 上升长度: " << pMaxLen[i]<<endl;
		}
	}
	cout << "最大上升子串的长度为:" << pMaxLen[n - 1];
}

四、动态回溯

寻路算法中的深度寻路算法,需要回退,就涉及了一部分。

动态回溯算法思想:

​ 1.针对问题来解空间:根据实际需求用不同方式描述

​ 深度寻路算法:要找到起点到终点的路径,用二维数组来描述地图

​ 2.确定易于搜索的解空间结构,并构造相应的判断函数

​ 3.用深度优先搜索的方式来解决问题,并要有合适的回溯方式(栈)

动态回溯==问题的解空间+深度优先搜索+判断结果的结构+回溯

可以解决的问题:

1.寻路 2.货物装载 3. 0-1背包问题 4…图的着色问题(涂格子) 5.电路排版问题 6.旅行者售后员问题 7.N皇后问题

8皇后问题:

​ 国际象棋的棋盘是8*8,在任意两个皇后不能互相触及的前提下有多少摆法

假设星星是皇后,红色的是她可以走的路径(另一个皇后不能触及)

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

根据这个来看问题:动态回溯==问题的解空间+深度优先搜索+判断结果的结构+回溯

问题的解空间:二维数组

深度优先搜索:递归(循环比较麻烦,摆好后还要把摆好的给删除),一个个摆,摆了n个结束

判断结果的结构:用函数判断能不能摆

回溯:栈结构实现回溯(二维数组赋值1为摆好了,0回退)

代码:

通过更改N决定棋盘大小,TEST(0/1)是否查看不同摆法

#include<iostream>
using namespace std;
#define N 8
#define TEST 1
int cnt = 0;//记录有多少种解法
void travel(int map[N][N]);//遍历棋盘
void NQueen(int j,int map[N][N]);
//判断i j 位置是否可以放皇后
bool isOK(int i, int j,int map[N][N]);
int main()
{
	int map[N][N] = { 0 };//0没有放皇后,1放了
	
	NQueen(0, map);
	cout << cnt;
	while (1)
	{

	}
	return 0;
}
void NQueen(int j, int map[N][N])//j表示摆了多少皇后
{
	if (N==j)//n个皇后摆放好了
	{
#if TEST
		travel(map);//遍历
#endif // TEST
		cnt++;
		return;
	}
	for (int i = 0; i < N; i++)//一行只能放一个皇后
	{//i表示y坐标
		if (isOK(i,j,map))
		{
			map[i][j] = 1;//能放
			NQueen(j + 1, map);//放下一个
			map[i][j] = 0;//回溯 能放就结束了,不能就回溯了
		}
	}
}
void travel(int map[N][N])
{
	cout << "-----------------------------------" << endl;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cout << map[i][j] << " ";
		}
		cout << endl;
	}
	cout << "-----------------------------------" << endl;
}
bool isOK(int i, int j,int map[N][N])
{
	int s=0;//s i 垂直 y
	int t;//t j	水平 x
	//横向判断 
	for (s=i,t=0 ; t < N; t++)
	{
		if (map[s][t]==1 && j!=t)
		{
			return false;
		}
	}
	//纵向判断
	for (t=j,s = 0; s < N; s++)
	{
		if (map[s][t] == 1 && s != i)
		{
			return false;
		}
	}
	//四个角
	//左上角
	for (t=j-1 ,s=i-1 ; s>=0 &&t>=0; s--,t--)
	{
		if (map[s][t]==1)
		{
			return false;
		}
	}
	//左下角
	for (t = j - 1, s = i + 1 ; s< N && t >= 0; s++, t--)
	{
		if (map[s][t] == 1)
		{
			return false;
		}
	}
	//右上角
	for (t = j + 1, s = i - 1 ; s >= 0 && t <N; s--, t++)
	{
		if (map[s][t] == 1)
		{
			return false;
		}
	}//右下角
	for (t = j + 1, s = i + 1 ; s < N && t <N; s++, t++)
	{
		if (map[s][t] == 1)
		{
			return false;
		}
	}
	return true;
}

五、分支定界思想

一般用来解决寻路问题(如最短路径):广度寻路,A星寻路都是这个思想的产物

常用广度优先或以最小耗费(最大收益)的优先方式搜索问题的解空间树。

在分支定界算法思想种,每一个活节点(坐标节点)只有一次机会成为扩展节点

​ 一旦成为扩展节点,就一次性生成其所有孩子节点

在这些孩子节点中,导致不可行解或非最优解的孩子节点被舍弃(剪枝

取余孩子节点被加入活节点表中(存放,记录,保存)

此后,从活节点表中获取下一节点成为当前扩展节点,并重复上述节点扩展过程

这个过程一直到找到需要的解,或活节点表为空

分支定界常用的两个数据结构:

​ 1.队列式分支定界法

​ 按照队列原则选取下一个节点成为扩展节点(广度寻路)

​ 2.优先队列分支定界法

​ 安装优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点(A星寻路)

广度寻路案例:爬虫(只能用http的爬图片,https的不行)

因为https还要去弄SSL协议解密,非常麻烦

​ 思想:

给一个网址,解析网址->连接网页的服务器->发送请求到网页服务器获取网页源码->源码中就很多的图片链接和网址链接->把网址链接存到队列中->循环从队列中一个个取出,取出后开始循环->解析网址->

图片链接也是一个道理

/*
		服务器的ip地址和服务器的端口号
		www.baidu.com
		http://www.netbian.com/s/meinvmm/
		http://					//前缀  说明了  协议类型
		www.netbian.com			//域名
		/s/meinvmm/				//路径
*/
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 4996)
#include <regex> //正则表达式头文件
#include <fstream> //文件操作头文件
#include <iostream>
#include <string>
#include <queue>//队列容器
#include <map>//解决网址重复问题
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")//加载动态库   把源代码弄过来
#include <windows.h>

using namespace std;
//网址       数据
map<string, int> g_map;//解决网址重复问题
//存放主机名
char g_zhuji[256] = { 0 };
//存放主机后的路径
char g_path[256] = { 0 };
//服务器socket
SOCKET g_socket;
//存放图片地址的容器
vector<string> photoAddr;
//存放网页地址的容器
vector<string> htmlAddr;

//爬jpg图片
void snapJpg(const char* addr);
//解析网址
void jiexiAddr(char* buff);
//连接服务器
void connectAddr();
//得到源代码
void getHtml();
//下载图片
void saveImage(string str);
//从网页源代码中获取图片链接
void getImage(string& allHtml);
//从网页源代码中获取网址链接
void getAddr(string& allHtml);
int main() {
	string begAddr;
	cout << "请输入起始网址:";
	cin >> begAddr;

	//创建一个文件夹
	CreateDirectory(".\\images", NULL);

	snapJpg(begAddr.c_str());



	while (1);
	return 0;
}
//爬jpg图片
void snapJpg(const char* addr) {
	queue<string> q;//存网址
	q.push(addr);//把起始网址放进去

	while (!q.empty()) {
		//从q中拿出一个 
		string currentAddr = q.front();
		//删掉
		q.pop();
		//解决网址重复问题
		g_map[currentAddr]++;
		//解析网址 拿到域名
		char buff[256] = { 0 };
		strcpy(buff, currentAddr.c_str());
		//解析网址
		jiexiAddr(buff);
		//连接服务器
		connectAddr();
		//得到源代码
		getHtml();
		//从源代码中解析出网址和图片地址 存入对应容器中
		//把网址存放到队列q中
		vector<string>::iterator it;
		for (it = htmlAddr.begin(); it != htmlAddr.end(); it++) {
			if (0 == g_map[*it]) {//没有连接过
				q.push(*it);//放到队列中
			}
		}
		htmlAddr.clear();//清空容器
		//下载图片
		for (it = photoAddr.begin(); it != photoAddr.end(); it++) {
			saveImage(*it);
		}
		photoAddr.clear();//清空容器
	}
}


//解析网址
void jiexiAddr(char* buff) {
	char temp[256] = { 0 };
	strcpy(temp, buff);

	//略过前面七个 http://
	char* p = strstr(buff, "http://");//buff中找子串 "http://" 找到返回子串首地址
	if (NULL == p) return;
	else
		p += 7;//往后挪七个

	sscanf(p, "%[^/]%s", g_zhuji, g_path);
	printf("主机:%s\n", g_zhuji);
	printf("路径:%s\n", g_path);
}
//连接服务器
void connectAddr() {
	//1 获取协议版本号
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 2) {
		printf("请求版本号失败!\n");
		return;
	}
	printf("请求版本号成功!\n");
	//2 创建socket
	g_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (SOCKET_ERROR == g_socket) {
		printf("创建socket失败!\n");
		return;
	}
	printf("创建socket成功!\n");
	//3 创建协议地址族
	SOCKADDR_IN addr = { 0 };
	addr.sin_family = AF_INET; //必须和socket函数第一个参数一致
	//4 绑定
	int r = bind(g_socket, (sockaddr*)&addr, sizeof addr);
	if (-1 == r) {
		printf("绑定失败!\n");
		return;
	}
	printf("绑定成功!\n");
	//5 拿到主机ip地址
	struct hostent* p = gethostbyname(g_zhuji);//192.168.0.44    ipv4
	if (NULL == p) {
		printf("获取主机地址失败!\n");
		return;
	}
	printf("获取主机地址成功!\n");

	memcpy(&addr.sin_addr, p->h_addr, 4);	//把主机地址放入协议地址族中
	addr.sin_port = htons(80);				//设置主机端口号   浏览器端口号一般为80

	//6 连接服务器
	r = connect(g_socket, (sockaddr*)&addr, sizeof addr);
	if (-1 == r) {
		printf("连接服务器失败!\n");
		return;
	}
	printf("连接服务器成功!\n");
	//7 通信:发送获取源代码请求

	//请求信息
	string reqInfo = "GET " + (string)g_path + " HTTP/1.1\r\nHost:" +
		(string)g_zhuji + "\r\nConnection:Close\r\n\r\n";
	//发送请求信息到服务器
	r = send(g_socket, reqInfo.c_str(), reqInfo.size(), NULL);
	if (r > 0) {
		printf("发送请求信息成功!\n");
	}
	else {
		printf("发送请求信息失败,失败原因:%d\n", WSAGetLastError());
	}

}
//得到源代码
void getHtml() {
	string allHtml;

	char buff[1024];
	int r;
	while (1) {
		r = recv(g_socket, buff, 1023, NULL);
		if (r > 0) {
			buff[r] = 0;
			allHtml += buff;
		}
		else {
			break;
		}
	}

	//cout << allHtml << endl;

	//从网页源代码中获取图片链接
	getImage(allHtml);
	//从网页源代码中获取网址链接
	getAddr(allHtml);
}
//下载图片
void saveImage(string str) {
	//1 解析图片地址
	char buff[256];
	memset(buff, 0, 256);
	strcpy(buff, str.c_str());

	jiexiAddr(buff);
	//2 连接服务器
	//3 发送下载图片请求
	connectAddr();
	//4 本地创建图片文件
	//4.1 得到图片文件名
	string photoName;
	photoName.resize(str.size());

	char ch;
	int j = 0;
	for (int i = 0; i < str.length(); i++) {
		ch = str[i];
		// '\0'  '\n' '\t' '\r' '\\' '\"'
		if (ch != '\\' && ch != '/' && ch != ':' && ch != '*' && ch != '?' &&
			ch != '"' && ch != '<' && ch != '>' && ch != '|') {
			photoName[j++] = ch;
		}
	}

	photoName = "./images/" + photoName.substr(0, j);

	//4.2 创建图片文件
	fstream file;
	file.open(photoName, ios::out | ios::binary);//二进制写

	//5 接收发送过来的图片信息并写入图片文件中
	int r;
	char tempBuff[1024] = { 0 };

	//排除掉文件头的"\r\n\r\n"
	r = recv(g_socket, tempBuff, 1023, NULL);
	char* p = strstr(tempBuff, "\r\n\r\n");
	file.write(p + strlen("\r\n\r\n"), r - (p - tempBuff) - strlen("\r\n\r\n"));

	while (1) {
		r = recv(g_socket, tempBuff, 1023, NULL);
		if (r > 0) {
			file.write(tempBuff, r);
		}
		else {
			break;
		}

	}
	//6 保存关闭
	file.close();

}

//从网页源代码中获取图片链接
void getImage(string& allHtml) {
	smatch mat;//用作匹配的对象
	regex pattren("src=\"(.*?\\.jpg)\"");
	string::const_iterator start = allHtml.begin();//起始位置
	string::const_iterator end = allHtml.end();//结束位置

	while (regex_search(start, end, mat, pattren)) {
		string msg(mat[1].first, mat[1].second);
		photoAddr.push_back(msg);
		cout << "找到图片:" << msg << endl;
		start = mat[0].second;//改变起始位置
	}
}
//从网页源代码中获取网址链接
void getAddr(string& allHtml) {
	smatch mat;//用作匹配的对象
	regex pattren("href=\"(http://[^\\s,\"]+)\"");
	string::const_iterator start = allHtml.begin();//起始位置
	string::const_iterator end = allHtml.end();//结束位置

	while (regex_search(start, end, mat, pattren)) {
		string msg(mat[1].first, mat[1].second);
		htmlAddr.push_back(msg);
		cout << "找到网址:" << msg << endl;
		start = mat[0].second;//改变起始位置
	}
}

A星寻路:图结构

在这里插入图片描述

代码:
#include <iostream>
#include <vector>
#include <limits>//numeric_limits
#include <queue>
using namespace std;

//节点类 
class Node {
public:
	int		index;		//序号
	int		weight;		//权重
public://功能
	//构造
	Node(int index = 0, int weight = 0) :index(index), weight(weight) {}
	//拷贝构造
	Node(const Node& object) :index(object.index), weight(object.weight) {}
	//重载小于运算符   为了比较  剪枝
	friend bool operator<(const Node& one, const Node& two) {
		return (one.weight > two.weight);
	}

	friend ostream& operator<<(ostream& o, const Node& n) {
		return o << n.index << " " << n.weight << endl;
	}
};



//路径类   节点类是边  路径类其实是很多节点相加
class Path {
public://属性  一般应该是 private 或者 protected
	int		index;		//序号
	int		weight;		//权重
public://功能
	Path() :index(0), weight(numeric_limits<int>::max()) {}
};

class shortTestPath {
public://属性
	vector<vector<int>>		graph;		//图
	int						nodeCount;	//节点统计
	const int				edge;		//起始
	const int				end;		//结束
	vector<int>				pathIndex;	//存储最短路径的容器
	int						shortPath;	//最短路径值
public://功能
	//构造函数
	shortTestPath(const vector<vector<int>>& object, int end) :edge(-1), end(end),
		nodeCount(object.size()), graph(object) {}
	//打印
	void printPath() {
		cout << "最短路径值:" << shortPath << endl;
		cout << "路径:";
		//反向打印
		copy(pathIndex.rbegin(), pathIndex.rend(),
			ostream_iterator<int>(cout, " "));
		cout << endl;
	}
	void getShortTestPath() {
		vector<Path> myPath(nodeCount);//初始化路径容器大小
		priority_queue<Node, vector<Node>> minHeap;//准备一个优先队列(小顶堆)
		minHeap.push(Node(0, 0));//入口入堆

		while (1) {
			Node top = minHeap.top();
			minHeap.pop();

			//如果是终点,结束循环
			if (top.index == end) break;

			for (int i = 0; i < nodeCount; i++) {//剪枝过程
				if (graph[top.index][i] != edge &&
					top.weight + graph[top.index][i] < myPath[i].weight) {

					cout << Node(i, top.weight + graph[top.index][i]);
					minHeap.push(Node(i, top.weight + graph[top.index][i]));
					myPath[i].index = top.index;
					myPath[i].weight = top.weight + graph[top.index][i];

				}
			}

			//最终堆空了,还没找到终点
			if (minHeap.empty()) break;
		}//end of while (1)

		//求路径和
		shortPath = myPath[end].weight;

		//求路径
		int index = end;
		pathIndex.push_back(index);
		while (1) {
			index = myPath[index].index;
			pathIndex.push_back(index);
			if (0 == index) break;
		}
	}
};

int main() {
	//准备图结构
	const int size = 11;//顶点个数
	vector<vector<int>> graph(size);
	for (int i = 0; i < size; i++) {
		graph[i].resize(size);//重置大小  开空间
	}

	//赋值
	for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++) {
			graph[i][j] = -1;
		}
	}

	graph[0][1] = 2;
	graph[0][2] = 3;
	graph[0][3] = 4;

	graph[1][2] = 3;
	graph[1][4] = 7;
	graph[1][5] = 2;

	graph[2][6] = 2;
	graph[2][5] = 9;

	graph[3][6] = 2;

	graph[4][7] = 3;
	graph[4][8] = 3;

	graph[5][6] = 1;
	graph[5][8] = 3;

	graph[6][9] = 1;
	graph[6][8] = 5;

	graph[7][10] = 3;

	graph[8][10] = 2;

	graph[9][8] = 2;
	graph[9][10] = 2;
	//创建shortTestPath对象
	shortTestPath sPath(graph, 10);

	sPath.getShortTestPath();

	sPath.printPath();

	while (1);
	return 0;
}



graph[top.index][i]));
					myPath[i].index = top.index;
					myPath[i].weight = top.weight + graph[top.index][i];

				}
			}

			//最终堆空了,还没找到终点
			if (minHeap.empty()) break;
		}//end of while (1)

		//求路径和
		shortPath = myPath[end].weight;

		//求路径
		int index = end;
		pathIndex.push_back(index);
		while (1) {
			index = myPath[index].index;
			pathIndex.push_back(index);
			if (0 == index) break;
		}
	}
	};
	int main() {
	//准备图结构
	const int size = 11;//顶点个数
	vector<vector<int>> graph(size);
	for (int i = 0; i < size; i++) {
		graph[i].resize(size);//重置大小  开空间
	}

	//赋值
	for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++) {
			graph[i][j] = -1;
		}
	}

	graph[0][1] = 2;
	graph[0][2] = 3;
	graph[0][3] = 4;

	graph[1][2] = 3;
	graph[1][4] = 7;
	graph[1][5] = 2;

	graph[2][6] = 2;
	graph[2][5] = 9;

	graph[3][6] = 2;

	graph[4][7] = 3;
	graph[4][8] = 3;

	graph[5][6] = 1;
	graph[5][8] = 3;

	graph[6][9] = 1;
	graph[6][8] = 5;

	graph[7][10] = 3;

	graph[8][10] = 2;

	graph[9][8] = 2;
	graph[9][10] = 2;
	//创建shortTestPath对象
	shortTestPath sPath(graph, 10);

	sPath.getShortTestPath();

	sPath.printPath();

	while (1);
	return 0;
	}


猜你喜欢

转载自blog.csdn.net/q244645787/article/details/128602303