各算法思想总结

数据结构与算法归纳总结

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 设计思想

  1. 父问题能无限划分成小问题,且子问题之间不相互影响。
  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结构性递归

  1. 类似二叉树的不断分支
  2. 先左节点,后右节点

3.2.2 过程型递归

  1. 汉诺塔问题。

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 算法思想

  1. 问题可以递归实现也可以滚动数组实现
  2. 动态规划算法注意可以求得得边界
  3. 原始问题是边界得重复迭代

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算法思想

  1. 从一点出发找到附近的节点看是否符合要求
  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 设计思想

  1. 从一个节点出发,不断向下寻找,
  2. 经过的节点标记走过
  3. 遇到无法寻找即返回。

6.3 算法模板

void dfs(状态A)
{
    if(A不合法)
        return;
    if(A为目标状态)
        输出或记录路径
    if(A不为目标状态)
        dfs(A+Δ ) 
 } 
    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/weixin_38331049/article/details/88696619
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞