最古老的算法莫过于对什么东西进行排序,现对于基本的排序算法 进行下总结,主要参考书籍 《算法导论 第二版》
1.1 插入排序简介
算法简单化类似于玩扑克游戏,在每次抓牌的过程中,总是按照一定的顺序将牌弄好(将小牌从左至右依次排好),当本次抓的牌比手中已有的牌的某一张小,就将它插入那张牌的前面,所以插入排序就类似于这样的一个过程。
对于 给定的输入:A = <5,2,4,6,1,3> 对应于相应的输出 A' = <1,2,3,4,5,6>
1.2 C++源码Demo
#include <iostream>
#include <iomanip>
#include <cstring>
using namespace std;
void insertion_sort(int *a,int len){
int i,j;
for(j=1;j<len;j++){ //模拟每次抓牌,插牌的过程
int key = a[j];
i = j-1;
while(i>=0 && a[i]>key){
a[i+1]=a[i];
i--;
}
a[i+1] = key; //对应于上面的i--
}
}
int main()
{
int a[6] = {5,2,6,1,3,4};
int _len = sizeof(a)/sizeof(int);
insertion_sort(a,_len); //函数调用
/*
**输出排好顺序的数组,同时里面还有包含了格式化的输出 setw 对应的头文件是#include <iomanip>
*/
int i;
for(i=0;i<_len;i++)
cout<<setw(5)<<a[i];
cout<<endl;
return 0;
}
2. 希尔排序
2.1 希尔排序简介
希尔排序先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。希尔排序源于插入排序,同时也是对插入排序的改进。希尔排序主要本质就是将分组之后进行插入排序,只是引入了个缩减量。
2.2 希尔排序C++代码
#include <iostream>
using namespace std;
void shell(int *a,int step){
int d,i,j,key;
/*
**对数据进行分组,在对分组进行插入排序,模拟插入排序过程
*/
for(d=step/2;d>=1;d/=2){
for(i=d;i<step;i++){
key = a[i];
for(j=i-d;(j>=0&&a[j]>key);j=j-d){
a[j+d] = a[j];
}
a[j+d] = key;
}
}
}
int main()
{
int a[10] = {49,38,65,97,26,13,27,49,55,4};
int len = sizeof(a)/sizeof(int);
shell(a,len);
int i;
for(i=0;i<len;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
3 选择排序
3.1 选择排序简介
选择排序主要就是每次的选择都将最大(或最小)的放在相应的位置上面。
3.2 选择排序C++代码
#include <iostream>
using namespace std;
void select_sort(int *a,int len){
int i,j,pos;
for(i=0;i<len;i++){
pos = i;
for(j=i+1;j<len;j++){
if(a[j]<a[pos]){
pos = j; //记录每次选择的最小位置
}
}
swap(a[pos],a[i]);
}
}
int main()
{
int a[5] = {10,21,2,3,5};
int _len = sizeof(a)/sizeof(int);
select_sort(a,_len); //函数调用
int i=0;
for(;i<_len;i++)
cout<<"a["<<i<<"]="<<a[i]<<" ";
cout<<endl;
return 0;
}
4 冒泡排序
4.1 冒泡排序简介
冒泡排序主要思想类似于水泡,较重的水泡沉下去,较轻的水泡浮上来。
4.2 冒泡排序C++代码
#include <iostream>
using namespace std;
void maopao_sort(int *a,int len){
int i,j;
for(i=0;i<len;i++){
/*
**每轮比较完之后,
**最大的都会是在最下面
**len-i-1 比较几轮,减少几次
*/
for(j=0;j<len-i-1;j++){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
}
}
}
int main()
{
int a[5] = {10,21,2,3,5};
int _len = sizeof(a)/sizeof(int);
maopao_sort(a,_len); //函数调用
int i=0;
for(;i<_len;i++)
cout<<"a["<<i<<"]="<<a[i]<<" ";
cout<<endl;
return 0;
}
5.归并排序
5.1 归并排序简介
归并
排序是建立在归并操作上的一种有效的
排序算法。该算法是采用
分治法(Divide and Conquer)的一个非常典型的应用。值得注意的是归并排序是一种
稳定的排序方法。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序
表
,称为二路
归并
。
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
5.2 归并排序C++代码
#include <iostream>
#include <climits>
#include <cmath>
using namespace std;
/*
**分割几部分 p代表开始位置,q为中间位置
**r为结束位置
*/
void merge(int *a,int p,int q,int r){
int n1 = q-p+1;
int n2 = r-q;
int l[n1+1],R[n2+1]; //二路归并
for(int i=1;i<=n1;i++)
l[i] = a[p+i-1]; //越界问题
for(int j=1;j<=n2;j++)
R[j] = a[q+j];
//结尾标志,带哨兵
l[n1+1] = INT_MAX;
R[n2+1] = INT_MAX;
int i=1,j=1; //从一开始
for(int k=p;k<=r;k++){
if(l[i]<R[j]){
a[k] = l[i];
i++;
}
else{
a[k] = R[j];
j++;
}
}
}
void merge_sort(int *a,int p,int r){
int q;
if(p<r){
q = floor((p+r)/2);
merge_sort(a,p,q);
merge_sort(a,q+1,r);
merge(a,p,q,r);
}
}
int main()
{
int a[5] = {10,21,2,3,5};
int _len = sizeof(a)/sizeof(int);
merge_sort(a,0,_len-1); //函数调用
int i=0;
for(;i<_len;i++)
cout<<"a["<<i<<"]="<<a[i]<<" ";
cout<<endl;
return 0;
}
6. 快速排序
6.1 快速排序简介(传统快排)
快速排序是对冒泡的改进,假使第一个元素作为标记元素,采用快排使得,分为俩部分,一部分比这个标记元素要小,而另一部分要比这个标记元素要大,同时迭代在各自的部分之内寻找新的子部分,直至排好顺序。
示例代码采取第一个元素作为标记元素:
6.2 快速排序C++源码
#include <iostream>
using namespace std;
int Partion(int *a,int p,int r){
int key,i,j;
key = a[p];
i=p;
for(j=p+1;j<=r;j++){
if(a[j]<key){
i++;
swap(a[i],a[j]);
}
swap(a[i],a[p]);
}
return i+1; //越界考虑
}
void quick_sort(int *a,int p,int r){
if(p<r){
int q = Partion(a,p,r);
quick_sort(a,p,q-1);
quick_sort(a,q+1,r);
}
}
int main()
{
int a[5] = {10,21,2,3,5};
int _len = sizeof(a)/sizeof(int);
quick_sort(a,0,_len-1); //函数调用
int i=0;
for(;i<_len;i++)
cout<<"a["<<i<<"]="<<a[i]<<" ";
cout<<endl;
return 0;
}
7. 计数排序
7.1 计数排序简介
计数排序是一种非比较的排序算法,而是根据统计次数和相应的位置进行存放数据,适用于数据中的最大值(k)不是远远大于数据个数(n),在一定的范围内此排序算法较快。
7.2 计数排序c++代码
#include <iostream>
using namespace std;
void counting_sort(int *a,int len,int k){
int b[len];
int c[k+1];
for(int i=0;i<=k;i++){
c[i] = 0;
}
//计数 每个数据出现次数
for(int j=0;j<len;j++){
c[a[j]] = c[a[j]]+1;
}
//位置 记录位置
for(int i=1;i<=k;i++){
c[i] = c[i]+c[i-1];
}
for(int j=len-1;j>=0;j--){
b[c[a[j]]] = a[j];
c[a[j]] = c[a[j]]-1;
}
int i=1;
for(;i<=len;i++)
cout<<"b["<<i<<"]="<<b[i]<<" ";
cout<<endl;
}
int main()
{
int a[5] = {2,3,3,1,4};
int _len = sizeof(a)/sizeof(int);
int k=4; //(a数据中的最大值 直接用来表示了 类似k=max(...)情况)
counting_sort(a,_len,k); //函数调用
return 0;
}
8. 堆排序
8.1 堆排序简介
堆数据结构是一组数组对象,可以被视为是一棵完全二叉树,每一层都是填满的,出了最后一层,但是最后一层也是从左向右填充,length(A)是数组中元素的个数,而heap-size(A)是存放在A中的堆元素个数,一般heap-size(A)<= lenght(A),树的根为A[1],给定某个节点下标i,父节点Parent(i),左孩子left(i),右孩子(i)表示为:
Parent(i)return floor(i/2); left(i) return 2*i; right(i) return 2*i+1;
堆的数据结构 包括大根堆 ,小根堆。大根堆,顾名思义根的值要大于孩子的值,而小根堆则是根的值 则要小于孩子的值,在堆的排序算法中,此文采用的是大根堆。
堆排序的步骤:
1.对于输入数组,和下标组成大根堆
2.建堆
3排序
8.2 堆排序C++代码
#include <iostream>
#include <cmath>
using namespace std;
//父节点
int Parent(int i){
return floor(i/2);
}
//左孩子
int heap_Left(int i){
return 2*i;
}
//右孩子
int heap_Right(int i){
return 2*i+1;
}
//最大堆
void max_heapify(int *a,int i,int heap_size){
int l = heap_Left(i);
int r = heap_Right(i);
int largest;
if(l<=heap_size-1 && a[l]>a[i])
largest = l;
else
largest = i;
if(r<=heap_size-1 && a[r]>a[largest])
largest = r;
if(largest != i){
swap(a[i],a[largest]);
max_heapify(a,largest,heap_size);
}
}
//建堆
void build_heap(int *a,int len){
for(int i=floor(len/2);i>=0;i--)
max_heapify(a,i,len);
}
//堆排序
void heap_sort(int *a,int len){
build_heap(a,len);
for(int i=len-1;i>=1;i--){
swap(a[i],a[0]);
len--;
max_heapify(a,0,len);
}
}
int main()
{
int a[6] = {10,21,2,3,5,37};
int _len = sizeof(a)/sizeof(int);
heap_sort(a,_len); //函数调用
int i=0;
for(;i<_len;i++)
cout<<"a["<<i<<"]="<<a[i]<<" ";
cout<<endl;
return 0;
}