数据结构与算法归纳总结
1.枚举
1.1 例题: 求24
/* 递归边界:只有一个数判断是否等于24,等于24不能用==判断 只能用<某个极限 两层遍历一个选取两个数将剩下的数放在一个数组中B[] 将前两个数运算的集合放在b数组的末尾 提取的两个数有 a[i]+a[j] a[i] - a[j] a[j] - a [i] a[i]/a[j] a[j] /a[i] a[i]*a[j] 任意情况成立则 总成立 所有情况不成立则不成立 */
#include<iostream>
#include<cmath>
using namespace std;
double a[5];
#define EPS 1e-6
bool isZero(double x)
{
return fabs(x) <= EPS;
}
bool count24(double a[], int n)
{
if( n == 1) //边界条件
if(isZero(a[0] - 24))
return true;
else
return false;
double b[5];
for(int i = 0;i < n-1;i++ ) //a[i] 和a[j] 用来运算的两个数
{
for(int j = i+1; j < n; ++j)
{
int m = 0; // 还剩m个数 m = n-2;
for( int k = 0; k < n; k++)
if(k != i && k != j) //枚举把其余数放入b中
b[m++] = a[k];
b[m] = a[i] + a[j];
if(count24(b,m+1))
return true;
b[m] = a[i]-a[j];
if(count24(b,m+1))
return true;
b[m] = a[j] - a[i] ;
if(count24(b,m+1))
return true;
b[m] = a[i]*a[j];
if(count24(b,m+1))
return true;
if(!isZero(a[j]))
{
b[m] = a[i] / a[j];
if(count24(b,m+1))
return true;
}
if(!isZero(a[i]))
{
b[m] = a[j]/a[i];
if(count24(b,m+1))
return true;
}
}
return false;
}
}
int main()
{
for(int i = 0; i < 4; i++)
{
cin >> a[i];
}
if(count24(a,4))
cout << "true" ;
else
cout << "false";
}
1.2 设计思想
1.将问题所有的可能性一一列举
2.保留合适的,排除不合适的。
PS:列举所有的数字组合情况,然后进行判断。
1.3 算法模板
循环
情况: <S>
for(x:S)
{
if judge(x) == true;
save x;
else
no do;
}
递归遍历情况
void f()
{
if(S.empty())
return ;
取出S中的一个x
if(judge(x))
save
else
f(x+1);
}
2.分治
2.1 例题 快速排序
#include<iostream>
#include<algorithm>
using namespace std;
void quick_sort(int a[], int s, int e)
{
if(s >= e)
return ;
int k = a[s];
int i = s,j = e;
while(i != j)
{
while(j > i && a[j] >= k)
--j;
swap(a[i],a[j]);
while(i < j && a[i] <= k)
++i;
swap(a[i],a[j]);
}
quick_sort(a,s,i-1);
quick_sort(a,i+1,e);
}
int main()
{
int a[8]={4,5,2,1,8,9,6};
quick_sort(a,0,8);
for(int i = 0;i < 8; i++)
cout << a[i];
}
/* 7 8 5 6 4 1 2 3 9 */
2.2 设计思想
- 父问题能无限划分成小问题,且子问题之间不相互影响。
- 所有子问题的解集合能得到父问题的解
在上述快速排序中,每一个元素的左边小于自己,右边大于自己即排序完成。
2.3 算法模板
begin {分治过程开始}
if ①问题不可分 then ②返回问题解
else begin
③从原问题中划出含一半运算对象的子问题1;
④递归调用分治法过程,求出解1;
⑤从原问题中划出含另一半运算对象的子问题2;
⑥递归调用分治法过程,求出解2;
⑦将解1、解2组合成整修问题的解;
end;
end; {结束}
3.递归
3.1 题目 N皇后问题
#include<iostream>
#include<cmath>
#include<stdio.h>
using namespace std;
int room[9][9],lie[9],sum = 0; //data
void fun(int t) //t hang find lie
{
if(t == 9)
{
int SUM = 0;
for(int i =1;i < 9;i++)
SUM += room[i][lie[i]];
if(SUM > sum)
sum = SUM;
return ;
}
int i;
for(i = 1;i <= 8;i++)
{
int j;
for(j = 1;j < t;j++)
{
if(i == lie[j]||abs(lie[j] - i) == abs(j - t))
break;
}
if(t == j)
{
lie[t] = i;
fun(t+1);
}
}
}
int main()
{
freopen("input.txt","r",stdin);
for(int i = 1;i < 9;i++)
for(int j = 1;j < 9;j++)
cin >> room[i][j];
fun(1);
cout << sum << endl;
}
3.2 算法思想
3.2.1结构性递归
- 类似二叉树的不断分支
- 先左节点,后右节点
3.2.2 过程型递归
- 汉诺塔问题。
3.2.3 总结
不论结构型递归还是过程性递归,都必须要抛弃程序的细节,只关注程序的功能
3.3 模板
过程性递归
void fun(int n)
{
if(达到出口的条件)
{
判断是否符合要求
return;
}
fun(n+1);
}
结构型递归
void dfs()
{
if(达到根节点返回)
dfs(左边);
dfs(右边);
deal(自己)
}
4.动态规划
4.1 案例 未命名湖畔的烦恼
#include<stdio.h>
typedef long long ll;
using namespace std;
int f(int m,int n)
{
if(m < n)
return 0;
if(n == 0)
return 1;
return f(m-1,n) + f(m,n-1);
}
int main()
{
int m,n;
while(scanf("%d%d",&m,&n) != EOF)
{
int sum = f(m,n);
printf("%d\n",sum);
}
return 0;
}
4.2 算法思想
- 问题可以递归实现也可以滚动数组实现
- 动态规划算法注意可以求得得边界
- 原始问题是边界得重复迭代
4.3 算法模板
递归实现
int f(int m,int n)
{
if(m == 0)
return 0;
if(n == 0)
return 1;
return f(m-1,n) + f(m,n-1);
}
滚动数组实现
a[N][N]
for(i->N)
a[i][0]=num;
a[0][i]=num;
for(i:N)
{
for(j:N)
{
a[i][j] = max(a[i-1][j],a[i][j-1]);
}
}
5.广度优先搜索
5.1 案例 学霸的迷宫
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
struct pos{
int x, y;
int num;
};
int n, m;
char map[550][550];
int vis[550][550];
int dir[4][2] = {1, 0, 0, -1, 0, 1, -1, 0};
int pre[550][550];
char str[5] = {"DLRU"};
void bfs() {
queue <pos> q;
pos t;
t.x = 1, t.y = 1, t.num = 0;
q.push(t);
vis[1][1] = 1;
pre[1][1] = -1;
while(!q.empty()) {
t = q.front();
q.pop();
if(t.x == n && t.y == m) {
printf("%d\n", t.num);
return ;
}
int i, j;
pos tt;
for(i = 0; i < 4; i++) {
tt.x = t.x + dir[i][0];
tt.y = t.y + dir[i][1];
tt.num = t.num + 1;
if(tt.x >= 1 && tt.x <= n && tt.y >= 1 && tt.y <= m && map[tt.x][tt.y] == '0' && vis[tt.x][tt.y] == 0) {
pre[tt.x][tt.y] = i;
q.push(tt);
vis[tt.x][tt.y] = 1;
}
}
}
}
void path(int x, int y) {
if(x == 1 && y == 1) return;
path(x - dir[pre[x][y]][0], y - dir[pre[x][y]][1]);
printf("%c", str[pre[x][y]]);
}
int main () {
//freopen("input.txt", "r", stdin);
//freopen("output.txt", "w", stdout);
scanf("%d %d", &n, &m);
getchar();
int i, j;
for(i = 1; i <= n; i++) {
for(j = 1; j <= m; j++) {
scanf("%c", &map[i][j]);
}
getchar();
}
bfs();
path(n, m);
return 0;
}
5.2算法思想
- 从一点出发找到附近的节点看是否符合要求
- 然后从相邻的节点出发,继续BFS
PS:从起始位置出发,一步一步搜索,每步标记自己从哪里走过来的。
5.3 算法模板
q.push(head);
while(!q.empty())
{
temp=q.front();
q.pop();
if(tempÎ为目标状态)
输出或记录
if(temp不合法 )
continue;
if(temp合法)
q.push(temp+¦Δ );
}
6. 深度优先搜索
6.1 案例 N皇后
#include<iostream>
#include<cmath>
#include<stdio.h>
using namespace std;
int room[9][9],lie[9],sum = 0; //data
void fun(int t) //t hang find lie
{
if(t == 9)
{
int SUM = 0;
for(int i =1;i < 9;i++)
SUM += room[i][lie[i]];
if(SUM > sum)
sum = SUM;
return ;
}
int i;
for(i = 1;i <= 8;i++)
{
int j;
for(j = 1;j < t;j++)
{
if(i == lie[j]||abs(lie[j] - i) == abs(j - t))
break;
}
if(t == j)
{
lie[t] = i;
fun(t+1);
}
}
}
int main()
{
freopen("input.txt","r",stdin);
for(int i = 1;i < 9;i++)
for(int j = 1;j < 9;j++)
cin >> room[i][j];
fun(1);
cout << sum << endl;
}
6.2 设计思想
- 从一个节点出发,不断向下寻找,
- 经过的节点标记走过
- 遇到无法寻找即返回。
6.3 算法模板
void dfs(状态A)
{
if(A不合法)
return;
if(A为目标状态)
输出或记录路径
if(A不为目标状态)
dfs(A+Δ )
}