算法学习(埃式筛和快速排序)

        最近作者在学习算法,从简单的开始,一点一点进步,一步一个脚印!

        首先先来介绍以下埃式筛这个经典的算法,这个算法的目的就是可以快速的选出一定范围内的素数,比如我们要选出1到100之间的素数,我们先回想一下素数的定义,只有1和它本身为公约数的数就是素数,那么我么再来回想以下我们以前是如何实现判断一个数字是否为素数的呢,很简单,直接从1到n-1开始循环判断只要有可以被整除的数就说明该数不是素数。

C++代码演示

#include <iostream>
using namespace std;

bool is_primes(int n) {
    for (int i = 2; i < n; ++i) {
        if ( n % i == 0 )
            return false;
    }
    return true;
}

int main() {
    int n;
    cin >> n;
    bool ret = is_primes(n);
    if (ret) 
        cout << "is primes!" << endl;
    else 
        cout << "isn't a primes!" << endl;
    return 0;
}

上述算法的时间复杂度为O(n),如果我们要求出1-n中的所有素数,我们每个素数都去判断一遍的话那么时间复杂度会编程O(n^2),显然效率很低

// 求1-100的素数
#include <iostream>
using namespace std;


int main() {
    int n = 100;
    for (int i = 1; i <= n; i++) {
        bool ret = is_primes(n);
        if (ret)
            cout << i << " ";
    }
    return 0;
}

is_primes就是第一个代码段写的函数,这是传统做法,也是初学者常用的做法,接下来我来介绍以下埃式筛。

         我们通过观察发现,假设一个数是素数,那么我们直接可以知道这个数字的倍数都不是素数,我们只需要通过一张表来记录这个数的倍数,让后把素数添加到另一张表中,这样我们就得到了一张素数表,非常的方便。

        直接上代码:

#include <iostream>
using namespace std;

const int N = 10010;
int p[N], cnt;

bool st[N];

void get_primes(int n) {

    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            p[cnt++] = i;
            // 枚举i的倍数
            for (int j = 2 * i; j <= n; j += i)
                st[j] = true; // st[j] 记录j是否为质数
        }
    }
}
    

int main() {
    int n;
    cin >> n;

    get_primes(n);

    for (int i = 0; i < cnt; ++i) {
        cout << p[i] << " ";
    }
    return 0;

}

以上就是埃式筛的代码,也是非常的简洁,而且效率上提升了不少。

接下来我来讲解以下快速排序算法,排序算法分有很多种,每种都值得去好好深入学习探究其中的思想,快速排序(quick sort)是一个十分经典的排序算法,这个算法的核心包含了双指针和递归

假设我有以下序列

64731259

显然它现在是乱序的,我们快速排序的核心就是要选出一个标兵,这个标兵随便选都可以,一般情况下我们是选序列的中间位置作为标兵,记为q,向以上序列我们就以3作为标兵,即q = 3。

接下来我们需要两个哨兵,这两个哨兵一个在第一个位置的前一位,另外一个在最后一个位置的后一位,分别记为  i  和  j  。如图:

64731259

               i                                                  q                                                                      j

好了,现在我们的准备工作已经做好了,我们这里以升序排序为例,我们让哨兵i向右走,哨兵j向左走,知道哨兵 i 位置的数字大于 q位置的数字, 哨兵 j 的数字小于 q的数字,那么经过这样一个过程,现在变成如下情况:

64731259

                       i                                            q                           j

i 到6位置停下,j 到2位置停下,此时交换两个数字的位置:

24731659

                       i                                           q                          j

我么重复上面的过程

最后这个序列会变成以下这个样子

21374659

                                                    q

                                                     i               j                                                        

最后, i j 两个哨兵就会相遇, 此时第一轮排序结束。

我们可以看到其实这时候序列还不是有序的,但是,我们仔细观察会发现3左边的数字都比3小,3右边的数字都比3大了!接下来我们就会发现,去排列3左边的数字和排列3右边的数字的过程是一样的,同样是定一个标兵,两个哨兵,于是我们就可以开心的递归调用,最后实现整个序列有序!

快速排序实现代码如下:

#include <iostream>
using namespace std;

const int N = 10010;
int q[N];

void quick_sort(int l, int r) {
    int i = l - 1;
    int j = r + 1; // 定义两个哨兵
    int x = q[(r - l) / 2 + l]; // 定义标兵 为数组的中间位置的值
    if (l >= r) return;
    while (i < j) {
        do ++i; while (q[i] < x);
        do --j; while (q[j] > x);
        if (i < j) swap(q[i], q[j]); // 如果达到条件,交换两数
    }
    // 递归调用
    quick_sort(l, j); // 处理标兵左边的数
    quick_sort(j + 1, r) // 处理标兵右边的数
}


int main() {

    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> q[i];
    }
    
    quick_sort(0, n - 1);

    for (int i = 0; i < n; ++i) {
        cout << q[i] << " ";
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值