算法学习笔记

1.拓扑排序

一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity)。在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。

拓扑排序:对于有向无环图,拓扑排序就是包含图中所有节点的一个线性序列,该序列需满足条件:如图中存在由节点u指向节点v的有向边,则拓扑序列中u一定出现在v的前面。

在这里插入图片描述
拓扑排序方法:
找到图中节点入度为0的节点,该节点加入到输入,去掉该节点的出边后重复步骤,当输入序列获得图中所有节点时,拓扑排序完成。
因为同时存在多个节点入度为0,拓扑序列不是唯一的。

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

void getInDegree(vector<int> &inDegree,int **matrix,int dim)
{
    for (int i = 0; i < dim; i++)
    {
        int cnt = 0;
        for (int j = i; j < dim*dim; j=j+dim)
        {
            if (matrix[j] != 0)
            {
                cnt++;
            }
        }
        inDegree.push_back(cnt);
    }
}

void topoLogic(int **matrix, vector<int> inDegree, queue<int> &outQ,int dim)
{
    int cnt = 0;
    queue<int>temp;
    for (int i = 0; i < dim; i++)
    {
        if (inDegree[i] == 0)
        {
            temp.push(i);
        }
    }  
    while (!temp.empty())
    {
        int cur = temp.front();
        for (int i = 0; i < dim; i++)
        {
            if (matrix[cur*dim + i] != 0)
            {
                inDegree.at(i)--;
                if (inDegree.at(i) == 0)
                {
                    temp.push(i);
                }
            }
        }
        outQ.push(temp.front());
        temp.pop();
    }
      
}

int main()
{
    int graph[7][7]={
    {0,1,1,0,0,0,0},
    {0,0,0,1,1,0,0},
    {0,1,0,0,1,1,0},
    {0,0,0,0,0,0,1},
    {0,0,0,0,0,0,1},
    {0,0,0,0,0,0,1},
    {0,0,0,0,0,0,0} };

    int dim = sizeof(graph[0])/sizeof(int);
    vector<int>inDegree;
    getInDegree(inDegree,(int**)graph,dim);
    queue<int>outQ;
    topoLogic((int**)graph, inDegree, outQ,dim);
    int size= outQ.size();
    for (int i = 0; i < size; i++)
    {
        cout << outQ.front() << endl;
        outQ.pop();
    }
}

运行成功

2.BFS

Breadth First Search,广度优先搜索算法,遍历图的所有顶点。
算了 不想写了 没什么用 就留个代码在这里吧

用bfs搜索迷宫所有可能走的路径,输出走出迷宫需要的最长时间:

#include<iostream>
#include<queue>
using namespace std;

struct coor {
    int x, y;
    coor(int x1, int y2)
    {
        x = x1;
        y = y2;
    }
};
int maze[50][50];//地图,
bool mark[50][50];//标记已走过的路径
queue<coor>q;

int go[4][2] = {//2:二维xy,4:当前位置下一步可到达的四个位置
    {0,1},
    {0,-1},
    {1,0},
    {-1,0},
};

int BFS(int a, int b)
{
    int time=0;
    while (!q.empty())
    {
        coor now = q.front();
        q.pop();
        for (int i = 0; i < 4; i++)
        {
            int nx = now.x + go[i][0];
            int ny = now.y + go[i][1]; 
            if (nx < 0 || nx >= a || ny < 0 || ny >= b)
            {
                continue;
            }
            if (maze[nx][ny] == 1)
            {
                continue;
            }
            if (mark[nx][ny] == true)
            {
                continue;
            }
            q.push(coor(nx, ny));
            mark[nx][ny] = true;
            time++;
            if (nx == a - 1 && ny == b - 1)
            {
                return time;
            }
        }
    }
    return - 1;
}

int main() 
{
    int a, b;
    int k;
    cout << "请输入行" << endl;
    cin >> a;
    cout << "请输入列" << endl;
    cin >> b;
    cout << "请输入元素:" << endl;
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < b; j++)
        {           
            cin >> k;
            maze[i][j] = k;
            mark[i][j] = false;
        }
        cout << endl;
    }
    coor n(0, 0);
    q.push(n);
    int time = BFS(a, b);
    if (time == -1)
    {
        cout << "failed" << endl;
    }
    else
    {
        cout << "time is" << time << endl;
    }
}

运行成功

3.最小生成树
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 得到的最小生成树边的权值最小

Prim算法简述
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew= {x},其中x为集合V中的任一节点(起始点),Enew= {},为空;
3).重复下列操作,直到Vnew= V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。

Kruskal算法简述
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

prim算法:

#include<iostream>
#include<vector>
using namespace std;

int graph[100][100];

void prim(int a,vector<int> &nodeV,vector<pair<int,int>> &edgeV)
{
    nodeV.push_back(0);
    int minEdge = graph[0][1];
    int head, rear;
    while (nodeV.size() < a)
    {
        minEdge = 9999;
        for (vector<int>::iterator iter=nodeV.begin(); iter != nodeV.end(); iter++)
        {   
            int i = *iter;
            for (int j = 0; j < a; j++)
            {
                if (find(nodeV.begin(), nodeV.end(), j) != nodeV.end())
                {
                    continue;
                }
                if (graph[i][j] <= minEdge)
                {
                    minEdge = graph[i][j];
                    head = i;
                    rear = j;
                }
            }
        }
        pair<int, int> temp(head, rear);
        edgeV.push_back(temp);
        nodeV.push_back(rear);
    }
}

int main()
{
    int a, b, k;
    cout << "请输入顶点的数目:" << endl;
    cin >> a;
    cout << "请输入边的数目:" << endl;
    cin >> b;
    for (int i = 0; i < a; i++)
    {
        for (int j = i; j < a; j++)
        {
            graph[i][j] = 9999;
            graph[j][i] = 9999;
        }
    }
    cout << "请输入各边的端点及权值:" << endl;
    for (int i = 0; i < a; i++)
    {
        for (int j = i+1; j < a; j++)
        {
            cin >>i>>j>> k;
            graph[i][j] = k;
            graph[j][i] = k;
            cout << endl;
        }
    }
    pair<int, int>edge;
    vector<int>nodeVec;
    vector<pair<int, int>>edgeVec;
    prim(a, nodeVec, edgeVec);
    cout << "node sepuence is:";
    for (vector<int>::iterator iter = nodeVec.begin(); iter != nodeVec.end(); iter++)
    {
        cout << *iter << ",";
    }
    cout << "edge s:";
    for (vector<pair<int, int>>::iterator iter = edgeVec.begin(); iter != edgeVec.end(); iter++)
    {
        cout << "<" << (*iter).first << "," << (*iter).second << ">" << ",";
    }
}

运行结果

DFS深度优先搜索:
dfs搜索图得到权值最小生成树:

#include<iostream>
#include<stack>
#include<vector>
using namespace std;

int  graph[100][100];

void dfs(int a,vector<int> &nodeV,vector<pair<int,int>> &edgeV)
{
    nodeV.push_back(0);
    stack<int>Nstack;
    Nstack.push(0);   
    while (nodeV.size() < a && !Nstack.empty())
    {
        int minEdge = 9999;
        int top = Nstack.top();
        //Nstack.pop();
        int next=9999;
        pair<int, int>temp;
        for (int i = 0; i < a; i++)
        {
            if (find(nodeV.begin(), nodeV.end(), i) != nodeV.end())
            {
                continue;
            }
            if (graph[top][i]<minEdge)
            {
                minEdge = graph[top][i];
                next = i;  
            }
        }
        if (next != 9999)
        {
            Nstack.push(next);
            nodeV.push_back(next);
            temp = pair<int, int>(top, next);
            edgeV.push_back(temp);
        }
        else
        {
            Nstack.pop();
        }
               
    }   
}


int main()
{
    int a, b;
    cout << "请输入节点数:"<<endl;
    cin >> a;
    cout << "请输入边数:"<<endl;
    cin >> b;
    for (int i = 0; i < a; i++)
    {
        for (int j = i; j < a; j++)
        {
            graph[i][j] = 9999;
            graph[j][i] = 9999;
        }
    }
    cout << "请输入两个节点及边的权值:"<<endl;
    int k;
    int end;
    int x, y;
    for (int i = 0; i < b; i++)
    {
        cin >> x >> y >> k;
        graph[x][y] = k;
        graph[y][x] = k;
        cout << endl;
    }
    vector<int>nodeVec;
    vector<pair<int, int>>edgeVec;
    dfs(a, nodeVec, edgeVec);
    cout << "节点序列为:"<<endl;
    for (vector<int>::iterator iter=nodeVec.begin(); iter!=nodeVec.end(); iter++)
    {
        cout << (*iter) << ",";
    }
    cout << "边序列为:" << endl;
    for (vector<pair<int, int>>::iterator iter=edgeVec.begin(); iter != edgeVec.end(); iter++)
    {
        cout << "<" << (*iter).first << "," << (*iter).second << ">" << ",";
    }
}

运行结果

二分查找:

#include<iostream>
#include<vector>

using namespace std;

int main()
{
    vector<int>vec;
    int x = 0;
    for (int i = 1; i < 101; i++)
    {
        vec.push_back(i);
    }
    int key;
    cout << "请输入查找数:" << endl;
    cin >> key;
    int low = 0;
    int high = vec.size() - 1;
    int mid;
    int time = 0;
    while (low <= high)
    {
        time++;
        mid = low + (high - low) / 2;
        if (key < vec[mid])
        {
            high = mid - 1;

        }
        else if (key > vec[mid])
        {
            low = mid + 1;//判断死循环问题,如果low=mid:0123456  3456  456 56  56 56  死循环。
        }
        else
        {
            cout << "找到!" << key << "的下标为" << mid;
            cout << "查找次数" << time;
            return 0;
        }
    }
    cout << "没找到" << endl;
    cout << "查找次数" << time;  
}

运行结果
运行结果
运行结果

并查集的亲戚查找问题:
相邻两个节点是亲戚,我亲戚的亲戚也是我的亲戚,只要能连通就是亲戚。查找两个是否是亲戚时只需要查找是否能向上找到同一个顶层祖宗节点。
fa[]存储每个节点的父节点,初始时fa[]存储自己,合并以后被合并的节点存储其合并上的父节点。
depth[]存储每个节点的深度。应该让较小深度的树加到较大深度的树上去,这样让树的深度增加得没有那么快,查找的时候才会快。
当两个节点能找到同一个顶层祖宗节点时,两个节点就是亲戚

#include<iostream>
using namespace std;

#define MaxN 1000
int fa[MaxN],depth[MaxN];


void init(int n)
{
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        depth[i] = 1;
    }
}

int find(int x)
{
    if (fa[x] == x)
    {
        return x;
    }
    else
    {
        return find(fa[x]);
    }
}


void merge(int a,int b)
{
    int aTop = fa[a];
    int bTop = fa[b];
    int aDeep = depth[aTop];
    int bDeep = depth[bTop];
    if (aDeep <= bDeep)
    {
        fa[a] = find(b);
        if (aDeep == bDeep)
        {
            depth[b]++;
        }
    }
    else
    {
        fa[b] = find(a);
    }
}
int main()
{
    cout << "请输入人数:" << endl;
    int n;
    cin >> n;
    init(n);
    cout << "总共有多少对亲戚关系:" << endl;
    int m;
    cin >> m;
    cout << "需询问多少对关系:" << endl;
    int p;
    cin >> p;
    cout << "请输入存在亲戚关系的两个个体:" << endl;
    for (int i = 0; i < m; i++)
    {
        int g, h;
        cin >> g >> h;
        merge(g, h);
        cout << endl;
    }
    cout << "需查询的亲戚对:" << endl;
    for (int i = 0; i < p; i++)
    {
        int g, h;
        cin >> g >> h;
        if (find(g) == find(h))
        {
            cout << "YES" << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }
}

运行结果

滑动窗口算法:
在给整数数组中找到长度为k的连续子数组中元素和的最大值,长度为k的窗口,在窗口两端分别放置两个指针,每次窗口向右滑动一格,只需减去最左边的一个元素值加上最右边的一个元素值即得到新窗口和值

#include<iostream>
#include<string>
using namespace std;

int arr[1000];

int SWMax(int n,int k)
{
    int sum = 0;
    for (int i = 0; i < k; i++)
    {
        sum += arr[i];
    }
    for (int i=0,j = k-1; j < n; i++,j++)
    {
        int oldLeft = arr[i];
        int newRight = arr[j + 1];
        int temp = sum + newRight - oldLeft;
        if (temp > sum)
        {
            sum = temp;
        }
    }
    return sum;   
}
int main()
{
    cout << "请输入数组长度" << endl;
    int n;
    cin >> n;
    cout << "请输入元素:" << endl;
    for (int i = 0; i < n; i++)
    {
        int k;
        cin >> k;
        arr[i] = k;
    }
    cout << "请输入窗口长度:" << endl;
    int k;
    cin >> k;
    int sum=SWMax(n, k);
    cout << "窗口中元素和的最大值为:" << sum << endl;
}

运行结果

用单调栈实现直方图上最大矩形面积:
以当前小矩形的高(直方图的纵左边)作为可能的最大矩形的宽,需要找到每个小矩形能往两边延伸的长度,该长度就是可能的最大矩形的宽。
向右遍历每个小矩形,通过单调递增栈可以找到右边第一个小于当前高度的位置,该位置也就是当前高度的矩形可向右延伸的最大长度
向左遍历每个小矩形,通过单调递增栈可得到左边第一个小于当前高度的位置。

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
vector<int> elem;
vector<int> getRight(int n)
{
    vector<int> ret(n,n);
    stack<int> stk;
    for (int i = 0; i <n; i++)
    {
        int j = i;
        while (!stk.empty() && stk.top() > elem[i])
        {
            j--;
            ret[j] = i;
            stk.pop();
        }
        stk.push(elem[i]);
    }
    return ret;
}

vector<int> getLeft(int n)
{
    vector<int> ret(n,-1);
    stack<int> stk;
    for (int i = n-1; i >= 0; i--)
    {
        int j = i;
        while (!stk.empty() && stk.top() > elem[i])
        {
            j++;
            ret[j] = i;
            stk.pop();
        }
        stk.push(elem[i]);
    }
    return ret;
}

int main()
{
    vector<int> vec;
    int n;
    cout << "请输入矩阵块数:" << endl;
    cin >> n;
    cout << "请输入各块高度" << endl;
    for (int i = 0; i < n; i++)
    {
        int k;
        cin >> k;
        elem.push_back(k);
    }
    vector<int> right = getRight(n);
    vector<int> left = getLeft(n);
    int max = 0;
    for (int i = 0; i < n; i++)
    {
        int temp = (right[i] - left[i]-1)*elem[i];
        if (temp > max)
        {
            max = temp;
        }
    }
    cout << "最大面积为:" << endl;
    cout << max;
}

运行结果

单调队列实现:在给定队列上滑动给定长度的窗口,求划过窗口中的最大值序列:

必须是双端队列deque,窗口向右移动,队列头出队是因为队列头节点已经不在当前窗口中,队列尾出队是因为新的节点比队列尾节点更大,队列尾就没有作为最大的资格了,所以出队。

#include<iostream>
#include<deque>
#include<vector>
using namespace std;

int arr[100];

vector<int> findMin(int n,int k)
{
    deque<int>deq;
    vector<int>ret(n - k + 1);
    for (int i = 0; i < n; i++)
    {
        while (!deq.empty() && arr[deq.front()] > arr[i])
        {
            deq.pop_back();
        }
        deq.push_back(i);
        if (i >= k - 1)
        {
            ret[i - k + 1] = deq.front();
            if (deq.front() <= i - k + 1)
            {
                deq.pop_front();
            }
        }

    }
    return ret;   
}

int main()
{
    cout << "请输入队列长度:" << endl;
    int n;
    cin >> n;
    cout << "请输入窗口长度:" << endl;
    int k;
    cin >> k;
    cout << "请输入队列元素:" << endl;
    int x;
    for (int i = 0; i < n; i++)
    {
        cin >> x;
        arr[i] = x;
    }
    vector<int> vec = findMin(n, k);   
    cout << "队列为:" << "<";
    for (vector<int>::iterator iter = vec.begin();iter!=vec.end();iter++)
    {
        cout <<  arr[*iter]<< " ";
    }
    cout << ">" << endl;
}

运行结果

遍历二叉树:
写了中序遍历和前序遍历:
输入二叉树存储在a[]中,按完全二叉树编号,因为只想考虑遍历算法,为了方便,给它补成满二叉树后又补了一层,后面就不用考虑数组越界的问题了。

#include<iostream>
#include<stack>
#include<string>
#include<vector>
using namespace std;
struct treeNode
{
    char data;
    treeNode *leftNode;
    treeNode *rightNode;
    treeNode(char c) :data(c), leftNode(nullptr), rightNode(nullptr) {}
};

char a[32] = { ' ','A','B','D','0','C','0','E','0','0','0','0','0','0','F','0',
                '0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};

void preOrderTraval(treeNode c,vector<treeNode> &ret)
{
    stack<treeNode>stk;
    ret.push_back(c);
    stk.push(c);  
    while (!stk.empty())
    {
        treeNode now = stk.top();
        stk.pop();
        if (now.leftNode != nullptr)
        {
            preOrderTraval(*(now.leftNode), ret);
        }
        if (now.rightNode != nullptr)
        {
            preOrderTraval(*(now.rightNode), ret);
        }
        return;
    }
    
}

void inOrderTraval(treeNode c, vector<treeNode> &ret)
{
    stack<treeNode>stk;
    stk.push(c);
    while (!stk.empty())
    {
        treeNode now = stk.top();
        if (now.leftNode != nullptr)
        {
            inOrderTraval(*(now.leftNode), ret);
        }        
        ret.push_back(now);
        if (now.rightNode != nullptr)
        {
            inOrderTraval(*(now.rightNode), ret);
        }
        stk.pop();
        return;
    }
}
void createT(treeNode *nowNode,int i)
{
    treeNode *lNode = new treeNode(a[2 * i]);
    treeNode *rNode = new treeNode(a[2 * i + 1]);
    if (lNode->data != '0')
    {
        nowNode->leftNode = lNode;
        createT(lNode, 2 * i);
    }
    if (rNode->data != '0')
    {
        nowNode->rightNode = rNode;
        createT(rNode, 2 * i+1);
    }
    if (lNode->data == '0' && rNode->data == '0')
    {
        return;
    }
}
int main()
{
    treeNode *head = new treeNode(a[1]);
    vector<treeNode> ret;
    createT(head, 1);
    inOrderTraval(*head, ret);
    for (int i = 0; i < ret.size(); i++)
    {
        cout << ret[i].data << " ";
    }
}

中序遍历结果

前序遍历结果

哈希表算法:
写出一个程序,接受一个由字母、数字和空格组成的字符串,和一个字母,然后输出输入字符串中该字母的出现次数。不区分大小写。

第一行输入一个由字母和数字以及空格组成的字符串,第二行输入一个字母。
输出输入字符串中含有该字符的个数。
这里的哈希表算法不是哈希算法,哈希算法是更高维度的一个概念,这里哈希表算法就是类似于用很多桶去装字符,相同字符装入一个桶,使用于很多关于字符的问题,非常好用。用map实现很好理解。但实际上,用新标准的unordered_map无序容器才是更适合的更快的,也更符合哈希表算法的原意。

#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
    string str;
    map<char,int>graph;
    getline(cin,str);
    for(int i=0;i<str.size();i++)
    {
        if(str[i]==' ')
        {
            continue;
        }
        else
        {
            graph[str[i]]++;
        }
    }    
    char obj;
    cin>>obj;
    if(obj>'Z')
    {
        char c=obj-('a'-'A');
        cout<<(graph[obj]+graph[c]);
    }
    else
    {
        char c=obj+('a'-'A');
        cout<<(graph[obj]+graph[c]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值