顺序如下:
1.各排序算法实现
2.各排序算法比较
1.1选择排序
把选择排序写在前面,因为这是我上学以来学的第一个排序算法
思路:在第i次处理中选出从i到len-1的最小值,将其与当前i位置上的元素进行交换
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
template<typename T>
void selectSort(T *num,int len)
{
int minIndex;
for(int i = 0;i<len;++i)
{
minIndex=i;
for(int j = i+1;j<len;++j)
{
if(num[j]<num[minIndex])
minIndex=j;
}
if(minIndex!=i)
{
num[minIndex]=num[minIndex]^num[i];
num[i]=num[minIndex]^num[i];
num[minIndex]=num[minIndex]^num[i];
}
}
}
template<typename T>
void print(T *num,int len)
{
for(int i=0;i<len;++i)
{
cout<<num[i]<<" ";
}
}
int main()
{
int a[]={3,1,2,5,4};
selectSort(a,5);
print(a,5);
}
易错点:在内循环中将num[i]与num[j]进行比较
正确做法:将当前最小的num[minIndex]与num[j]进行比较
2.2插入排序
2.2.1普通插入排序
思路:在第i次处理中,将第i个元素插入到前i-1个已排好序的元素中间去
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
template<typename T>
void print(T *num,int len)
{
for(int i=0;i<len;++i)
{
cout<<num[i]<<" ";
}
cout<<endl;
}
template<typename T>
void insertSort(T *num,int len)
{
for(int i = 1;i<len;++i)
{
T temp=num[i];
int j=i-1;
for(;j>=0&&num[j]>temp;--j)
{
num[j+1]=num[j];
}
num[j+1]=temp;
}
}
int main()
{
int a[]={3,1,2,5,4};
insertSort(a,5);
print(a,5);
}
注意:要用第三变量保存num[i],num[i]在内层循环中可能被改变。内层循环找到小于等于temp的数的位置就可以截止了,我一开始一直循环到0……结果千奇百怪。
2.2.2折半插入
思路:在找寻插入位置时采用二分查找
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
template<typename T>
void print(T *num,int len)
{
for(int i=0;i<len;++i)
{
cout<<num[i]<<" ";
}
cout<<endl;
}
template<typename T>
void insertSort(T *num,int len)
{
for(int i = 1;i<len;++i)
{
T temp=num[i];
//二分查找
int high=i-1,low=0;
int mid=(high+low)/2;
while(high>=low)
{
if(num[mid]>temp)
high=mid-1;
else
low=mid+1;
mid=(low+high)/2;
}
for(int j=i;j>=low;--j)
{
num[j]=num[j-1];
}
num[low]=temp;
}
}
int main()
{
int a[]={3,1,2,5,4};
insertSort(a,5);
print(a,5);
}
1.3堆排序
堆—— 一颗完全二叉树,任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。下面假设这棵树有m个结点
大顶堆:对i:m/2->1的结点,k[i]>=k[2i]且k[i]>=k[2i+1]
小顶堆:对i:m/2->1的结点,k[i]<=k[2i]且k[i]<=k[2i+1]
堆排序主要包括以下两个步骤
1)初始化堆:将k[1..n]构造为堆;
2)将当前无序区的堆顶元素k[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
#include <iostream>
#include<algorithm>
using namespace std;
void HeapAdjust(int *a,int i,int size) //调整堆
{
int lchild=2*i; //i的左孩子节点序号
int rchild=2*i+1; //i的右孩子节点序号
int max=i; //临时变量
if(i<=size/2) //如果i是叶节点就不用进行调整
{
if(lchild<=size&&a[lchild]>a[max])
{
max=lchild;
}
if(rchild<=size&&a[rchild]>a[max])
{
max=rchild;
}
if(max!=i)
{
swap(a[i],a[max]);
HeapAdjust(a,max,size); //避免调整之后以max为父节点的子树不是堆
}
}
}
void BuildHeap(int *a,int size) //建立堆
{
int i;
for(i=size/2;i>=1;i--) //非叶节点最大序号值为size/2
{
HeapAdjust(a,i,size);
}
}
void HeapSort(int *a,int size) //堆排序
{
int i;
BuildHeap(a,size);
for(i=size;i>=1;i--)
{
swap(a[1],a[i]); //交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
HeapAdjust(a,1,i-1); //重新调整堆顶节点成为大顶堆
}
}
int main()
{
//int a[]={0,16,20,3,11,17,8};
int a[100];
int size;
while(scanf("%d",&size)==1&&size>0)
{
int i;
for(i=1;i<=size;i++)//编码要从1开始,否则非叶节点就不是size/2->1
cin>>a[i];
HeapSort(a,size);
for(i=1;i<=size;i++)
cout<<a[i]<<"";
cout<<endl;
}
return 0;
}
1.4冒泡排序
个人觉得最简单的一个排序
#include<iostream>
#include<algorithm>
using namespace std;
template<typename T>
void BubbleSort(T *a,int size){
for(int i = size;i>0;--i){
for(int j = 1;j<i;++j){
if(a[j]<a[j-1]){
swap(a[j],a[j-1]);
}
}
}
}
template<typename T>
void print(T *a,int size){
for(int i = 0;i<size;++i){
cout<<a[i]<<" ";
}
}
int main()
{
int arr[]={1,3,2,5,4,8,10,9};
BubbleSort(a,8);print(a,8);
}
1.5快速排序
快速排序的思想:在待排序列中选择一个pivot,将序列中除去pivot的数划分为左右两部分:小于pivot和大于pivot,再对左右两部分的序列分别进行快速排序。(递归思想)
#include <cstdio>
#include<algorithm>
using namespace std;
int a[] = { 1, 2, 8, 7, 9, 5, 6, 4, 3, 66, 77, 33, 22, 11 };
/* 输出数组前n各元素 */
void prt(int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("%d\t", a[i]);
}
printf("\n");
}
void quick_sort(int a[], int left, int right)
{
int i = left + 1, j = right;
int key = a[left];
if (left >= right) return;
/* 从i++和j--两个方向搜索不满足条件的值并交换 *
* 条件为:i++方向小于key,j--方向大于key */
while (1)
{
while (a[j] > key)
j--;
while (a[i] <= key&&i<j)
i++;
if(i >= j)
break;
swap(a[i],a[j]);
}
/* 关键数据放到‘中间’ */
swap(a[left],a[j]);
if(left < i - 1) quick_sort(a, left, i - 1);
if(j + 1 < right) quick_sort(a, j + 1 , right);
}
int main(void) {
/* 排序与输出 */
quick_sort(a, 0, 13);
prt(14);
system("pause");
return 0;
}
1.6归并排序
思路:将两个已排好序的序列合并成一个有序序列
#include<iostream>
#include<algorithm>
using namespace std;
void Merge(int *num,int p,int q,int r){
int n1=q-p+1;
int n2=r-q;
int * left = new int[n1];
int * right = new int[n2];
int i;
for(i=0;i<n1;++i){
left[i]=num[p+i];
}
for(i=0;i<n2;++i){
right[i]=num[q+1+i];
}
int j=0,k=0;
for(i=p;i<=r;++i){
if(left[j]<=right[k])
{
num[i]=left[j];
if(j==n1-1){
++i;//此处需先加i
while(i<=r){
num[i]=right[k];
++i;
++k;
}
break;
}
++j;
}
else
{
num[i]=right[k];
if(k==n2-1){
++i;//此处需先加i
while(i<=r){
num[i]=left[j];
++i;
++j;
}
break;
}
++k;
}
}
}
void mergesort(int * num,int p,int r){
if(p<r){
int q=(p+r)/2;
mergesort(num,p,q);
mergesort(num,q+1,r);
Merge(num,p,q,r);
}
}
template<typename T>
void print(T *a,int size){
for(int i = 0;i<size;++i){
cout<<a[i]<<" ";
}
}
int main()
{
int arr[]={1,3,2,5,4,8,10,9};
mergesort(arr,0,7);print(arr,8);
system("pause");
}
1.7希尔排序
每次以渐减增量对序列进行插入排序,最后的一个增量为1(即直接插入排序)
#include<iostream>
#include<algorithm>
using namespace std;
void shellsort(int * num,int size,int incre){
int i,j;
for(i = incre;i<size;++i){
if(num[i]<num[i-incre]){
int temp=num[i];
j=i-incre;
while(j>=0&&num[j]>temp){
num[j+incre]=num[j];
j-=incre;
}
num[j+incre]=temp;
}
}
}
template<typename T>
void print(T *a,int size){
for(int i = 0;i<size;++i){
cout<<a[i]<<" ";
}
}
int main()
{
int arr[]={1,3,2,0,4,8,10,9};
int d=8;//数组元素个数
while(d>1){
d=d/3+1;
shellsort(arr,8,d);
}
print(arr,8);
system("pause");
}
1.8基数排序
多关键字分配与收集。算法(略)
若基数为radix,每个关键字有d位,需要重复执行d趟“分配”与“收集”。每趟对n个对象进行“分配”,对radix个队列进行“收集”。总时间复杂度为O(d(n+radix))。
2.各排序算法对比
衡量排序方法的标准:
排序时所需要的平均比较次数
排序时所需要的平均移动
排序时所需要的平均辅助存储空间
排序的稳定性
由于各项写不太整洁,我贴了我上课时候老师总结的表格:
上表没有希尔排序,因为选取的增量不同希尔排序的效果不同,上面算法我取d/3+1,据说是Knuth提出来的。
资料上的原话是:Knuth利用大量的实验统计资料得出,当n很大时,关键字平均比较次数和对象平均移动次数大约在n^1.25到1.6n^1.25的范围内。这是在利用直接插入排序作为子序列排序方法的情况下得到的。
稳定性排序:插入,冒泡,归并,基数
非稳定性排序:选择,堆排序,快速排序,希尔排序(非相邻元素交换)
依赖于初始顺序的排序:直接插入,冒泡,快排
补充:有些资料上说的交换排序,一般来说指冒泡与快速排序,主要思想是两两比较关键字,如果逆序则进行交换