归并排序和桶排序

归并排序就是将两个或多个有序表合并成一个有序表的过程。若将两个有序表合并成一个表则称为二路归并。

二路归并过程如下:

首先把待排的每一个元素看成一个有序表。n个元素构成n个有序表。接着两两合并,即第一个表和第二个表合并;第三个表和第四个表合并;.....。依次类推,若最后还剩一个表没有合并(即n为奇数),则直接进入下一次两路归并。此为一趟归并。然后再两两合并,直到最后合并为一个表结束。

例如:10个元素的归并排序{45 32 12 77 48 97 2 36 18 26}

    初始每个元素为一个有序表{45} {32} {12} {77} {48} {97} {2} {36}  {18} {26}

     两两合并,完成第一趟排序  {32 45}     {12 77}      {48 97}     {2   36}  {18   26}

             两两合并,完成第趟排序  {12 32 45 77}   {2 36 48 97}  {18 26}

     两两合并,完成第三趟排序  {2 12  32 36 45 48 77 97}   {18 26}

      两两合并,完成第四趟排序 {2 12  18 26 32 36 45 48 77 97} 

//*****************7归并排序*******************************

void Merge(int a[],int b[],int left,int mid,int right)
{
	int i=left,j=mid+1,k=left;
	while(i<=mid && j<=right)
	{
		if(a[i]<=a[j])
			b[k++] = a[i++];
		else
			b[k++]=a[j++];
	}
	while(i<=mid)
		b[k++] = a[i++];
	while(j<=right)
		b[k++] = a[j++];

	for(i=left;i<=right;i++)
		a[i] = b[i];
}

void MergeSort(int a[],int b[],int left,int right)
{
	int mid;
	if(left>1;
		MergeSort(a,b,left,mid);
		MergeSort(a,b,mid+1,right);
		Merge(a,b,left,mid,right);
	}
}

归并排序的分治实现类似快排

归并排序空间复杂度---O(n)  需要辅助数组b[n]

时间复杂度分析  

T(n)=O(1)   if  n=1

T(n)=2T(n/2)+O(n)    if   n>=2

               利用主方法求解T(n)=O(nlgn)

最坏情况和平均情况   O(nlgn)   ----->递归深度lgn   

最好情况 待排有序 O(n)  

稳定性分析

归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有 序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定 性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结 果序列的前面,这样就保证了稳定性。所以,归并排序是稳定的排序算法。

关于归并排序的一个应用是求逆序对数。归并排序中的关键一步是将两个有序的数组合并成一个数组,合并的过程中,依次比较两个有序数组元素大小,以升序有序为例,

(结合下面代码理解)(left <=i <=mid mid+1<=j<=right) 如果a[i] > a[j] 说明出现了逆序对,这时在 i 指向的数组内,a[ i ] 与其后所有元素a[ i ~ mid]所有元素都是逆序的,所以逆序对是mid-i+1;下面直接看代码:

int Merge(int a[],int b[],int left,int mid,int right)  
{  
    int i = left, j = mid + 1, k = left;
	int t = 0;
    while(i <= mid && j <= right)  
    {  
        if(a[i] <= a[j])  
            b[k++] = a[i++];  
        else
		{
			t += (mid-i+1);
            b[k++] = a[j++];  
		}
    }  
    while(i <= mid)  
        b[k++] = a[i++];  
    while(j <= right)  
        b[k++] = a[j++];  
  
    for(i = left; i <= right; i++)  
        a[i] = b[i]; 
	return t;
}   
  
int MergeSort(int a[],int b[],int left,int right)  
{  
    int mid;  
	int inversion = 0;
    if(left> 1;  
        inversion += MergeSort(a,b,left,mid);  
        inversion += MergeSort(a,b,mid+1,right);  
        inversion += Merge(a,b,left,mid,right);  
    }
	return inversion;
}

桶排序

-----典型以空间换时间

基本思想:把区间[0 ,1)划分成n个相同大小的子区间,称为桶。将n个记录分布到各个桶中。如果有多于一个记录被分到同一个桶中,需要进行桶内排序。最后依次把各个桶中的记录列出来就得到有序序列。

显然,桶排序的思想是先预定义了很多区间,把满足区间范围的待排序数放到对应的桶中。

利用 函数的映射关系,减少了几乎所有的比较工作,把大量数据分割成基本有序的数据块(桶),然后只需要对桶中的少量数据做先进的比较排序即可。

/****************************桶排序-升序*************************/
void BucketSort(double * a,int n)  
{  
    //链表结点描述  
    typedef struct Node{  
        double key;  
        struct Node * next;   
    }Node;  
    //辅助数组元素描述  
    typedef struct{  
         Node * next;  
    }Head;  
    int i,j;  
    Node *p,*q,*node;  
	Head head[10]={NULL}; //10个桶,每个桶里放置一个链表 

    for(i=0;ikey=a[i];  
        node->next=NULL;  
        p = q = head[(int)(a[i]*10)].next;//小数*10取整即为桶号  
        if(p == NULL)//桶装入第一个元素时
		{  
            head[(int)(a[i]*10)].next=node;  
            continue;  //进行下一次for循环
        }
		//直接插入排序一个一个插入
        while(p){  
            if(node->key < p->key)  
                break;  //如果逆序,跳到下面else,插入到p前面
            q=p;  //如果顺序,指针后移,再进行if判断
            p=p->next;  
        }  
        if(p == NULL){  
            q->next=node;  
        }else{  
            node->next=p;  
            q->next=node;  
        }  
    }
	//遍历每个桶中的元素
    j=0;  
    for(i=0;i<10;i++){  
        p=head[i].next;  
        while(p){  
            a[j++]=p->key;  
            p=p->next;  
        }  
    }  
}  
  
void test_Bucket_sort()  
{  
    int i;  
    double a[13]={0,0.13,0.25,0.18,0.29,0.81,0.52,0.52,0.83,0.52,0.69,0.13,0.16};
	BucketSort(a,13);  
    
	for(i=0;i<=12;i++)  
        printf("%-6.2f",a[i]);  
    printf("\n");  
} 
空间复杂度 O(n) 

时间复杂度

1 循环计算每个关键字的桶映射函数 O(N)

2对每个桶内的所有数据进行排序,其中第i个桶进行桶内排序时间复杂度为  O(Ni*logNi)    //Ni 为第i个桶中的数据量

                  假设N个待排数据  M个桶 平均每个桶N/M个元素则:

T(N)=  O(N)+O(M*N/M*logN/M) = O(N+C)  其中  C=Nlog(N/M)
稳定性  桶排序是稳定的,对于相同的元素都是按待排次序放入到桶中的,再从桶中收集的时候也是按原来顺序收集的,不会改变顺序。

你可能感兴趣的:(数据结构与算法,归并排序,桶排序)