算法
BFS广度优先算法
- 广度优先搜索解决迷宫问题
#include <iostream>
#include <queue>
using namespace std;
int a[100][100], v[100][100];//全局数组,未初始化时默认值都是0
//方向数组 搞清哪个是x轴y轴
int dx[] = { 0, 1, 0, -1 };//右下左上
int dy[] = { 1, 0, -1, 0 };//右下左上
struct point
{
int x;
int y;
int step;
};
queue<point> r;
//示例一
/*
6 5
1 1 1 1 1
1 1 1 2 1
1 1 1 1 1
1 1 1 2 1
1 1 2 1 1
1 1 1 1 2
1 1 4 3
*/
//示例二
/*
5 4
1 1 2 1
1 1 1 1
1 1 2 1
1 2 1 1
1 1 1 2
1 1 4 3
*/
int main()
{
//输入
int n, m, startx, starty, p, q;
cin >> n >> m;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> a[i][j];//1表示能通过 2表示不能通过
}
}
cin >> startx >> starty >> p >> q;//起点坐标和终点坐标
//BFS
//memset(v, 0, sizeof(v));
point start;
start.x = startx;
start.y = starty;
start.step = 0;
r.push(start);
v[startx][starty] = 1;//设置为已经访问
int flag = 0;
while (!r.empty())//只要队列不为空就继续访问
{
//将队首元素取出判断
int x = r.front().x;
int y = r.front().y;
if (x == p && y == q)
{
flag = 1;
cout << r.front().step;
break;//找到终点就break
}
for (int k = 0; k < 3; k++)
{
int tx;
int ty;
tx = x + dx[k];
ty = y + dy[k];
if (a[tx][ty] == 1 && v[tx][ty] == 0)
{
point temp;
temp.x = tx;
temp.y = ty;
temp.step = r.front().step + 1;
r.push(temp);
v[tx][ty] = 1;
}
}
r.pop();//拓展完了 需要对队首元素出队
}
if (flag == 0)
{
cout << "no answer!";
}
return 0;
}
小结:最开始用示例二的时候结果为no answer!经过调试之后发现是数组v的索引出了问题,因为数组下标是从0开始的,所以应当也要对第0行和第0列进行初始化,如示例一。
全局变量的数组默认初始化为0
,所以数组v默认都初始化为0,v数组相当于用来记录已经搜索过的位置该算法流程大致如下:先将初始位置记录下来入队(队列元素包括三要素:起点坐标x,y和步骤,所以这里定义了一个结构体,然后queue容器以该结构体为模板),循环结束的标志是队列为空或者找到该最短步骤
动态规划
- 最长回文字串
给你一个字符串 s
,找到 s
中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
代码样例
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();//记录输入串的长度
vector<vector<int>> dp(n, vector<int>(n));//理清如何用开辟一个n*n的数组
if (n < 2)
{
return s;
}
//初始化对角线的值 因为单个字符它是回文串 所以先将它们都赋值为true
for (int i = 0; i < n; i++)
{
dp[i][i] = true;
}
int begin = 0;
int length = 1;
//填表 L代表字串长度,所以先遍历所有长度为2的情况
//可知 填表方式是斜着填的
for (int L = 2; L <= n; L++)
{
//i为左边界 j为右边界
for (int i = 0; i < n; i++)
{
int j = L + i - 1;//j的表达式很关键 建立变量之间的关系的能力需要加强
//当j超过有边界的情况表明L长度的字串情况都已经遍历完
if (j >= n)
{
break;
}
//如果两边字符相等
if (s[i] == s[j])//这里是s而不是dp
{
//当长度小于3的情况下满足两边相等 就将该置为true
if (j - i + 1 <= 3)
{
dp[i][j] = true;
}
else//大于三的情况下
{
//将去掉两头的字串的值赋给当前子串
//absabd中的absa 因为bs是false 那么absa也是false
dp[i][j] = dp[i + 1][j - 1];
}
}
else//两头字符不相等 那么直接置为false
{
dp[i][j] = false;
}
//字串中只要输出第一次就行j-i+1 > length
if (dp[i][j] && j-i+1 > length)
{
begin = i;
length = j - i + 1;
}
}
}
return s.substr(begin, length);
}
};
如何用到了动态规划? absabd中的absa 因为bs是false 那么absa也是false,就是利用字串bs为false直接推出absa为false,而不用再重新判断其是否为回文串。
按照斜线的顺序填好表格
//填表 L代表字串长度,所以先遍历所有长度为2的情况
for (int L = 2; L <= n; L++)
//i为左边界 j为右边界
for (int i = 0; i < n; i++)
//j的表达式很关键 建立变量之间的关系的能力需要加强
int j = L + i - 1;`
个人认为这三行很关键,也是需要学习的点。在涉及到字串的问题的时候可以考虑
其他:
vector<vector> dp(n, vector(n));//理清如何用开辟一个n*n的数组
if (dp[i][j] && j-i+1 > length)中j-i+1 > length是让其输出第一个最长字串的关键,在这里出现过疑点
return s.substr(begin, length),string中的substr函数以s = string为例 s.substr(1,3)为tri
并查集
代码实现(递归法)
#include <iostream>
#include <vector>
using namespace std;
int v[100];
void init(int n)
{
for (int i = 1; i <= n; i++)
{
v[i] = i;
}
}
int find(int x)
{
if (v[x] == x)
{
return x;
}
else
{
v[x] = find(v[x]);//压缩路径
return v[x];
}
}
void Union(int i, int j)
{
int f_i = find(i);
int f_j = find(j);
v[f_i] = f_j;
}
int main()
{
int n, m, q;
int x, y;
cin >> n >> m;
init(n);
for (int i = 0; i < m; i++)
{
cin >> x >> y;
Union(x, y);
}
cin >> q;
for (int i = 1; i <= q; i++)
{
cin >> x >> y;
if (find(x) == find(y))
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
/*
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
*/
书上给出的实现(循环法,初始化方式也不同)
#include <iostream>
using namespace std;
struct ElemetType
{
char data;//假定并查集的元素为字符
int parent;//游标,该元素的双亲在数组中的下标
};
const int MaxSize = 100;
class UnionFind
{
public:
UnionFind(char ch[], int n);//每个元素构成一个子集
~UnionFind();
int Find(char x);//查找元素x所在子树的根结点
void Union(char x, char y);//合并元素x和y所在子集
private:
ElemetType elem[MaxSize];
int length;//集合的元素个数
};
UnionFind::UnionFind(char ch[], int n)
{
for (int i = 0; i < n; i++)
{
elem[i].data = ch[i];
elem[i].parent = -1;
}
length = n;
}
UnionFind::~UnionFind()
{
}
int UnionFind::Find(char x)
{
int i;
for (i = 0; i < length; i++)
{
if (elem[i].data == x)
{
break;
}
}
while (elem[i].parent != -1)
{
i = elem[i].parent;//向上找祖先
}
return i;//返回下标
}
void UnionFind::Union(char x, char y)//将y连接到x上
{
int vex1 = Find(x);
int vex2 = Find(y);
if (vex1 != vex2)
{
elem[vex2].parent = vex1;
}
}
int main()
{
char str[6] = { 'a', 'b', 'c', 'd', 'e', 'f' };
int flag[6];
UnionFind UF{ str, 6 };
UF.Union('a', 'b');
UF.Union('a', 'c');
UF.Union('d', 'e');
for (int i = 0; i < 6; i++)
{
flag[i] = UF.Find(str[i]);
cout << flag[i] << ":" << str[i] << endl;
}
return 0;
}
这表示元素a、b和c属于同一个集合,根节点为0;元素d和e属于同一个集合,根节点为3;元素f自己独立成为一个集合,根节点为5。
一维前缀和
输入一个长度为n的整数序列。
接下来再输入m个询问,每个询问输入一对l, r。
对于每个询问,输出原序列中从第l个数到第r个数的和。
输入格式
第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。
输出格式
共m行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
代码实现
#include <iostream>
#include <vector>
using namespace std;
int a[100], s[100];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
int temp;
cin >> temp;
a[i] = temp;
}
for (int j = 1; j <= n; j++)
{
s[j] = s[j - 1] + a[j];//初始化前缀和
}
for (int k = 0; k < m; k++)
{
int r, l;
cin >> r >> l;
cout << s[l] - s[r - 1] << endl;
}
return 0;
}
二分查找
704.二分查找
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
- 你可以假设
nums
中的所有元素是不重复的。 n
将在[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size()-1 , middle = (left+right)/2;
while(left <= right)
{
if(nums[middle] < target)
{
left = middle + 1;
middle = (right+left)/2;
}
else if(nums[middle] > target)
{
right = middle - 1;
middle = (right+left)/2;
}
else
{
return middle;
}
}
return -1;
}
};