目录
一、了解排序
1、内部
2、外部
二、排序的稳定性
三、插入排序
1、算法和操作
2、代码
四、选择排序
1、算法和操作
2、代码
五、冒泡排序
1、算法和操作
2、代码
六、堆排序
1、优先队列
2、排序代码
七、归并排序
1、定义
2、基本算法
(1)、分离
(2)、合并
(3)、图片讲解
3、C++代码实现
1、分离函数
2、合并函数
3、C++完整代码
八、计数排序
1、算法思想
2、样例题目
3、代码
九、sort函数
十、结尾
一、了解排序
排序是计算机内经常进行的一种操作,其目的是将一组 "无序" 的记录序列调整为 "有序" 的记录序列。
1、内部
若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。内部排序是一个逐步扩大记录的有序序列长度的过程。
2、外部
若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。
二、排序的稳定性
排序算法的稳定性:如果在数据序列中有两个元素 a[ i ] ,a[ j ] ,它们的值相等,且在排序前,数据元素 a[ i ] 在 a[ j ] 前面。如果经过排序后,a [ i ] 仍然在 a[ j ] 前面,则称这种排序算法是稳定的,否则称这个排序算法是不稳定的。
哪种排序算法是稳定的呢?我也不知道!看下表:
排序方法 | 稳定性 |
直接插入排序 | √ |
直接选择排序 | × |
冒泡排序 | √ |
快速排序 | × |
堆排序 | × |
二路归并排序 | √ |
计数排序 | 无 |
三、插入排序
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
1、算法和操作
插入排序对于少量元素的排序,是一个有效的算法 。
插入排序是一种简单的排序方法,它是将一个数据插入到已经排好序的有序数组,从而形成一个新的有序数组。
插入排序的工作方式像许多人排序扑克牌:
我们每次从桌子上拿走一张牌并将其插入到手中正确的位置。
为了找到它的正确位置,我们从右到左将它与手中的每张牌进行比较。
因此,手上的牌总是有序。
文章看不懂,看看操作:
原本要排序的数为 5 3 4 2 9 1,从小到大排序。
3 5 4 2 9 1 // 将3放到合适的位置(5前面)
3 4 5 2 9 1 // 将4放到合适的位置(3、5中间)
2 3 4 5 9 1 // 将2放到合适的位置(最前面)
2 3 4 5 9 1 // 将9放到合适的位置(最后面)
1 2 3 4 5 9 // 将1放到合适的位置(最前面)
排序结束!!!
2、代码
原理已经知道了,代码实现还难吗?
#include <iostream>
using namespace std;
int n,a[2000]; //定义数据个数n,排序数组a
int main()
{
cin >>n; //输入个数
for (int i=1;i<=n;i++)
cin >>a[i]; //输入数据
for (int i=2;i<=n;i++) //第一个数本身只有一个元素,所有有序,因此不用参与排序
{
int j,k=a[i]; //记录下当前元素
for (j=i-1;j>0;j--)
{
if (a[j]>k) //若前面一个数大于当前元素
a[j+1]=a[j]; //则将前面一个元素往后移动
else
break; //否则:说明当前元素已经找到了合适的位置,推出循环
}
a[j+1]=k; //将当前元素放入数组的合适的位置
/* 输出排序的过程
for (int j=1;j<=n;j++)
cout <<a[j] <<" ";
cout <<endl;
*/
}
for (int i=1;i<=n;i++)
cout <<a[i] <<" "; //输出排序好的数组
return 0;
}
四、选择排序
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
1、算法和操作
选出一个作为基本位置,然后再把位置的数和后面的依次比较,交换
将基本位置的数确定好,重复,直到完成排序。
算法看不懂??
看看具体操作:
将数列:4 9 2 8 6 从小到大排序。
第一步: 4 9 2 8 6 // 基本位置是第一位:4,开始比较。4<9,不交换。
第二步: 2 9 4 8 6 // 接下来将 4 2 比较,交换。
第三步: 2 9 4 8 6 // 将 2 8 比较,不交换。
第四步: 2 9 4 8 6 // 将 2 6 比较,不交换。
//现在完成了最小数放在最前面,用了4次比较,1次交换。
第五步: 2 4 9 8 6 // 基本位置是第二位:9,开始比较。4<9,交换。
第六步: 2 4 9 8 6 // 将 4 8 比较,不交换。
第七步: 2 4 9 8 6 // 将 4 8 比较,不交换。
//我们已经将第2小数放到第2位,用了3次比较,1次交换。
第八步: 2 4 8 9 6 // 将 9 8 比较,交换。
第九步: 2 4 6 9 8 // 将 8 6 比较,交换。
//我们已经将第3小数放到第3位,用了2次比较,2次交换。现在要将第4小数放到第4位。
第十步: 2 4 6 8 9 // 将 9 8 比较,交换。
这时,程序结束了。我们也将数列排好了。每一次的交换如下:
4 9 2 8 6
2 9 4 8 6
2 4 9 8 6
2 4 8 9 6
2 4 6 9 8
2 4 6 8 9
排序结束。
2、代码
#include <iostream>
#include <cmath>
using namespace std;
int n,i,j,a[2000];
bool t; //定义变量
int main()
{
cin >>n;
for (i=1;i<=n;i++)
cin >>a[i]; //输入
for (i=1;i<n;i++) //从1开始,最后一位不用比
for (j=i+1;j<=n;j++)
if (a[i]>a[j]) //a[i]是基本位,a[j]是当前位
swap(a[i],a[j]); //交换
for (i=1;i<=n;i++)
cout <<a[i] <<" "; //输出
return 0; //华丽结束
}
五、冒泡排序
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
1、算法和操作
冒泡排序(从小到大):从后面开始往前两两对比,如果前数大于后数则交换,否则不交换,重复此操作,直到在一次操作中没有交换才结束。由于在过程中,最小/大数会不断向前移动,像泡泡从水里冒出来一样,故称冒泡排序。
文字看不懂,看看具体操作:将数列:4 9 2 8 6 从小到大排序。
第一步: 4 9 2 6 8 // 后面两个数 8 6 比较,8大于6,所以交换。
第二步: 4 9 2 6 8 // 接下来将 2 6 比较,不交换。
第三步: 4 2 9 6 8 // 将 9 2 比较,交换。
第四步: 2 4 9 6 8 // 将 4 2 比较,交换。
//现在完成了最小数放在最前面,用了4次比较,3次交换。我们要将第2小数放到第2位。
第五步: 2 4 9 6 8 // 再次从后面开始,6 8 比较,不交换。
第六步: 2 4 6 9 8 // 将 6 9 比较,交换。
第七步: 2 4 6 9 8 // 将 4 6 比较,不交换。
//我们已经将第2小数放到第2位,用了3次比较,1次交换。现在要将第3小数放到第3位。
第八步: 2 4 6 8 9 // 将 9 8 比较,交换。这时,排序已经完成,但程序还没结束。
第九步: 2 4 6 8 9 // 将 6 8 比较,不交换。
//我们已经将第3小数放到第3位,用了2次比较,1次交换。现在要将第4小数放到第4位。
第十步: 2 4 6 8 9 // 将 8 9 比较,不交换。
这时,程序结束了。我们也将数列排好了。每一次的交换如下:
4 9 2 8 6
4 9 2 6 8
4 2 9 6 8
2 4 9 6 8
2 4 6 9 8
2 4 6 8 9
排序结束。
知道了基本原理,代码也就好打了(将整形的数从小到大排序):
2、代码
#include <iostream>
#include <cmath>
using namespace std;
int n,i,j,a[2000];
bool t; //定义变量
int main()
{
cin >>n;
for (i=1;i<=n;i++)
cin >>a[i]; //输入
for (i=1;i<=n;i++)
{
t=1;
for (j=n;j>i;j--)
if (a[j]<a[j-1]) //从后面往前两两判断
{
swap(a[j],a[j-1]); //交换
t=0;
}
if (t)
break; //如一次循环中没有交换,说明已经排好序了,退出循环
}
for (i=1;i<=n;i++)
cout <<a[i] <<" "; //输出
return 0; //华丽结束
}
六、堆排序
一下内容来自我自己的文章 和 我的另一篇文章,若想获得更佳的阅读效果,请点击链接。
今天我们来讲一个速度较快的排序算法:堆排序。
1、优先队列
要学堆排序,首先要会优先队列(链接就是 “我的另一篇文章” )。
既然是队列那么先要包含头文件#include <queue>, 它和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的。
和队列基本操作相同,有如下几种:
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
定义:priority_queue< 数据类型, 容器类型, 比较的方式 >
当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大根堆。
这里区分一下小根堆和大根堆:
小根堆是保证最小值在队列最前面,大根堆是保证最大值在最前面。
例子:
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <string,vector<string>,less<string> >q;
这里就定义了两个优先队列
第一个是小根堆,是int类型
第二个是大根堆,string类型
使用示例:
#include <iostream>
#include <queue>
using namespace std;
priority_queue <int>a; //定义优先队列a,int类型,默认是大根堆
/*相当于:
priority_queue <int,vector<int>,less<int> >a;
*/
priority_queue <string,vector<string>,greater<string> >b; //这是小根堆,类型为string
string f;
int t;
int main()
{
cout <<"请输入5个整数,用空格隔开,输入之后换行!\n";
for (int i=1;i<=5;i++)
{
cin >>t;
a.push(t); //输入5个数存入a,每一个时刻a中最大的永远在开头
}
while (!a.empty()) //a非空时
{
cout <<a.top() <<' '; //输出a的第一位
a.pop(); //将a的开头弹出
}
//上面的循环输出了一个排序好的数列
cout <<endl;
cout <<"请输入3串乱七八糟的字符串(英文+数字),用空格隔开,输入之后换行!\n";
for (int i=1;i<=3;i++)
{
cin >>f;
b.push(f);
}
while (!b.empty())
{
cout << b.top() << ' ';
b.pop();
}
//同上
cout <<endl;
return 0;
}
2、排序代码
下面代码将会利用刚才讲到的优先队列的小根堆,进行从小到大排序:
#include <iostream>
#include <queue>
using namespace std;
priority_queue<int,vector<int>,greater<int> > a; //定义小根堆(小顶堆),从小到大排序
int n,t;
int main()
{
cin >>n;
for (int i=1;i<=n;i++)
{
cin >>t;
a.push(t); //输入
}
for (int i=1;i<=n;i++)
{
cout <<a.top() <<' '; //每一次输出第一个
a.pop(); //把第一个去掉
}
return 0;
}
七、归并排序
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
1、定义
归并排序(Merge Sort)是建立在归并操作上的一种既有效又稳定的排序算法,该算法是采用
分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的
序列。即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二
路归并。
2、基本算法
(1)、分离
将已有数列不断分离成两段长度基本相同(当已有数列长度是奇数时,则一半长一半短),直到分离成长度为 1 的 n 个数列(其实就是 n 个数)。
(2)、合并
将数列两两合并,每次合并时进行比较和排序,直到完成排序。
(3)、图片讲解
将一个无序数列排好序:
先是分离成长度为 1 的 n 个数列,然后再合并,合并过程中两个红色区域代表两两比较,然后将小的放在前面。
3、C++代码实现
1、分离函数
void mergesort(int x,int y) //分离,x 和 y 分别代表要分离数列的开头和结尾
{
if (x>=y) return; //如果开头 ≥ 结尾,那么就说明数列分完了,就要返回
int mid=(x+y)/2; //将中间数求出来,用中间数把数列分成两段
mergesort(x,mid);
mergesort(mid+1,y); //递归,继续分离
merge(x,mid,y); //分离玩之后就合并
}
2、合并函数
void merge(int low,int mid,int high) //归并
//low 和 mid 分别是要合并的第一个数列的开头和结尾,mid+1 和 high 分别是第二个数列的开头和结尾
{
int i=low,j=mid+1,k=low;
//i、j 分别标记第一和第二个数列的当前位置,k 是标记当前要放到整体的哪一个位置
while (i<=mid && j<=high) //如果两个数列的数都没放完,循环
{
if (a[i]<a[j])
b[k++]=a[i++];
else
b[k++]=a[j++]; //将a[i] 和 a[j] 中小的那个放入 b[k],然后将相应的标记变量增加
} // b[k++]=a[i++] 和 b[k++]=a[j++] 是先赋值,再增加
while (i<=mid)
b[k++]=a[i++];
while (j<=high)
b[k++]=a[j++]; //当有一个数列放完了,就将另一个数列剩下的数按顺序放好
for (int i=low;i<=high;i++)
a[i]=b[i]; //将 b 数组里的东西放入 a 数组,因为 b 数组还可能要继续使用
}
3、C++完整代码
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
int n,a[12000],b[12000];
void merge(int low,int mid,int high)
{
int i=low,j=mid+1,k=low;
while (i<=mid && j<=high)
{
if (a[i]<a[j])
b[k++]=a[i++];
else
b[k++]=a[j++];
}
while (i<=mid)
b[k++]=a[i++];
while (j<=high)
b[k++]=a[j++];
for (int i=low;i<=high;i++)
a[i]=b[i];
}
void mergesort(int x,int y)
{
if (x>=y) return;
int mid=(x+y)/2;
mergesort(x,mid);
mergesort(mid+1,y);
merge(x,mid,y);
}
int main()
{
cin >>n;
for (int i=1;i<=n;i++)
cin >>a[i];
mergesort(1,n); //调用函数
for (int i=1;i<=n;i++)
cout <<a[i] <<" ";
return 0;
}
八、计数排序
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
1、算法思想
计数排序的主要思想是:若待排序的数据元素的关键字在一个明显有限的范围内(整形)时可设计一个计数数组,记录每一个元素的个数,然后把数组中的原数按顺序输出。
2、样例题目
题目:输入n(1≤n≤10^8)个 0~2000 之间的整数,从小到大排序输出。
题目分析:这道题是真的难,由于数据量太大,而每个数又有固定的小范围,并且是有序类型,我们要用时间复杂度为O(n)的排序方法:计数排序。
3、代码
#include <iostream>
using namespace std;
int n,i,a[3000]; //这里把数组定大一点
int main()
{
cin >>n;
for (i=1;i<=n;i++)
{
cin >>k;
a[k]++; //将当前数对应数组位置累加(统计每个数的个数)
}
for (i=1;i<=2000;i++)
while (a[i])
{
cout <<i <<" ";
a[i]--; //按顺序输出
}
return 0; //华丽结束
}
计数排序虽然耗时短,但有两个局限:
1、要排序的类型必须是有序类型(如整形),像浮点型这种无序数据类型则不能用计数排序。
2、要排序的数据必须有固定的小范围,否则就会数组太大爆内存。
九、sort函数
以下内容来自我自己的文章,若想获得更佳的阅读效果,请点击链接。
sort () 函数包含在头文件为 #include<algorithm> 的 c++标准库中,是一种类似于快排的方法,时间复杂度为O(nlogn)。
sort () 函数有三个参数:
(1)要排序的数组的起始地址。
(2)结束的地址(最后一位要排序的地址)
(3)参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。
使用样例 —— 共 n 个整数,从小到大排序:
#include <algorithm>
#include <iostream>
using namespace std;
int n,a[20000];
int main()
{
cin >>n;
for (int i=1;i<=n;i++)
cin >>a[i];
sort (a+1,a+n+1); //由于是从 1 开始放,所以要加一,默认为从小到大排序
for (int i=1;i<=n;i++)
cout <<a[i] <<" ";
return 0;
}
使用样例 —— 共 n 个整数,从大到小排序:
现在该怎么办呢? 要用到 sort () 的第三个参数了,使用方法如下:
//需要加入一个比较函数 complare(),此函数的实现过程是这样的
bool complare(int a,int b)
{
return a>b;
}
然后:
#include <algorithm>
#include <iostream>
using namespace std;
int n,a[20000];
bool complare(int a,int b)
{
return a>b;
}
int main()
{
cin >>n;
for (int i=1;i<=n;i++)
cin >>a[i];
sort (a+1,a+n+1,complare); //现在不需要给 complare 函数加参数。
for (int i=1;i<=n;i++)
cout <<a[i] <<" ";
return 0;
}
就可以了。
sort () 函数的速度很快,使用很方便,不需要自己打一些复杂代码,还能根据数据量来选择排序算法,非常的good!
十、结尾
创作不易,给个3连吧~~