近期学习总结(快速排序/栈/队列/图论)

前言:

1、这篇博客基本上是写给自己看的,不过本人特别喜欢打注释,所以有兴趣看的朋友也可以往下看看我的理解...

2、本文的代码都基于《啊哈!算法》一书。


(可能在更新中......)

目录

前言:

快速排序

等等!

栈和队列简介

队列

图的两种表示法

邻接矩阵表示法

邻接表表示法

图的遍历(邻接矩阵表示法)

用DFS遍历图



快速排序

大致思路:

1、我们令首位为基准数,而在其后面的数都要一直与此基准数做比较。

2、使用双指针,使得一个指针 j 先从最后一位开始向左移动,一个指针 i 从第二位开始往左移动。一直到右指针 j 找到一个小于等于基准数的数,左指针 i 找到一个大于基准数的数,此时交换二者的位置。(一定是 j 先移动,因为数列原先就有序,i 必会移动到最后一位并与 j 相遇,此时首位与末尾交换位置,序列会被打乱)

3、重复2的操作直到 i , j 相遇,此时让首位(基准数)与i j 所指的元素交换位置,则保证了基准数左边的数都小于等于基准数,右边的数都大于基准数。此时我们认定的基准数在数列中已经到了它该到的位置。

4、然后将序列分成基准数左右两边的两个子序列分别重复1~3操作,直到拆不出新的子序列为止。

时间复杂度:

最差O(n^2),平均O(nlogn)

拓展:

某stl函数: nth_element()实现的应该是一次快排,所以若你只想要找到第 n 个元素,使用这个函数花费的时间是O(n)的。

#include <bits/stdc++.h>
using namespace std;
//这个是快速排序

int a[101], n;
//实际上是递归函数
void quicksort(int left, int right)
{
    int i, j, t, temp;
    //直到拆不出新的子序列为止
    if(left > right) return;
    //先找一个基准数,也就是当前数列最左端的数字
    temp = a[left];
    i = left;
    j = right;
    //
    while(i != j)
    {
        //一定是先让右端点的指针往左移动,找到一个小于基准数的数
        while(a[j] >= temp && i < j) j--;
        //找到后左指针往右移动,找到一个大于基准数的数
        while(a[i] <= temp && i < j) i++;
        //如果二者没相遇,就交换左右指针所指的数
        if(i < j)
        {
            t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }
    //让基准数归位
    a[left] = a[i];
    a[i] = temp;
    //基准数左右边的数列再进行快排
    quicksort(left, i-1);
    quicksort(i+1, right);
}
//
int main()
{
    int n;cin>> n;
    for(int i = 1;i <= n;++i) cin >> a[i];
    //
    quicksort(1, n);
    //
    for(int i = 1;i <= n;++i) cout << a[i] << " ";
    return 0;
}

等等!

突然发现后面有点不好写了,因为学习栈/队列/bfs/图论貌似都是融合着学的,所以现在先写一点前置知识

对了我们一般用uvw分别表示从点 u 到 点 v 的权值是 w。


栈和队列简介

可以把栈看成一个竖着放的木桶,我们把手上的东西依次放进木桶内,若想取出物品,必然只能逆序取出。所以栈是先进后出的(FILO),仅栈顶元素出栈。

队列

可以把队列看成一个横着放的羽毛球桶,我们把手上的东西依次从右边放进羽毛球桶内,羽毛球只能从左边取出,所以队列是先进先出的(FIFO),即从队尾入队,队首出队。


图的两种表示法

邻接矩阵表示法

定义:

1、我们建立一个二维数组e[i][j],表示的是从点 i 到点 j 的距离(权值),并规定自己到自己为0, 两点不连通为inf(这里我们令inf = 99999999)。

读入:

1、无向图的话显然 i 到 j 和 j 到 i 都可以,所以两个都要读入哦。

//矩阵的初始化
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            if(i == j) e[i][j] = 0;
            else e[i][j] = 99999999;
    //读入无向图
    int a, b;
    for(int i = 1;i <= m;++i)
    {
        cin >> a >> b;
        e[a][b] = 1;
        e[b][a] = 1;
    }

邻接表表示法

定义:

1、大致意思就是以每个顶点u建立一个链表。

(这个有点难解释,会链表的话应该会比较好理解。看代码+注释吧)

#include <bits/stdc++.h>
using namespace std;
//邻接表adjacency list
//我们把m远小于n^2的图称为稀疏图,而m相对较大的图称为稠密图
//对于稀疏图我们用邻接表代替邻接矩阵,时间复杂度O(m+n)logn
//_first数组存的是1~n号顶点最后一条边的编号
//_next数组存的是编号为i的边的上一条边的编号
int main()
{
    int n, m;cin >> n >> m;
    //uvw要比m的最大值大1
    int u[6], v[6], w[6];
    //_first要比n的最大值大1, _next要比m的最大值大1
    int _first[5], _next[5];
    //
    for(int i = 1;i <= n;++i) _first[i] = -1;
    for(int i = 1;i <= m;++i)
    {
        cin >> u[i] >> v[i] >> w[i];
        _next[i] = _first[u[i]];
        _first[u[i]] = i;
    }
    return 0;
}

图的遍历(邻接矩阵表示法)

用DFS遍历图

就是选定一个顶点然后进行dfs即可。

#include <bits/stdc++.h>
using namespace std;
//使用dfs来遍历图
//我们用e[i][j]数组来存放i到j的关系,0表示自己到自己,1表示有边,INF表示无边
int book[1001], n, m, e[101][101];
int sum = 0;
//
void dfs(int cur)
{
    //打印访问过的点
    printf("%d ", cur);
    sum++;
    //如果n个点都被访问过了
    if(sum == n) return ;
    for(int i = 1;i <= n;++i)
    {
        //如果当前点与遍历到的点有边且没有访问过
        if(e[cur][i] == 1 && book[i] == 0)
        {
            book[i] = 1;
            dfs(i);
        }
    }
    return ;
}
//
int main()
{
    //n个点以及m条边
    cin >> n >> m;
    //初始化图的数组,用99999999表示INF
    for(int i = 1;i <= n;++i)
        for(int j = 1;j <= n;++j)
            if(i == j) e[i][j] = 0;
            else e[i][j] = 99999999;
    //读入边
    int a, b;
    for(int i = 1;i <= m;++i)
    {
        cin >> a >> b;
        //因为是无向图所以i-j与j-i都要赋值为1
        e[a][b] = 1;
        e[b][a] = 1;
    }
    //选择一个顶点出发
    book[1] = 1;
    dfs(1);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值