什么是算法?
平时学计算机的小伙伴经常会听到算法这个词,那到底什么是算法呢? 算法并没有一个明确的定义,依我个人理解算法就是求解特定问题对解题思路和步骤的描述。
其实网上有很多十大算法的文章,各位大佬们写的都非常不错,大佬们水平很高,所以写的文章也是比较深的,我个人是个新人。写的这篇文章也特别适合刚入门学习的新人,简单入手。
十大排序算法
🎈冒泡排序
解析
冒泡排序是一种最基础的交换排序,就好比是我们平时喝的可乐,从底部一直冒到顶部。具体实现方法就是根据数的大小一点一点的往一侧移动,如果按照从小到大的顺序就将小的数从右边一直往左挪。
举例:将3.2.5.8.1这五个数从小到大排序
1.第一步,从右面开始比较,将小的数往左挪,1先和8比较,1小所以和8互换位置。
2.第二步,1再和5比较,还是1较小和5互换位置。
3.同理,直到全部比较之后,1最小在第一个位置
4.然后重新开始比较,8比5大所以不用挪动,然后2和5比较,5大不用移动,2再和3比较,2小和3互换位置,2再和1比较不用移动,再从右往左比较一遍,没有可以移动的最终结果为1.2.3.5.8
代码实现
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { // 相邻元素两两对比
var temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
例题
本题目用到的就是冒泡排序,如果前一个数比后一个数大的话ans就加1
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
int a[n+1],ans;
ans=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
if(a[i]<a[j])
ans++;
printf("%d",ans);
}
🎈选择排序
解析
选择排序顾名思义是从所有的元素中选择一个最小(大)的,放在序列的起始位置,然后再从剩余队列中,选择一个最小(大)的,放在第二个位置。
举例:将3.2.5.8.1这五个数从小到大排序
1.第一步:首先从所有队列中选出最小的那个数,最小的是1,然后放到序列首位
2.第二步:再从剩下的四个数中找到最小的那个,放到第二个位置,最小的是2
3.第三步:同理,依次排序下去,最终为
代码实现
function selectionSort(arr) {
var len = arr.length;
var minIndex, temp;
for (var i = 0; i < len - 1; i++) {
minIndex = i;
for (var j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) { // 寻找最小的数
minIndex = j; // 将最小数的索引保存
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
例题
本人感觉此题就是为选择排序“量身定做”,第一个数选最大的,第二个数选最小的,第三个数是从剩下的数中选最大的。
#include<iostream>
using namespace std;
int main(){
int n=0;
long int a[1000]={0};
cin>>n;
for(int i=0; i<n; i++) cin>>a[i];
for(int i=0; i<n-1; i++) {
int min_index = i;
for(int j=i; j<n; j++) if(a[j] > a[min_index]) min_index = j;//找出第 i 小的数所在的位置
swap(a[i],a[min_index]);//将第 i 小的数,放在第 i 个位置;如果刚好,就不用交换
i++;
for(int j=i; j<n; j++) if(a[j] < a[min_index]) min_index = j;//找出第 i 小的数所在的位置
swap(a[i],a[min_index]);//将第 i 小的数,放在第 i 个位置;如果刚好,就不用交换
}
for(int i=0; i<n; i++) cout<<a[i]<<endl;
}
🎈插入排序
解析
插入排序的思想是分为有序序列和无序序列,将无序序列中的数插入到有序序列中,直到所有的数全部插入。
举例:将3.2.5.8.1这五个数从小到大排序
1.第一步:3为有序序列,2.5.8.1为无序序列,
2.将无序序列的第一个数拿出来插入到有序序列
3.然后将无序序列的5插入有序序列
4.同理
5.最终结果为
代码实现
function insertionSort(arr) {
var len = arr.length;
var preIndex, current;
for (var i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
例题
题目描述
清华附小期末考试结束后,分别由数学、语文、英语按照学号顺序输入30名同学的成绩,班主任想知道三门课总分的最高分和最低分,以及取得总分最高分和最低分的两位同学的编号。(输入数据保证没有同分情况,编号由1到30)
输入描述
第一行输入编号为1-30的30位同学的数学成绩,分数之间用空格隔开;第二行输入语文成绩,第三行输入英语成绩
输出描述
输出四个数,分别是总分最高分,总分最低分,取得最高分同学的编号,取得最低分同学的编号
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
int*math = new int[3];
int*chinese = new int[3];
int*english = new int[3];
for (int n = 0; n < 3; n++) {
cin >> math[n];
}
for (int n = 0; n < 3; n++) {
cin >> chinese[n];
}
for (int n = 0; n < 3; n++) {
cin >> english[n];
}
int*sum= new int[3];
int*order = new int[3];
for (int n = 0; n < 3; n++) {
sum[n] = math[n] + chinese[n] + english[n];
}
for (int n = 0; n <= 2; n++) {
order[n] = sum[n];
}
for (int n = 1; n <=2; n++){
int now = sum[n], space = 0;
while (now > sum[space])
space++;
for (int i = n; i > space; i--)
sum[i] = sum[i - 1];
sum[space] = now;
}
int max, min;
for (int n = 0; n <= 2; n++) {
if (order[2] == sum[n])
max = n;
if (order[0] == sum[n])
min = n;
}
cout << sum[2] << endl;
cout << sum[0] << endl;
cout << max<<endl;
cout << min<<endl;
system("pause");
return 0;
}
🎈希尔排序
解析
希尔排序主要思想是将数据进行分组,然后对每一组数据进行插入排序,在每一组数据都有序后,再对所有的分组利用插入排序进行最后一次排序。可以说是对插入排序的升级版,通过减少数据交换的次数,以达到加快排序的速度。
举例:将3.2.5.8.1.6这六个数从小到大排序
1.第一步:分组依据为两个一组,六个数分为三组,第一个和第四个一组,第二个和第五个一组,第三个和第六个一组。
2.第二步:3和8相比,2和1相比,5和6相比,小的排前大的排后
3.第三步:然后再分成3/2=1组,直接所有的数据插入排序
代码实现
function shellSort(arr) {
var len = arr.length;
for (var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
// 注意:这里和动图演示的不一样,动图是分组执行,实际操作是多个分组交替执行
for (var i = gap; i < len; i++) {
var j = i;
var current = arr[i];
while (j - gap >= 0 && current < arr[j - gap]) {
arr[j] = arr[j - gap];
j = j - gap;
}
arr[j] = current;
}
}
return arr;
}
例题
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int n,k,i,a[100001],b[100001],ans=0;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);//sort从小到大排序
for(i=1;i<=n-1;i++)
b[i]=a[i+1]-a[i];//算出各等级选手的差
sort(b+1,b+n);//sort从小到大排序
for(i=1;i<=k;i++)
ans+=b[i];//找出前K个等级差的和
printf("%d",ans);//输出
return 0;
}
🎈归并排序
解析
归并排序主要思想是对整个序列的元素进行逐层折半分组,然后从最小分组开始比较排序,合并成一个大的分组,逐层进行,最终所有的元素都是有序的。
举例:将3.2.5.8.1.6这六个数从小到大排序
1.第一步:首先是逐层折半分组,两个一组一共分成三组。
2.第二步:然后是每组进行排序
3.第三步:然后再逐层合并,按照从小到大。
4.第四步:在进行排序合并
这里插入个动图便于大家理解
代码实现
function mergeSort(arr) {
var len = arr.length;
if (len < 2) {
return arr;
}
var middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {
var result = [];
while (left.length>0 && right.length>0) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
例题
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
string s;
map<string,int>nmsl;
int n;
long long ans=0;
int c[1000001],d[1000001];
void qsort(int a,int b)
{
if(a==b)return;
int mid=(a+b)>>1;
int i=a,j=mid+1,k=a;
qsort(a,mid),qsort(mid+1,b);
while(i<=mid&&j<=b)
if(c[i]<=c[j])
{
d[k++]=c[i++];
}
else
{
d[k++]=c[j++];
ans+=mid-i+1;
}
while(i<=mid)
d[k++]=c[i++];
while(j<=b)
d[k++]=c[j++];
for(int l=a;l<=b;l++)
c[l]=d[l];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>s;
nmsl[s]=i;
}
int j=0;
for(int i=1;i<=n;i++)
{
cin>>s;
c[++j]=nmsl[s];
}
qsort(1,n);
printf("%lld",ans);
return 0;
}
🎈快速排序
解析
快速排序是先从数列中取出一个数作为基准数,分区过程是将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边,再对左右区间重复第二步,直到各区间只有一个数 举例:将3.2.5.8.1.6这六个数从小到大排序 1.第一步:以3为基准数,比三大的往右移动,比三小的往左移动
2.第二步:然后3的左侧以2为基准数进行排序
3.第三步:3的右侧以8为基准数进行排序
代码实现
function quickSort(arr, left, right) {
var len = arr.length,
partitionIndex,
left = typeof left != 'number' ? 0 : left,
right = typeof right != 'number' ? len - 1 : right;
if (left < right) {
partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex-1);
quickSort(arr, partitionIndex+1, right);
}
return arr;
}
function partition(arr, left ,right) { // 分区操作
var pivot = left, // 设定基准值(pivot)
index = pivot + 1;
for (var i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index-1;
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
例题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<bits/stdc++.h>
int n;
int ans=0;
int i,j,k;
int a[100010];
using namespace std;
int main(){
cin>>n;
j=0;
for(i=1;i<=n;i++)
cin>>a[i];
sort(a,a+n+1);//从小到大排序
ans=n+a[n]*10;//每个人所需要的时间和电梯上下时间和
for(i=1;i<=n;i++){
if(a[i]!=a[i-1]){//循环从一开始,数组默认初值为0,避免漏掉全部在同一层楼
ans=ans+5;//电梯需要开关
}
}
cout<<ans;
return 0;
}
🎈计数排序
解析
计数排序是一种非基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中以达到排序的效果
举例:将3.2.5.8.1.6这六个数从小到大排序
1.第一步:先确定待排序的所有元素中最大数和最小数
2.第二步:然后开辟一块新的数组空间,最小值就是1,最大值就是8
3.然后就是将各个元素按照从小到大得顺序排列到新的数组空间
插入个动图便于大家理解
代码实现
function countingSort(arr, maxValue) {
var bucket = new Array(maxValue + 1),
sortedIndex = 0;
arrLen = arr.length,
bucketLen = maxValue + 1;
for (var i = 0; i < arrLen; i++) {
if (!bucket[arr[i]]) {
bucket[arr[i]] = 0;
}
bucket[arr[i]]++;
}
for (var j = 0; j < bucketLen; j++) {
while(bucket[j] > 0) {
arr[sortedIndex++] = j;
bucket[j]--;
}
}
return arr;
}
例题
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
int const maxk = 1000005,maxn = 1000005;
//接口:a[i]原数组。ranked[i]排好顺序的数组,maxn数组个数,maxk数字的范围
//调用 jishusort(n,maxk); //n代表数组的大小
int a[maxn],ranked[maxn],cnt[maxk];//cnt[i]小于等于i的个数
void jishusort(int n,int k)//n数组的大小,k数组中数字的范围
{
for(int i = 0; i < n; i ++)
{
cnt[a[i] + 500000] ++;
}
for(int i = 1; i < k; i ++)
{
cnt[i] += cnt[i - 1];
}
for(int i = n -1 ; i >= 0; i --)
{
ranked[--cnt[a[i]+ 500000]] = a[i];
}
}
int main()
{
//freopen("in.txt","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)!= EOF){
memset(cnt,0,sizeof(cnt));
for(int i = 0; i < n; i ++)
{
scanf("%d",&a[i]);
}
jishusort(n,maxk);
for(int i = n - 1; i > n - m; i --)
{
printf("%d ",ranked[i]);
}
printf("%d\n",ranked[n - m]);
}
return 0;
}
//忘记多组数据了
//忘记清空了
🎈基数排序
解析
基数排序是将待排序序列的每个元素统一为同样位数长度的元素,位数较短的通过补0达到长度一致,然后从最低位或从最高位开始,依次进行稳定的计数排序,最终形成有序的序列。基数排序主要是针对整数的排序,由于整数也可以表示字符串或和特定格式的浮点数,因此能用整数表达的其他数据类型也能用基数排序。
举例:将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序。
第1轮排序 [按照个位排序]:
说明: 事先准备10个数组(10个桶), 0-9 分别对应 位数的 0-9
(1) 将 各个数,按照个位大小 放入到 对应的 各个数组中
(2) 然后从 0-9 个数组/桶中依次按照加入元素的先后顺序取出
第1轮排序后:542 53 3 14 214 748
第2轮排序 [按照十位排序]
(1) 将 各个数,按照十位大小 放入到 对应的 各个数组中
(2) 然后从 0-9 个数组/桶中依次按照加入元素的先后顺序取出
第2轮排序后: 3 14 214 542 748 53
第3轮排序 [按照百位排序]
(1) 将 各个数,按照百位大小 放入到 对应的 各个数组中
(2) 然后从 0-9 个数组/桶中依次按照加入元素的先后顺序取出
第3轮排序后:3 14 53 214 542 748 (得到了我们要的顺序)
代码实现
var counter = [];
function radixSort(arr, maxDigit) {
var mod = 10;
var dev = 1;
for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(var j = 0; j < arr.length; j++) {
var bucket = parseInt((arr[j] % mod) / dev);
if(counter[bucket]==null) {
counter[bucket] = [];
}
counter[bucket].push(arr[j]);
}
var pos = 0;
for(var j = 0; j < counter.length; j++) {
var value = null;
if(counter[j]!=null) {
while ((value = counter[j].shift()) != null) {
arr[pos++] = value;
}
}
}
}
return arr;
}
例题
#include <cstdio>
#include <algorithm>
#define int long long
struct node {
int id, v;
inline bool operator < (const node x) const {return v < x.v;}
} b[300005];
inline bool operator < (const int x, const node y) {return x < y.v;}
int a[300005], c[300005], s[300005], n, N;
inline void update(const int x) {
for (int i(x); i <= n; i += (i & ~i + 1)) ++ c[i];
}
inline int query(const int x) {
int sum(0);
for (int i(x); i; i -= (i & ~i + 1)) sum += c[i];
return sum;
}
signed main() {
int m, last(0);
scanf("%lld%lld", &n, &m);
for (int i(1); i <= n; ++ i) scanf("%lld", &b[i].v), b[i].id = i;
std::sort(b + 1, b + n + 1);
a[b[1].id] = 1;
for (int i(2); i <= n; ++ i)
a[b[i].id] = (b[i].v != b[i - 1].v) + a[b[i - 1].id];
for (int i(n); i >= 1; -- i)
s[a[i]] += query(a[i]), update(a[i]);
N = a[b[n].id];
for (int i(2); i <= N; ++ i) s[i] += s[i - 1];
printf("%lld\n", s[N]);
a[0] = -0x3fffffff;
while (m --) {
int k;
scanf("%lld", &k);
if (a[k] < a[last]) k = last;
last = k;
printf("%lld\n", s[N] - s[a[k]]);
}
return 0;
}
🎈堆排序
解析
堆排序是利用二叉堆的概念来排序的选择排序算法,分为两种:
升序排序:利用最大堆进行排序
降序排序:利用最小堆进行排序
举例:加入个动图大家看的更加清楚
代码实现
var len; // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量
function buildMaxHeap(arr) { // 建立大顶堆
len = arr.length;
for (var i = Math.floor(len/2); i >= 0; i--) {
heapify(arr, i);
}
}
function heapify(arr, i) { // 堆调整
var left = 2 * i + 1,
right = 2 * i + 2,
largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest);
}
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function heapSort(arr) {
buildMaxHeap(arr);
for (var i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0);
}
return arr;
}
例题
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
priority_queue<ll,vector<ll>,greater<ll> > a;//这里直接调用优先队列
#define in(t) freopen("t.in","r",stdin)
#define out(t) freopen("t.out","w",stdout)
#define m(a) memset(a,0,sizeof(a))
int main(){
long long ans=0,n,t;//ans注意要开long long,不然会爆
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&t);
a.push(t);
}
for(int i=1;i<=n-1;i++){
int c,d;
c=a.top();
a.pop();
d=a.top();
a.pop();//每次取最小的两个数
ans+=c+d;//加上能量
a.push(c+d);
}//和合并果子一样,具体可以参考合并果子题解
printf("%lld",ans);
return 0;
}
🎈桶排序
解析
桶排序是计数排序算法的升级版,将数据分到有限数量的桶子里,然后每个桶再分别排序
举例:
代码实现
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 输入数据的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i]; // 输入数据的最大值
}
}
// 桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
// 利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
例题
#include <bits/stdc++.h>
const int MAXN=5000005;
int n,m,i,j,k=3,x,y,a[MAXN],b[MAXN];
//x是「缩小药水」数量,y是最小费用,b是用来桶排序的
inline void read(int &x) //快读
{
short negative=1;
x=0;
char c=getchar();
while(c<'0' || c>'9')
{
if(c=='-')
negative=-1;
c=getchar();
}
while(c>='0' && c<='9')
x=(x<<3)+(x<<1)+(c^48),c=getchar();
x*=negative;
}
inline void print(int x) //快输
{
if (x<0)
putchar('-'),x=-x;
if (x>9)
print(x/10);
putchar(x%10+'0');
}
signed main(void)
{
read(n),read(m);
for (i=1;i<=n;i++)
read(a[i]),b[a[i]]++;
for (i=1;i<=30000;i++) //桶排序
while (b[i])
a[++j]=i,b[i]--;
for (i=1;i<=n;i++)
{
bool f=false;
while (a[i]>k) //不得不使用「缩小药水」时
{
if (m<=0) //尽量发挥「缩小药水」的效果,在一次结束时在退出而不是在m一旦小于等于0时就退出
{
f=true;
break;
}
x++,y++,k+=3; //使用一次「缩小药水」
}
if (f)
break;
y+=(a[i]<k)?1:4; //判断一下是1费还是4费
m-=(a[i]%3)?(a[i]%3):3; //这里采用的是直接从m上扣
}
if (m>0) //拉了所有随从还是A不暴它
return printf("Human Cannot Win Dog"),0;
for (i=1;i<=n && m<0;i++)
if (a[i]%3==0 && a[i]<=k && m+3<=0)
y-=4,m+=3;
for (i=1;i<=n && m<0;i++)
if (a[i]%3==1 && a[i]<=k && m+1<=0)
y-=1,m+=1;
for (i=1;i<=n && m<0;i++)
if (a[i]%3==2 && a[i]<=k && m+2<=0)
y-=1,m+=2;
//先3,再2,最后1
print(x),putchar(' '),print(y);
return 0;
}
十大排序算法的时间复杂度
结语:
由于本人能力有限,如若文章出现错误,欢迎评论指正。如果本文有帮助到你,希望能留个点赞支持一下,感谢!