算法学习小心得——基于比较的排序算法汇总

来源:互联网 发布:visio 数据库建模 编辑:程序博客网 时间:2024/05/04 15:21

这几天学习了基于比较的排序算法,有插入排序、归并排序、堆排序和快速排序。下面自己用C++代码实现了一下:

1、插入排序,插入排序的思想比较简单,就是像我们打扑克牌把手中的牌整理好一样,假设手中的牌是已经排好的,只需要找找到适当的位置把下一张牌插入到已有的牌中就好了。

void InsertionSort(int *A,int beg,int end){int key;for(int i=beg+1;i<=end;i++){//对所有的牌进行遍历,假设手中只有一张牌。key=A[i];//用key来表示要插入的牌int j=i-1;while(j>=0 && A[j]>key){//找到正确的位置:如果手中的牌比要插入的大就往后移,直到正确的位置A[j+1]=A[j];j--;}A[j+1]=key;//把要插入的牌放到正确的位置}}

2、归并排序,归并排序使用了分治的方法,即把问题分为若干个子问题再分别解决每个子问题。主要思想是这样的,如果a和b是两个已经排好序的数组,那么可以比较a和b中的元素大小,把他们合并成一个排好序的数组。所以就把整个要排序的数组进行划分,直到每个字数组中只有一个元素(一个元素肯定是排好序的)。然后开始合并,直到最后合并成原长度的数组。这里用了一个Merge作为合并函数。

void Merge(int *A,int beg,int mid,int end){//合并函数int n1=mid-beg+1,n2=end-mid;int i1=beg,i2=mid+1;vector<int> v;//用了一个vector来做中间数组,这里我纯粹是为了练习,完全可以用一个数组while(i1<=mid && i2<=end){if(A[i1]<A[i2]){v.push_back(A[i1++]);}elsev.push_back(A[i2++]);}while(i1<=mid){v.push_back(A[i1++]);}while(i2<=end){v.push_back(A[i2++]);}int n=beg;auto it=v.begin();while(it!=v.end()){//将合并好的数组拷入到原数组中A[n++]=*it;it++;}}
void MergeSort(int *A,int beg,int end){//进行归并if(beg<end){//如果数组元素不为1,就进行划分int mid=beg+(end-beg)/2;MergeSort(A,beg,mid);MergeSort(A,mid+1,end);Merge(A,beg,mid,end);}}

3、堆排序,堆排序是使用了最大堆这种数据结构。最大堆就是堆顶的元素是堆中最大的。堆排序的主要思想就是用数组来构造最大堆。这样每次把堆顶元素和数组中最后一个元素调换位置,直到堆中只有一个元素,这样数组的元素就从大到小了。这个算法写了一个堆的类,用成员函数进行排序。

class Maxheap{private:int size;//存放堆得大小int length;//存放数组的长度vector<int> data;//存放堆中元素public:Maxheap(){size=0;length=0;}void Add(int value){data.push_back(value);size++;length++;}void MaxHeapify(int i);void BuildMaxheap();void HeapSort();int Get(int i){if(i<length)return data[i];else cout<<"overflow";}int Getlen(){return length;}};void Maxheap::MaxHeapify(int i){//这个函数用来不断的维护堆的性质,因为建堆的时候需要用到,而且调换了堆顶元素和堆中最后一个元素后会破坏堆的性质。int l=2*i+1,r=2*i+2;//找到左孩子和又孩子的序号int largest=i,temp;if(l<size && data[l]>data[i]){largest=l;}if(r<size && data[r]>data[largest]){largest=r;}//将根、左孩子、右孩子中最大的作为根if(largest!=i){//如果左子树和右子树的根变化,可能破坏了子树的性质,所以再次对子树进行维护。temp=data[largest];data[largest]=data[i];data[i]=temp;MaxHeapify(largest);}}void Maxheap::BuildMaxheap(){//建堆size=length;for(int i=length/2-1;i>=0;i--){//因为对树来说,后n/2节点为叶节点,所以不需要维护,只维护前n/2MaxHeapify(i);}}void Maxheap::HeapSort(){//排序BuildMaxheap();//先建堆int temp;while(size>1){//然后不断的把堆顶元素移到堆尾,并且维护堆的性质temp=data[size-1];data[size-1]=data[0];data[0]=temp;size--;MaxHeapify(0);}}
4、快速排序,这个排序相对比较难懂,也是用了分治的策略。把目标数组的最后一个元素作为主元素,然后对前n-1个进行遍历,试数组被分为起点到i,i到j,j到末尾3段,第一段的数都小于主元素,第二段的数都大于主元素,第三段没有排,然后知道第三段的元素都被移除到第一段和第二段。再把主元素和到i+1的位置(大于主元素的位置)的元素调换,这样就被分成了两段,前一段都小于主元素,后一段都大于主元素。再对这两段递归的执行相同算法。
int Partition(int *A,int beg,int end){//用于划分int key=A[end];int i=beg-1,j=beg;int temp;while(j<end){if(A[j]<key){temp=A[++i];A[i]=A[j];A[j++]=temp;}elsej++;}temp=A[++i];A[i]=A[end];A[end]=temp;return i;//返回后i之前的元素都小于A[i],i之后的都大于A[i]}void QuickSort(int *A,int beg,int end){if(beg<end){int p=Partition(A,beg,end);QuickSort(A,beg,p-1);QuickSort(A,p+1,end);}}

除了插入排序的时间复杂度是N平方外,其他都是nlgn,对于快速排序来说这个时间复杂度是平均的,而不是最坏的,当然如果随机的来选取主元素的话,是可以打到更好的效果,这里需要的数学证明比较多,就不说了哈。nlgn也是基于比较排序的算法的时间复杂度下限,也就是最优的。其中归并排序不是原址的,也就是会占用和问题大小相关的存储空间。其他三个算法都是原址的。


原创粉丝点击