【温故而知新】排序复习

[写在前面以防忘记]
如果想要在VS Code执行 C 语言代码,需要先编译代码。编译代码需要用到 Linux 终端,接下来在终端中输入以下命令
gcc -o hello hello.c
编译C++代码就是:
g++ -o bubble_sort bubble_sort.cpp
接下来,我们就可以在终端中执行文件,注意执行的是编译之后的文件:
./hello

好接下来开始正文
我们来看题目

Time Limit: 6000/1000MS (Java/Others) Memory Limit: 64M/32M (Java/Others)
给你 n 个整数,请按从大到小的顺序输出其中前 m 大的数。
输入:每组测试数据有两行,第一行有两个数 n, m(0 < n, m < 1000000),第二行包含 n 个各不相同,且都处于区间[-500000, 500000]的整数。
输出:对每组测试数据按从大到小的顺序输出前 m 大的数。
Sample Input
5 3
3 -35 92 213 -644
Sample Output
213 92 3

C++
1.冒泡排序运行代码

#include<bits/stdc++.h>
using namespace std;
int a[1000001];                   //记录数字
#define swap(a, b) {int temp = a; a = b; b = temp;} //交换
int n, m;
void bubble_sort(){          //冒泡排序,结果仍放在a[]中
    for(int i = 1; i <= n-1; i++)
        for(int j = 1; j <= n-i; j++)
            if(a[j] > a[j+1])
                swap(a[j], a[j+1]);
}
int main(){
    while(~scanf("%d%d", &n, &m)){/*while (~scanf("%d%d",&n,&m))等效于 while (scanf("%d%d",&n,&m)!=EOF)~是按位取反scanf的返回值是输入值的个数如果没有输入值就是返回-1-1按位取反结果是0所以while(~scanf("%d", &n))就是当没有输入的时候退出循环*/


        for(int i=1; i<=n; i++)  scanf("%d", &a[i]);
        bubble_sort();
        for(int i = n; i >= n-m+1; i--){   //打印前m大的数,反序打印
            if(i == n-m+1)  printf("%d\n", a[i]);
            else           printf("%d ", a[i]);
        }
    }
    return 0;
}

2.快速排序

快速排序是一种基于分治法的优秀排序算法。这里先直接用 STL 的 sort()函数,它是改良版的快速排序,称为「内省式排序」。

在上面的程序中,把 bubble_sort(); 改为:
sort(a + 1, a + n + 1);

3.哈希算法是一种以空间换取时间的算法。本题的哈希思路是:在输入一个数字 t 的时候,对应 a[500000 + t]这个位置,记录 a[500000 + t] = 1;输出的时候,逐个检查 a[i],如果 a[i]等于 1,表示这个数存在,打印出前 m 个。程序如下

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000001;
int a[MAXN];
int main(){
    int n,m;
    while(~scanf("%d%d", &n, &m)){
        memset(a, 0, sizeof(a));
        for(int i=0; i<n; i++){
            int t;
            scanf("%d", &t); //此题数据多,如果用很慢的cin输入,肯定TLE
            a[500000+t]=1;    //数字t,登记在500000+t这个位置
        }
        for(int i=MAXN; m>0; i--)
            if(a[i]){
                if(m>1)  printf("%d ", i-500000);
                else      printf("%d\n", i-500000);
                m--;
            }
    }
    return 0;
}

程序并没有做显式的排序,只是每次把输入的数按哈希插入到对应位置,只有 1 次操作;n 个数输入完毕,就相当于排好了序。总的时间复杂度是 O(n)O(n)。在 hdu 上提交,返回的运行时间是 500MS。

算法的特征如下
1.输入:一个算法有零个或多个输入。可以没有输入,例如一个定时闹钟程序,它不需要输入,但是能够每隔一段时间就输出一个报警。
2.输出:一个算法有一个或多个输出。程序可以没有输入,但是一定要有输出。
3.有穷性:一个算法必须在执行有穷步之后结束,且每一步都在有穷时间内完成。
4.确定性:算法中的每一条指令必须有确切的含义,对于相同的输入只能得到相同的输出。
5.可行性:算法描述的操作可以通过已经实现的基本操作执行有限次来实现。
以冒泡排序算法为例子,上一节已经描述过它的执行步骤,它满足上述 5 个特征:

输入:由 n 个数构成的序列{a1, a2, a3, …, an};
输出:对输入的一个排序{a’1, a’2, a’3, …, a’n},且 a’1 ≤ a’2 ≤ a’3 ≤ … ≤ a’n;
有穷性:算法在执行 O(n^2)O(n2) 次后结束,这也是对算法性能的评估,即算法复杂度;
确定性:算法的每个步骤都是确定的;
可行性:算法的步骤能编程实现。
需要指出的是,上述第(5)条可行性,也是很重要的。有些算法并不能编程实现。例如一个有趣的排序算法,珠排序(Bead sort),如果它用重力法,能够在 O(1)O(1) 或 O(\sqrt{n})O( n ) 时间内得到排序结果,效率高到令人惊叹,但是无法编程实现。

一个程序或算法的复杂度有以下可能:

O(1)O(1)
计算时间是一个常数,和问题的规模 n 无关。例如:用公式计算时,一次计算的复杂度就是 O(1);哈希算法,用 hash 函数在常数时间内计算出存储位置;在矩阵 A[M][n]中查找 i 行 j 列的元素,只需要一次访问 A[i][j]就够了。

O(logn)O(logn)
计算时间是对数,通常是以 2 为底的对数,每一步计算后,问题的规模减小一倍。例如在一个长度为 n 的有序数列中查找某个数,用折半查找的方法,只需要 lognlogn 次就能找到。再例如分治法,一般情况下,在每个步骤把规模减少一倍,所以一共有 O(logn)O(logn) 个步骤。

O(logn)O(logn) 和 O(1)O(1) 没有太大差别。

O(n)O(n)
计算时间随规模 n 线性增长。在很多情况下,这是算法可能达到的最优复杂度,因为对输入的 n 个数,程序一般需要处理所有的数,即计算 n 次。例如查找一个无序数列中的某个数,可能需要检查所有的数。再例如图问题,有 V 个点和 E 个边,大多数图的问题,都需要搜索到所有的点和边,复杂度的上限就是 O(V+E)O(V+E)。

O(nlogn)O(nlogn)
这常常是算法能达到的最优复杂度。例如分治法,一共 O(logn)O(logn) 个步骤,每个步骤对每个数操作一次,所以总复杂度是 O(nlogn)O(nlogn)。用分治法思想实现的快速排序算法和归并排序算法,复杂度就是 O(nlogn)O(nlogn)。

如果一个算法是多项式复杂度,称它为「高效」算法。如果是指数复杂度,则是一个「低效」算法。可以这样通俗地解释「高效」和「低效」算法的区别:多项式复杂度的算法,随着规模 n 的增加,可以通过堆叠硬件来实现,「砸钱」是行得通的;而指数复杂度的算法,增加硬件也无济于事,其增长的速度超出了想象力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

007的米奇妙妙屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值