您的位置:首页 > 其它

常见排序算法总结

2012-07-16 10:15 351 查看
参考了大牛的代码,自己理解整理。vs2008运行正确,如发现有误,请各位大牛指正!

// Sort.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

const int len = 100;
class CSort
{
public:
CSort();
~CSort();
void swap(int &a,int &b);
virtual void sort()=0;   //虚函数
friend ostream& operator<<(ostream& out,const CSort& csort); //流操作符重载,定义为友元,方便类之间的数据共享

protected:
int *arr;
int length;
};//注意此处应该有分号

CSort::CSort():length(len)  //构造函数,初始化数组
{
arr = new int[length];
for (int i=0; i<length;i++)
{
arr[i] = rand()%1000;//产生随机数
}
}

CSort::~CSort()   //析构函数,释放申请的内存空间
{
delete[] arr;
arr = NULL;
}

void CSort::swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}

ostream& operator<<(ostream& cout,const CSort& csort)
{
for (int i=0; i<csort.length;i++)
{
cout<<csort.arr[i]<<" ";
}
cout<<endl;
return cout; //注意
}

//插入排序  从小到大
class CInsertSort : public CSort
{
public:
void sort();
};

/************************************************************************/
/* 直接插入排序:
初始状态认为第一个元素是一个有序的序列,从第二个元素开始逐个插入到有序序列中
的正确位置。当有序序列的最后一个元素比当前元素(用key记录)大,则需要插入,这时
需要移动有序序列中的部分元素,腾出当前元素应该插入的正确位置;否则不插,当前
元素不动。
时间复杂度:O(n*n)  最好情况:O(n)
*/
/************************************************************************/
void CInsertSort::sort()
{
int i,j;
int key = 0;//当前待插入的数
for (i=1; i<length; i++)
{
if (arr[i-1] > arr[i])//当有序序列的最后一个比当前大,则需要插入,否则不插
{
key = arr[i];
for (j = i-1;j>=0 && arr[j]>key;j--)//寻找待插入数的正确位置
{
arr[j+1] = arr[j];//后移
}
arr[j+1] = key;
}
}
}

//折半插入排序
class CBInsertSort : public CSort
{
public:
void sort();
};

/*
折半插入排序:
与直接插入排序所不同的是,在有序序列中找到当前元素的正确位置时
采用折半查找的方式,而不是挨个儿比较。
时间复杂度:O(n*logn)
*/
void CBInsertSort::sort()
{
int i,j;
for (i=1; i<length; i++)
{
int low = 0;
int high = i-1;
int key = arr[i];
//通过折半找到插入的位置
while(high >= low)//取等号的时候仍要执行
{
int mid = (low + high)/2;
if (arr[i] > arr[mid])
{
low = mid + 1;
}
else
{
high = mid -1;
}
}
//找到插入位置high+1后,移位
/*for (j=i-1;j>=high+1;j--)
{
arr[j+1] = arr[j];
}
arr[high+1] = key;*/
//找到插入位置low后,移位
for (j=i-1;j>=low;j--)
{
arr[j+1] = arr[j];
}
arr[low] = key;
}
}

//希尔排序
class CShellInsertSort : public CSort
{
public:
void sort();
private:
void shellinsert(int inc);
};

/**
希尔排序:
又称缩小增量的插入排序。
增量一定的时候,将直接插入排序中的1改为增量inc即可。
相比直接插入排序好处在哪?
时间复杂度:O(n^1.3)
*/
void CShellInsertSort::sort()
{
int a[3]={5,3,1};//增量
for (int i=0; i<3; i++)
{
shellinsert(a[i]);
}
}

void CShellInsertSort::shellinsert(int inc)//希尔排序,将直接插入排序中的1改为增量inc即可
{
int i,j;
int key = 0;//当前待插入的数
for (i=inc; i<length; i++)
{
if (arr[i-inc] > arr[i])//当有序序列的最后一个比当前大,则需要插入,否则不插
{
key = arr[i];
for (j = i-inc;j>=0 && arr[j]>key;j-=inc)//寻找待插入数的正确位置
{
arr[j+inc] = arr[j];//后移
}
arr[j+inc] = key;
}
}
}

//冒泡排序
class CBubbleSort : public CSort
{
public:
void sort();
};

/**
冒泡排序:
比较长度减1趟,每趟都是从第一个元素开始依次比较,如果前者比后者大,则交换位置
每趟找到一个正确的位置,最大值、次大值。。。
时间复杂度:O(n^2)  最好情况为:O(n)
*/
void CBubbleSort::sort()
{
//bool change = true;//这样设置的目的是什么?可以减少比较次数吗?
for (int i=1; i<=length-1; i++)
{
//change = false;//设置排好
for (int j=0; j<length-i; j++)
{
if (arr[j] > arr[j+1])
{
//change = true;//需要调换位置
swap(arr[j],arr[j+1]);
}
}
}
}

//快速排序
class CFastSort : public CSort
{
public:
void sort();
private:
int partition(int low, int high);//进行一次划分,找到一个确定的位置
void fastsort(int low, int high);//枢轴元素左右两部分分别进行快速排序
};

/**
快速排序:
初始状态将第一个元素作为枢轴元素,进行一次划分,找到它的确定位置
左半部分的元素都比枢轴元素小,右半部分的元素都比枢轴元素大
然后分别递归的对左右两部分的元素进行快速排序。
*/
void CFastSort::sort()
{
fastsort(0,length-1);
}

/**
找到枢轴元素的位置,对数组进行划分。
设置两个游标:low,high.如果游标high对应的元素大于或等于枢轴元素,则游标high减1;否则将该值赋洞口
如果游标low对应的元素小于或等于枢轴元素,则游标low加1;否则将该值赋给洞口
当low=high时循环结束,它们所指示的位置就是枢轴元素所在的位置。
时间复杂度:O(n*logn)  最坏情况:O(n^2)
*/
int CFastSort::partition(int low, int high)
{
int key = arr[low];//枢轴元素初始化为第一个元素   为了方便理解,可以成为挖洞
while (low < high)
{
while (low < high && arr[high] >= key)
{
high--;
}
arr[low] = arr[high];//找到第一个比key小的元素
while (low < high && arr[low] <= key)
{
low++;
}
arr[high] = arr[low];//找到第一个比key大的元素
}
arr[low] = key;
return low;
}

void CFastSort::fastsort(int low, int high)
{
if (low < high)
{
int keypos = partition(low,high);
fastsort(low,keypos-1);
fastsort(keypos+1,high);
}
}

//堆排序 从小到大 初始堆为大根堆
class CHeapSort : public CSort
{
public:
void sort();
private:
void heapsort();  //堆排序
void adjustheap(int s,int w); //对堆进行调整 s表示待调整的位置下标,w表示待排序的元素个数
};

void CHeapSort::sort() //实现父类的虚函数
{
heapsort();
}

/**
堆排序:
初始状态按照完全二叉树的结构依次排列形成一个初始堆。然后从第一个非终端节点开始到根结点
依次调整以该结点为根的子树,形成一个大根堆。之后将堆顶元素和最后一个元素交换,输出堆顶元素。
接着调整新形成的堆,注意这时是直接从根结点开始调整堆的。然后将堆顶元素和最后一个元素交换。。。
其中有个很重要的调整堆的操作。整个排序的过程,实际上是在数组中进行的。
时间复杂度:O(n*logn)

*/
void CHeapSort::heapsort()
{
//首先从第一个非终端节点开始调整,初始化为大根堆
for (int i=length/2-1; i>=0; i--)
{
adjustheap(i,length);
}
for (int j=length-1; j>0; j--)
{
swap(arr[0],arr[j]); //将堆顶元素将最后一个元素交换
adjustheap(0,j);  //交换后,重新调整堆为大根堆
}
}

/**
调整堆的过程:
有两个参数:s表示待调整的位置下标,w表示待排序的元素个数
局部变量i表示的是最大值的下标
用key记录待调整的元素,比较时是从它的左孩子开始的,
注意要判断待调整的元素是否有两个孩子。如果它比孩子大,则不需要调整
否则将较大的孩子的值赋给待调整的元素。s变为该孩子的下标,继续比较。
最后找到待调整元素的正确位置。调整结束
*/
void CHeapSort::adjustheap(int s,int w)
{
int key = arr[s];//key记录待调整的元素
for (int i=2*s+1; i<w; i=2*i+1)
{
if (i<w-1 && arr[i]<arr[i+1])  //下标i表示的是最大值的下标,i<m-1:保证其有左右孩子时,才进行左右孩子的比较
{
i++;
}
if (key>arr[i]) //比孩子都大,不需要调整
{
break;
}
arr[s]=arr[i];
s=i;
}
arr[s]=key;
}

//选择排序
class CSelectionSort : public CSort
{
public:
void sort();
};

/*
选择排序:
比较长度减1次,每次找到一个最小值,和未排序序列中的第一个元素(即i所记录的)进行交换
用一个下标记录最小值的下标
时间复杂度:O(n^2)
*/
void CSelectionSort::sort()
{
for (int i=0; i<length-1; i++)
{
int minpos = i;//minpos表示最小值的下标
for (int j = i+1; j<length; j++)
{
if (arr[j] < arr[minpos])
{
minpos = j;//遍历一趟找到最小值的下表
}
}
if (minpos!=i)
{
swap(arr[i],arr[minpos]);//注意此处是交换
}
}
}

//归并排序
class CMergingSort : public CSort
{
public:
void sort();
private:
void mergesort(int left,int right);
void merge(int left,int mid,int right);
};

void CMergingSort::sort()
{
mergesort(0,length-1);
}

/*
归并排序:
采用递归的思想,从中间将数组划分为两部分,然后分别递归的对两部分进行归并排序
然后将两部分排好序的合并到一起,完成排序。
时间复杂度:O(n*logn)
*/
void CMergingSort::mergesort(int left,int right)
{
if (left<right)
{
int mid = (left+right)/2;
mergesort(left,mid); //左半部分排好序
mergesort(mid+1,right); //右半部分排好序
merge(left,mid,right); //左右两部分排序
}
}

/*
归并两个排好序的部分的过程:
申请一个临时数组,挨个儿比较两个部分里的元素,先将小的元素加入到临时数组中
最后将临时数组中的元素复制到原数组中即可
*/
void CMergingSort::merge(int left,int mid,int right)
{
int i=left;
int j=mid+1;
int *tem = new int[right-left+1];//申请和数组大小相等的内存空间 临时空间
int k=0;

while (i<=mid && j<=right)
{
if (arr[i] < arr[j])
{
tem[k++]=arr[i++];
}
else
{
tem[k++]=arr[j++];
}
}

while (i<=mid)
{
tem[k++]=arr[i++];
}

while (j<=right)
{
tem[k++]=arr[j++];
}

for (i=0,j=left;j<=right;i++,j++)
{
arr[j]=tem[i];
}
delete[] tem;
}

int _tmain(int argc, _TCHAR* argv[])
{
CSort *pcsort;
//pcsort = new CInsertSort();  //插入排序
//pcsort = new CBInsertSort();  //折半插入排序
//pcsort = new CShellInsertSort(); //希尔排序
//pcsort = new CBubbleSort();  //冒泡排序
//pcsort = new CFastSort();//快速排序
//pcsort = new CSelectionSort();//选择排序
//pcsort = new CHeapSort();//堆排序
pcsort = new CMergingSort();//归并排序
cout<<"排序前:"<<endl;
cout<<*pcsort;
pcsort->sort();
cout<<"排序后:"<<endl;
cout<<*pcsort;

system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: