攻城狮凌风

排序法汇总

1.基本概念

    主关键字唯一区分不同数据,否则为次关键字。主关键字排序具有唯一性,次关键字否。若相同次关键字的元素排序可能发生交换顺序,则称算法不稳定。常用排序算法优劣衡量指标:

     ❶时间性能好。即较少的关键字比较次数和元素移动次数。

     ❷空间性能好。即辅助缓存小。

     ❸稳定性。即相同次关键字元素,排序前后相对位置恒定


2.排序方法汇总

2.1直接插入(二分法)排序

    ❶原理:从第2个元素开始,假设前面的元素已经排序完毕,每次将后序一个元素插入至前面合适位置。
    ❷特点:稳定。适用基本有序或者排序或者元素个数少。
    ❸伪代码:

viod insertSort (int data[],int n)
int temp;
int i,j;
for i=1:n-1
   temp=data[i];
   j=i-1;
   while(j>-1 &&temp <A[j])
       A[j+1]=A[j];
       j--;
   A[j+1]=temp;
end

         二分法直接插入。先寻找合适的插入位置。继而移动元素,然后插入。减少了比较次数,移动次数未变。

    

template<typename T, unsigned int n>
void insertSort(T (&data)[n]){
	int temp, mid;
	int low, high;
	int i, j;
	for (i = 1; i < n; i++){
		temp = data[i];

		if (data[0]>data[i])
			low = 0;
		else if (data[i] < data[i - 1]){
			low = 0;
			high = i - 1;
			while (low<high){//寻找low,使得倒数第一个data[low]>temp  
				mid = (low + high) / 2;
				if (data[mid]>temp)
					high = mid;
				else
					low = mid + 1;
			}
		}
		else
			low = i;

		for (j = i - 1; j >= low; j--)//数据向后移动  
			data[j + 1] = data[j];
		data[low] = temp;// 插入数值
	}
}


2.2 希尔排序:

    ❶原理:又称最小增量排序。希尔排序先将元素分为若干不重叠小组。每个小组分别直接排序。小组个数逐渐减小。最后在一个组排序完成。希尔排序基于直接排序在基本有序元素列的高效率考虑。
    ❷特点:不稳定。
    ❸伪代码:

void shellSort(int data,int n,int d[],int m)


int span,temp;
int i,j,k,key;
for(i=0;i<m;i++)//m个跨度,d[m]为步长,最后一个增量为1
{
    span=d[m];
    for(k=0;k<span;k++)//至多span个分组,data[0]......data[span-1]分别为span个分组的起始位置。
       for(j=k+span;j<n;j+=span)//每个分组使用插入排序。
	   temp=data[j];
           key=j-span;
	   while(key>=0&&temp>data[key])
	       data[key+span]=data[key];
	       key-=span;
           data[key+span]=temp;	  
}

   分析:shell排序时间性能优于直接选择排序。

         1)初始时候步长比较大,每组元素少,排序很快。

         2)后来基本有序,使用直接插入的时间也较快。

2.3直接选择排序

     ❶原理:选取最小的元素作为第一个元素,继而寻找第2大的元素作为第2个元素。继而依次....
     ❷特点:不稳定。
     ❸伪代码:

viod insertSort (int data[],int n)
  int min;
  int temp;
  for(int i=0;i<n-1;i++)
    min=i;
    for(int j=i+1;j<n;j++)//寻找i---n-1最小元素位置
      if(data[min]>data[j])
        min=j;
    if(min!=i)//将最小元素放在比较序列的最前面
	temp=data[i];
	data[i]=data[min];
	data[min]=temp;

 

   

2.4堆排序

      ❶堆:以大跟堆为例,满足3个性质。根节点最大;从大根堆至任何叶子节点的路径,非递增有序;任意非空左右子树均为大根堆。
      ❷原理:调整堆
      ❸特点:不稳定。不适宜元素少或者排列有序的情况,适合元素多且乱序情况。堆不稳定因为需要每次最后一个元素和堆顶元素交换。

void adjustHeap(int A[],int n,int k)//调整堆,已知A[k+1]----A[n-1]已经为堆。调整根节点为A[k]的堆
{
	int j,temp;
	bool flag=false;
	temp=A[k];//暂存根节点值
	j=2*k+1;
	while(j<n&&!flag)
	{
		if(j<n-1&&A[j+1]>A[j])//j指向左右节点的关键字最大
			j++;
		if(temp>A[j])
			flag=true;
		else
		{
			A[k]=A[j];
			k=j;//更新k
			j=2*k+1;//更新j
		}
	}
	A[k]=temp;//根节点赋值给当前子节点
}
void heapSort(int A[],int n)
{
	int temp;
	for(int i=(n-2)/2;i>=0;i--)//从非叶子节点开始,从后到前调整建立堆
	   adjustHeap(A,n,i);

	for(int i=n;i>1;i--)//将根节点(最大值)与最后一个叶子节点的值交换,i-表示长度
	 {
	   temp=A[0];
	   A[0]=A[i-1];
	   A[i-1]=temp;
	   adjustHeap(A,i-1,0);//调整长度序列为i-1。
	 }
}


2.5冒泡排序

     ❶原理:两两比较大小,将较大的元素像右移动。因此先是最大元素移动至列尾,继而是次最大..........
     ❷特点:比直接插入和直接排序移动次数多。是最慢的一种方法。
     ❸伪代码:

void bubbleSort(int data[],int n)
{
     int temp=0;
	 int flag=true;
	 for(int i=1;i<n&&flag;i++)
	     flag=false;
	     for (int j=0;j<n-i;j++)
	        if(data[j]>data[j+1])
		      {
			     flag=true;//代表该次循环中发生过交换。倘若没有发生,则不需要下一次循环。
			     temp=data[j];
			     data[j]=data[j+1];
			     data[j+1]=temp;
			   }
}


2.6快速排序

    ❶原理:又称为划分排序。选取基本元素,将小于基本元素向前移动。大于基本元素的向后移动。最后将基准元素左右2边分别作为子区间递归调用。
    ❷特点:需要附加栈空间。不稳定(涉及到元素交换)。平均速度最快。可以比较居中元素,前后元素。取三者中居中的作为基本元素,并将其移动至队前列。辅助空间O(log2n).
    ❸伪代码:
void quickResort(int data[], int low, int high)
{
	int i = low;
	int j = high;
	int temp = data[i];//选取第一个元素为中间元素  
	while (i < j){
		while (i<j&&data[j] >= temp)
			j--;
		data[i] = data[j];
		while (i<j&&data[j] < temp)
			i++;
		data[j] = data[i];
	}
	data[i] = temp;
	if (low<i - 1) //左区间不止一个元素   
		quickResort(data, low, i - 1);
	if (i + 1<high) //右区间不止一个元素   
		quickResort(data, i + 1, high);
}




2.7归并排序

     ❶原理:将2个有序序列合并为一个有序序列。从2个元素合并一直进行到最后。
     ❷特点:稳定。由于需要将2个数组合并,因此需要辅助数组。空间复杂的相对较高。
     ❸伪代码:

mergeSort(int data[],int n,int low,int high){
      mid=(low+high)/2;
      if(mid-low>0)
	 mergeSort(data,n,low,mid);
     if(high-mid>1)
	 mergeSort(data,n,mid+1,high);
      
     int i=low,j=mid+1,k=0;
     int* temp=new int[high-low+1];
     while(i<=mid&&j<=high)
        if(data[i]<data[j])
            temp[k++]=data[i++];
        else
           	temp[k++]=data[j++];
    
    while(i<=mid)
      temp[k++]=data[i++];
    while(j<=high)
      temp[k++]=data[j++];
	
   k=low;
   while(k<=high)
     data[k]=temp[k]
     k++;

}

2.8计数排序

      1.设n个数,范围为n。则时间复杂度为O(m+n),且需要辅助内存O(n),为稳定排序。一般而言,计数排序以空间换性能,且一般适用于正数数组排序。当且仅当n不是很大时,计数排序十分有效。

      2.思想:不是基于比较的排序:

           1)统计数组中每个值为i的元素出现的次数,存入数组counter[i]

           2)循环遍历counter[i],使得counter[i]=counter[i]+counter[i-1]。目的是统计待排序数组中,小于等于i的元素个数

           3)从后向前遍历data[j], 则输出的数组中sort[--counter[data[j]]]=data[j]。注意从后向前遍历data[]保证了计数排序的稳定性。

void countSort(int data[], int n,int sorted[],int n){
    
	int max=data[1]
	for(int i=1;i<n;i++)
	     if(max<data[i])
		      max=data[i];

	int * count=(int*)malloc((max+1)*sizeof(int));
	if(!count)
	    exit(1)
    
	for(int i=0;i<n;i++)
	    count[data[i]]++;
	
	for(int i=1;i<=max;i++)
	   count[i]=count[i]+count[i-1];
	   
	for(int i=n-1;i>-1;i--)
	    sorted[--count[data[i]]]=data[i];
	   	
	free(count);
}


2.9 桶排序

        思想:通过映射函数将n个数映射至m个桶中(比如划分区间),在单独对每个桶的数进行单独排序比如快速排序。时间复杂度为O(n+n*log(n/m)),最好为O(n)此时每个桶只有一个数,但是空间很大,最坏为log(nlogn),此时所有的数都被分到一个桶里面。

        桶排序的稳定性取决于单个桶的排序算法。若是快速排序则不稳定。

       计数排序是桶排序的特例,此时映射函数未f(x)=x,桶的个数为max+1


2.10基数排序

      一种适用于关键值为整形的高效排序法。设数字为m位d进制数(如1234为4位10进制数)。则定义d个桶。对于总数为n的d进制位的数。最好最差平均时间均为O(m*n),辅助存储方面,若采用链式队列,则为O(n);考虑顺序队列,因为要考虑最差情况,则O(n*d),此时被n个数进入同一个队列,实现需要分配d*n个辅助存储单元.

       思想:先排最低位进桶,再排次地位进桶,依次按序号进桶。

                   桶使用队列存储,保证先进先出。为稳定算法。

//m位d进制
void radixSort(int data[],int length,int m,const int d){
	queue<int>* q=new queue<int>[d];

	int power;
	for(int i=0;i<m;i++){
		if(i==0)
			power=1;
		else
			power=power*d;

		int k=0;
		for(int j=0;j<length;j++){
			k=data[j]/power-(data[j]/(power*d))*d;
			q[k].push(data[j]);
		}

		k=0;
		for(int j=0;j<d;j++){
			while(!q[j].empty()){
				data[k++]=q[j].front();
				q[j].pop();
			}
		}
	}
	delete []q;
}



3.排序方法比较

   

    ❶从时间角度。
         平均。直接插入,直接选择,冒泡均为O(n.^2)。堆,归并,快速为O (nlogn)。希尔排序基于2者之间。
         最好。直接插入,冒泡和直接插入为O(n)最好。快速,归并,堆次之为 O (nlogn)。
         最差。堆和归并仍然为 O (nlogn),其它为O(n.^2)
    ❷从空间的角度。
        归并最差,需要O(n)。快速为O(logn)(辅助栈空间)。其它均为O(1)。
    ❸从稳定角度
        只要直接插入,归并,冒泡,基数,计数 排序稳定。
    ❹总结:
        数目多且随机。内存容许要求稳定可以用归并;内存不容许或者不要求稳定,可以用堆排序。直接插入和冒泡在最好的情况下性能最好。当n比较大且随机分布时,快速排序效果好。当n很大时且关键字很小,使用基数排序。

       

       ❺.std::sort封装了快速排序算法,因此是不稳定的,如果要使用稳定排序,可以用std:stable_sort

       另外,二叉树排序法的时间复杂度,最坏为O(n.^2),平均为O(nlogn).空间复杂度为O(n)




  

posted on 2015-03-18 17:36  攻城狮凌风  阅读(177)  评论(0编辑  收藏  举报

导航