数据结构--排序(上)冒泡、插入、希尔、堆排、归并

以下算法默认以排成从小到大形式

冒泡排序 O(n^2)

冒泡排序就是遍历一次把最大的放到最后面,下一次排序把第二大的放到倒数第二个位置,直到第一个位置,即从小到大排序。

void Bubble_Sort(ElementType A[] , int n)
{
	for (int i=n-1;i>=0;i--)
	{
		int flag = 0 ;
		for (int j=0;j<i;j++)
		{
			
			if (A[j]>A[j+1])
			{
				Swap(A[j],A[j+1]);
				flag = 1;  //记录是否发生交换 
			}
		}
		if (flag==0) break ; //未发生交换说明有序,结束排序 
	}
}

插入排序 O(n^2)

插入排序就像城里扑克牌一样,一张张的插入,没插入一张,从后往前推,如果比插入的大则往后退一个位置,直到找到比插入的数小的位置时,放入即可。

void Insertion_Sort(ElementType A[] , int n)
{
	int p , i;
	for (i=1;i< n;i++)
	{
		int item = A[i];
		for ( p = i ; p>0 && A[p-1]>item ;p--)
			A[p] = A[p-1];
		A[p]  =  item ;
	}
}

希尔排序 O(n ^(4/3))

定义一串间隔d ,d从大到小,分别对每个间隔为di 的元素进行插入排序。Sedgewick增量序列是效率最高的。

int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 }	;

排序代码:

void Shell_sort(ElementType A[], int n)
{
	int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 }	;
	int si ,d,i,j,tmp;
	for (si = 0 ;Sedgewick[si]<=n;si++ );//间隔不能超过序列个数
	for ( d = Sedgewick[si] ; d>0 ; d=Sedgewick[--si] ) //间隔逐渐缩小到1
	{
		for ( i= d ; i<n ;i++)
		{
			tmp =  A[i];
			for ( j=i ;j>=d && A[j-d] >tmp;j-=d )
				A[j] = A[j-d];
			A[j] = tmp;
		}
	}
}

堆排序 O(nlogn)

依赖堆删除的方法,每次把堆顶的元素删除,放到数组末尾,然后再调整堆顶,这样重复n次数字就成从小到大排序了。

建堆的地方有一个小细节要注意,之前博客介绍最大堆时第一个下标0的元素是哨兵的,所以PercDown的时候是调整到1的位置,排序的时候没有哨兵因此是到0才是堆顶的结点。

堆讲解:https://blog.csdn.net/qq_20225851/article/details/104148777

void PercDown(ElementType A[] , int p , int n)  //大堆   调整p为堆顶的堆结构   
{
	int item = A[p];
	int parent , child ;
	for (parent = p ; parent*2<=n;parent = child )
	{
		child = parent*2;
		if (child <n && A[child]<A[child+1])
			child ++ ;
		if (item> A[child]) break ;
		else 
			A[parent] = A[child];
	}
	A[parent] = item ;
}
void Heap_sort(ElementType A[] , int n)
{
	n--; //数组下标只有到n-1
	for (int i=n/2 ;i>=0;i--)
		PercDown (A, i,n);  //调整堆
	for (int i=n-1 ;i>0;i--)
		{
			Swap(A[0],A[i+1]); //堆顶元素与数组最后一个元素呼唤, 当前最后一个元素的数组下标是i+1
			PercDown(A,0,i);
		}
}

归并排序 O(nlogn) 稳定

归并排序是利用二分的思想分为两份,分别排序,然后再合并,每以份又可以递归的分成两份。这种排序,不依赖序列的逆序数,是比较稳定的排序方法。

void merge(ElementType A[] , ElementType B[],int L, int R ,int RightEnd)
{
	int LeftEnd = R-1 ;
	int flag  = L ;//初始位置
	int first = L ;
	int last = RightEnd; 
	while (L<=LeftEnd && R<= RightEnd)
	{
		if (A[L] > A[R])
			B[flag++] = A[R++];
		else 
			B[flag++] = A[L++];
	}
	while (L<=LeftEnd) B[flag ++] = A[L++];
	while (R<=RightEnd) B[flag++] = A[R++];
	
	for (int i = first ; i<=last ;i++)
		A[i] = B[i];
}

void Msort(ElementType A[], ElementType B[] ,int first ,int last)
{
	if (first < last)
	{
		int center = (first +last) / 2;
		Msort(A, B , first ,center);
		Msort(A,B,center+1 , last);
		merge(A,B,first,center+1 , last);
	}
	
}
void Merge_sort(ElementType A[], int n)
{
	ElementType *B ;
	B = (ElementType *)malloc(sizeof(ElementType)*n);
	Msort (A,B,0,n-1);
	free (B);
}

测试代码:

#include <iostream>
#include <stdlib.h>
#include <time.h>
#define ElementType int 
#define Max 100000
using namespace std ;

void Swap(int &a, int &b)
{
	int item = a ;
	a= b ;
	b =item ;
}
void Bubble_Sort(ElementType A[] , int n)
{
	for (int i=n-1;i>=0;i--)
	{
		int flag = 0 ;
		for (int j=0;j<i;j++)
		{
			
			if (A[j]>A[j+1])
			{
				Swap(A[j],A[j+1]);
				flag = 1;  //记录是否发生交换 
			}
		}
		if (flag==0) break ; //未发生交换说明有序,结束排序 
	}
}

void Insertion_Sort(ElementType A[] , int n)
{
	int p , i;
	for (i=1;i< n;i++)
	{
		int item = A[i];
		for ( p = i ; p>0 && A[p-1]>item ;p--)
			A[p] = A[p-1];
		A[p]  =  item ;
	}
}



void print(ElementType A[] , int n)
{
	cout <<"排序结果:";
	for (int i=0;i<n;i++)
	cout << A[i]<<" ";
}

void Shell_sort(ElementType A[], int n)
{
	int Sedgewick[] = {0,1, 5 ,19, 41, 109, 209, 505, 929, 2161, 3905 ,8929, 16001, 36289, 64769, 146305, 260609 ,587521, 1045505, 2354689 ,4188161 }	;
	int si ,d,i,j,tmp;
	for (si = 0 ;Sedgewick[si]<=n;si++ );
	for ( d = Sedgewick[si] ; d>0 ; d=Sedgewick[--si] )
	{
		for ( i= d ; i<n ;i++)
		{
			tmp =  A[i];
			for ( j=i ;j>=d && A[j-d] >tmp;j-=d )
				A[j] = A[j-d];
			A[j] = tmp;
		}
	}
}

void PercDown( ElementType A[], int p, int N )
{ /* 改编代码4.24的PercDown( MaxHeap H, int p )    */
  /* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;
    X = A[p]; /* 取出根结点存放的值 */
    for( Parent=p; (Parent*2+1)<N; Parent=Child ) {
        Child = Parent * 2 + 1;
        if( (Child!=N-1) && (A[Child]<A[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= A[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            A[Parent] = A[Child];
    }
    A[Parent] = X;
}
 
void HeapSort( ElementType A[], int N ) 
{ /* 堆排序 */
     int i;
       
     for ( i=N/2-1; i>=0; i-- )/* 建立最大堆 */
         PercDown( A, i, N );
      
     for ( i=N-1; i>0; i-- ) {
         /* 删除最大堆顶 */
         Swap( A[0],A[i] ); /* 见代码7.1 */
         PercDown( A, 0, i );
     }
}

void merge(ElementType A[] , ElementType B[],int L, int R ,int RightEnd)
{
	int LeftEnd = R-1 ;
	int flag  = L ;//初始位置
	int first = L ;
	int last = RightEnd; 
	while (L<=LeftEnd && R<= RightEnd)
	{
		if (A[L] > A[R])
			B[flag++] = A[R++];
		else 
			B[flag++] = A[L++];
	}
	while (L<=LeftEnd) B[flag ++] = A[L++];
	while (R<=RightEnd) B[flag++] = A[R++];
	
	for (int i = first ; i<=last ;i++)
		A[i] = B[i];
}

void Msort(ElementType A[], ElementType B[] ,int first ,int last)
{
	if (first < last)
	{
		int center = (first +last) / 2;
		Msort(A, B , first ,center);
		Msort(A,B,center+1 , last);
		merge(A,B,first,center+1 , last);
	}
	
}
void Merge_sort(ElementType A[], int n)
{
	ElementType *B ;
	B = (ElementType *)malloc(sizeof(ElementType)*n);
	Msort (A,B,0,n-1);
	free (B);
}

int main ()
{
	clock_t start ,stop ;
	double T ;
	int A[Max] ,B[Max];
	for (int i=0 ; i< Max; i++)
		A[i] = rand();
		
	for (int i=0 ; i< Max; i++)
	B[i] = A[i];
	start = clock ();
	Bubble_Sort(B , Max);
	stop = clock();
	T = (double)(stop-start)/CLOCKS_PER_SEC;
	cout <<"冒泡排序用时:"<<T<<"s"<<endl;

	for (int i=0 ; i< Max; i++)
		B[i] = A[i];
	start = clock ();
	Insertion_Sort(B , Max);
	stop = clock();
	T = (double)(stop-start)/CLOCKS_PER_SEC;
	cout <<"插入排序用时:"<<T<<"s"<<endl;

	for (int i=0 ; i< Max; i++)
		B[i] = A[i];
	start = clock ();
	Shell_sort(B , Max);
	stop = clock();
	T = (double)(stop-start)/CLOCKS_PER_SEC;
	cout <<"希尔排序用时:"<<T<<"s"<<endl;
	
	for (int i=0 ; i< Max; i++)
		B[i] = A[i];
	start = clock ();
    HeapSort(B , Max);
	stop = clock();
	T = (double)(stop-start)/CLOCKS_PER_SEC;
	cout <<"堆排序用时:"<<T<<"s"<<endl;
	
	for (int i=0 ; i< Max; i++)
		B[i] = A[i];
	start = clock ();
	Merge_sort(B , Max);
	stop = clock();
	T = (double)(stop-start)/CLOCKS_PER_SEC;
	cout <<"归并排序用时:"<<T<<"s"<<endl;
		
 } 

这是100000个数据每个排序算法的时间对比
在这里插入图片描述
通过对比可以看到,最后三种排序的效率是差不多的,上面五中排序算法,归并排序是最稳定的。
插入会比冒泡快那么多是因为随机生成的数据更有序化,所以会比冒泡快。

程序竞赛时,如果数据范围达到5次方,限时一秒时是不能用O(n^2)的排序算法的,会直接超时因为需要30s左右 的时间,其他算法也是一样的,要设计出nlogn复杂度一下的算法才有可能AC。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值