排序算法总结


一,插入排序

1.直接插入排序

向有序的序列中插入元素,初始时从第二个元素开始,依次将后续元素 i 插入已排好序的序列中(0 -- i)。

稳定排序   时间复杂度O(n)  空间复杂度O(1)

代码

//直接插入排序
void InsertStraight(int a[],int len)
{
	int temp;

	for(int i = 1; i < len; ++i)
	{
		temp = a[i];
		int j = i - 1;
		while(a[j] > temp && j >= 0)
		{
			a[j+1] = a[j];
			--j;
		}
		a[j+1] = temp;

	}


}



2,折半插入排序

对于待插入的数a[ i ] 来说,与上述直接插入算法不同的是,每次参与比较的是 已排好序列的中间元素 a[mid],若a[ i ] > a[mid] 则  low = mid + 1; 否则 high = mid -1  直至 low > high.

稳定排序   时间复杂度O(n)  空间复杂度O(1)

代码

//折半插入
void BinSearch(int a[], int len)
{
	int temp;
	
	for(int i = 1; i < len; ++i)
	{
		temp = a[i];
		int low = 0;
	        int high = i-1;
	
		while(high >= low)
		{
			int mid = (high+low)/2;
			if( a[mid] > temp)
				high = mid - 1;
			else
				low = mid + 1;

		}
		for(int j = i - 1; j >= high + 1; --j)
			a[j+1] = a[j];
		a[high + 1] = temp;

		
	}


}



3.希尔排序

又叫作缩小增量排序,一般取初始增量为待排序列的一半, 每次缩小一半,直至为1.

不稳定排序  希尔排序的分析是一个复杂的问题,它的时间是所取“增量”序列的函数,这涉及到一些数学上尚未解决的难题,时间复杂度不稳定,较好情况下

时间复杂度为 O(n^1.3)   空间复杂度为O(1)

过程如下:


上述图片来自http://blog.csdn.net/cjf_iceking/article/details/7951481


代码

void ShellInsert(int a[], int len, int d)
{
	for(int i = d; i < len; ++i)
	{
		int j = i - d;
		int temp = a[i];
		while( j>=0 && a[j]>temp )
		{
			a[j+d] = a[j]; 
			j -= d;
			
		}

		a[j+d] = temp;
	
	}
}


//希尔排序
void ShellSort(int a[], int len)
{
	int d = len/2; 
	while(d >=1)
	{
		ShellInsert(a, len, d);
		d /= 2;	
	}

}


二,交叉排序
  

1.冒泡排序 

设置flag为优化该算法,若遍历一次没有改动则停止。

稳定排序  平均时间复杂度O(n)  空间复杂度O(1)

代码

//冒泡排序
void BubbleSort(int a[], int len)
{
	bool flag;
	for(int i = 1; i < len; ++i)
	{
		flag = false;
		for(int j = 0 ; j < len - i; ++j )
		{
			if(a[j]>a[j+1])
			{
				flag = true;
				int temp = a[j+1];
				a[j+1] = a[j];
				a[j] = temp;			
			}
		}

		if( flag==false )
			break;
	}


}


2,快速排序

    快速排序具有最好的平均性能(average behavior),但最坏性能(worst case behavior)和插入排序相同,也是O(n^2)。比如一个序列5,4,3,2,1,要排为1,2,3,4,5。按照快速排序方法,每次只会有一个数据进入正确顺序不能把数据分成大小相当的两份,很明显,排序的过程就成了一个歪脖子树,树的深度为n,那时间复杂度就成了O(n^2)。尽管如此,需要排序的情况几乎都是乱序的,自然性能就保证了。据书上的测试图来看,在数据量小于20的时候,插入排序具有最好的性能。当大于20时,快速排序具有最好的性能,归并(merge sort)和堆排序(heap sort)也望尘莫及,尽管复杂度都为nlg(n)。

      快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。” 

  随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降低到O(n^2)。解决方法是用一种方法进行扫描,使没有交换的情况下主元保留在原位置。

    (以上对快速排序的总结来自博客http://www.cnblogs.com/foreverking/articles/2234225.html

稳定排序

快速排序空间复杂度为o(lgn)(针对问题:书本上说快速排序要用深o(lgn)的栈实现递归,但是归并排序同样有递归啊,为什么归并只需o(n)的空间复杂度?)

答:

    归并排序每次递归都要用到一个辅助表,长度与待排序的表长度相同,虽然递归次数是O(lgn),但每次递归都会释放掉所占的辅助空间,所以下次递归的栈空间和辅助空间与这部分释放的空间就不相关了,因而空间复杂度还是O(n)。
    快速排序空间复杂度只是在通常情况下才为O(lgn),如果是最坏情况的话,很显然就要O(n)的空间了。当然,可以通过随机化选择pivot来将空间复杂度降低到O(lgn)。

代码

//快速排序
void QuickSort(int a[], int iBegin, int iEnd)
{
	if(iBegin>=iEnd)
		return;

	int temp = a[iBegin];
	int low = iBegin;
	int high = iEnd;

	while( low!=high )
	{
		while(low<high && a[high]>=temp) --high;
		a[low] = a[high];
		while (low<high && a[low]<= temp) ++low;
		a[high] = a[low];		
	}

	a[high] = temp;

	QuickSort(a, iBegin, low-1);
	QuickSort(a, low +1, iEnd);




}


三,选择排序

1,简单选择排序

每次选取第 i 个最小的数放在位置 i 上。

不稳定排序,时间复杂度O(n^2), 空间复杂度O(1)

例子:

序列5 8 5 2 9,我们知道第一遍选择第一个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了



代码

void SimpleSelectSort(int a[], int len)
{	
	for(int i = 0; i < len; ++i)
	{
		int min = a[i];
		int t = i;
		for(int j = i; j<len; j++)
			if(a[j]<min)
			{
				min = a[j];
				t = j;
			}
		swap(a[t],a[i]);	
	}

}


2,堆排序

    先用给定的数构造一棵完全二叉树,然后从下标为cnt/2(cnt指给定的数的个数)开始一个一个和它的孩子结点比较,小的就往上挪,最后得到一个小顶堆,取出堆顶,并把最后一个数放入堆顶,进行同样的操作,直到所有的数都已取完为止。我们可以用一个数组来顺序地表示这棵树,左孩子可以通过2*n来找到,右孩子可以通过2*n+1来找到。

    时间复杂度是O(nlgn),也是最快的排序方法之一,在最坏的情况下,其时间复杂度还是O(nlgn),相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需要一个记录大小供交换用的辅助存储空间。堆排序也是不稳定的排序。

    空间复杂度O(n)。


3,归并排序

    归并的意思就是两个或两个以上的有序表组合成一个新的有序表。整个归并排序需要进行【lgn取上限】次,总的时间复杂度为O(nlgn)。与快速排序相比,归并排序的最大特点是:它是一种稳定的排序方法。

空间复杂度O(n)

代码

//2-路归并排序
void Merge(int* array, int low, int mid, int high)
{

    int* temp = (int*)malloc((high - low + 1) * sizeof(int));
    if (!temp) {
        printf("Error:out of memory!");
        return;
    }

    int i = low;
    int j = mid + 1;
    int index = 0;

    while (i <= mid && j <= high) {
        if (array[i] <= array[j]) {
            temp[index++] = array[i++];
        }
        else {
            temp[index++] = array[j++];
        }
    }

    while (i <= mid) {
        temp[index++] = array[i++];
    }

    while (j <= high) {
        temp[index++] = array[j++];
    }

    memcpy((void*)(array + low), (void*)temp, (high - low + 1) * sizeof(int)) ;

    free(temp);
}



void MergSort(int a[], int start, int end)
{
	int m;
	if(start<end)
	{
		m = (start+end)/2;
		MergSort(a, start, m);
		MergSort(a, m+1, end);
		Merge(a, start, m, end);

	}
	

}





4,基数排序

所谓的基数排序 其实就是一种多关键字的排序,最经典的例子就是英语字典的编排,先按第一个字母排列,分成26堆,再按第二个字母排列,……以此类推。。。




   






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值