现在的位置: 首页 > 综合 > 正文

排序方法

2013年09月10日 ⁄ 综合 ⁄ 共 3870字 ⁄ 字号 评论关闭

1、冒泡排序

每一次比较相邻元素的大小,所以一趟比较n-1对相邻位置的数,并在每次发现前面的数大于相邻的后面的数时,交换位置,直到某一趟跑完后发现这一趟没有进行任何交换操作,结束。

最好的情况:正序排列,比较n-1次,移动0次,时间复杂度:O(n)

最坏的情况:逆序排列,比较1+2+3+...+(n-1) = n(n-1)/2,移动n(n-1)/2次,时间复杂度:O(n^2)

//a:待排序的数组;n:数组元素个数
void BubbleSort(int a[], int n)
{	
	bool tag = true;
	int tmp = 0;
	for(int i=1; i<n&&tag; i++)
	{
		tag = false;
		for(int j=0; j<n-i; j++)
		{		
			if(a[j] > a[j+1])
			{
				tmp = a[j];
				a[j] = a[j+1];
				a[j+1] = tmp;
				tag = true;
			}
		}			
	}
}

2、插入排序

2.1直接插入排序

将一个记录插入到已排好序的有序表中.

最好的情况:正序排列,比较n-1次,时间复杂度:O(n)

最坏的情况:逆序排列,比较n(n-1)/2,时间复杂度:O(n^2)

//直接插入排序
void InsertSort(int a[], int n)
{
	int tmp = 0;
	int i, j;
	for(i=1; i<n; i++)
	{
		tmp = a[i];
		for(j=i; j>0&&tmp<a[j-1]; j--)
			a[j] = a[j-1];
		a[j] = tmp;
	}
}

2.2 折半插入

折半插入减少了比较的次数,但是移动次数不变,所以时间复杂度仍为:O(n^2)

//折半查找
int HalfFind(int a[], int low, int high, int value)
{
	int index = 0;
	while(low <= high)
	{
		index = (low + high)/2;
		if(value == a[index])
			break;
		else if(value < a[index])
			high = index - 1;
		else low = index + 1;
	}
	return index;
}
//折半插入
void HalfInsertSort(int a[], int n)
{
	int tmp = 0;
	int i, j;
	for(i=1; i<n; i++)
	{
		if(a[i] < a[i-1])
		{
			tmp = a[i];
			int index = HalfFind(a, 0, i-1, tmp);
			for(j=i; j>index; j--)
				a[j] = a[j-1];
			a[index] = tmp;
		}
	}
}

3、希尔排序

不断把待排序的对象分成若干个小组,对同一小组内的对象采用直接插入法排序,当完成了所有对象都分在一个组内的排序后,排序过程结束。

基本操作:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

希尔排序的一个特点是:子序列的构成不是简单地“逐段分割”,而是将相隔某个“增量” 的记录组成一个子序列。希尔排序的分析是一个复杂的问题,因为它的时间是所取“增量”序列的函数,这涉及一些数学上尚未解决的难题。因此,到目前为止尚未有人求得最好的增量序列。增量序列可以有各种方法,但需要注意:应使增量序列中的值没有除1之外的公因子;并且最后一个增量值必须等于1。

时间复杂度:O(nlogn)

//希尔排序
void ShellSort(int a[], int n)
{
	int d = n;
	while(d > 1)
	{
		d = (d + 1)/2;
		for(int i=0; i<n-d; i++)
		{
			if(a[i] > a[d + i])
			{
				int tmp = a[i];
				a[i] = a[d + i];
				a[d + i] = tmp;
			}
		}
	}
}

4、选择排序

每次从数列中找出一个最小的数放到最前面来,再从剩下的n-1个数中选择一个最小的,不断做下去。

最坏或者平均,均比较n(n-1)/2 所以最坏和平均的时间复杂度为O(n^2),他没有什么好的情况。

//选择排序
void SelectSort(int a[], int n)
{
	int tmp = 0;
	int min = 0;
	for(int i=0; i<n; i++)
	{
		min = i;
		for(int j=i+1; j<n; j++)
		{
			if(a[j] < a[min])
				min = j;
		}
		tmp = a[i];
		a[i] = a[min];
		a[min] = tmp;
	}
}

5、归并排序

时间复杂度:最差、最好、平均都是 O(nlogn)

//将有二个有序数列a[first...mid]和a[mid...last]合并。
void Merge(int a[], int low, int mid, int high, int tmp[])
{
	int i = low, j = mid + 1;
	int m = mid, n = high;
	int k = 0;
	while(i<=m && j<=n)
	{
		if(a[i] <= a[j])
			tmp[k++] = a[i++];
		else tmp[k++] = a[j++];
	}
	while(i<=m)
		tmp[k++] = a[i++];
	while(j<=n)
		tmp[k++] = a[j++];
	for(i=0; i<k; i++)
		a[low + i] = tmp[i];
}

void mergeSort(int a[], int low, int high, int tmp[])
{
	if(low < high)
	{
		int mid = (low + high)/2;
		mergeSort(a, low, mid, tmp);	//左边有序
		mergeSort(a, mid + 1, high, tmp);//右边有序
		Merge(a, low, mid, high, tmp);	//两个有序序列合并
	}
}
//归并排序
bool MergeSort(int a[], int n)
{
	int *p = new int[n];
	if(p == NULL)
		return false;
	mergeSort(a, 0, n-1, p);
	delete []p;
	return true;
}

6、快速排序
 最好:O(nlogn)

最坏:O(n^2) 在元素有序的情况下,最坏。

设置两个指针low和high,初值为low和high,枢轴的关键字为key。首先从high所指的位置起向前搜索找到第一个关键字小于key的记录,和枢轴记录相互交换,然后从low所指位置开始向后搜索,找到第一个关键字大于key的记录,和枢轴记录交换,重复这两步,直至low=high为止。

int Partition(int a[], int low, int high)
{
	int tmp = a[low];
	int key = a[low];	//枢轴
	while(low < high)
	{
		while(low<high && a[high]>=key) high --;
		a[low] = a[high];
		while(low<high && a[low]<=key) low ++;
		a[high] = a[low];
	}
	a[low] = tmp;
	return low;
}
//快速排序
void QSort(int a[], int low, int high)
{
	int loc = 0;
	if(low < high)
	{
		loc = Partition(a, low, high);
		QSort(a, low, loc - 1);
		QSort(a, loc + 1, high);
	}
}

快速排序的最坏情况和最快情况。(下面的内容来自精通八大排序算法系列:一、快速排序算法
最坏情况发生在划分过程产生的俩个区域分别包含n-1个元素和一个0元素的时候,

即假设算法每一次递归调用过程中都出现了,这种划分不对称。那么划分的代价为O(n),

因为对一个大小为0的数组递归调用后,返回T(0)=O(1)。

估算法的运行时间可以递归的表示为:

    T(n)=T(n-1)+T(0)+O(n)=T(n-1)+O(n). 

可以证明为T(n)=O(n^2)。
因此,如果在算法的每一层递归上,划分都是最大程度不对称的,那么算法的运行时间就是O(n^2)。

亦即,快速排序算法的最坏情况并不比插入排序的更好。
此外,当数组完全排好序之后,快速排序的运行时间为O(n^2)。

而在同样情况下,插入排序的运行时间为O(n)。
//注,请注意理解这句话。我们说一个排序的时间复杂度,是仅仅针对一个元素的。

//意思是,把一个元素进行插入排序,即把它插入到有序的序列里,花的时间为n。
 
再来证明,最快情况下,即PARTITION可能做的最平衡的划分中,得到的每个子问题都不能大于n/2.

因为其中一个子问题的大小为|_n/2_|。另一个子问题的大小为|-n/2-|-1.

在这种情况下,快速排序的速度要快得多。为,

      T(n)<=2T(n/2)+O(n).可以证得,T(n)=O(nlgn)。
直观上,看,快速排序就是一颗递归数,其中,PARTITION总是产生9:1的划分,

总的运行时间为O(nlgn)。各结点中示出了子问题的规模。每一层的代价在右边显示。

每一层包含一个常数c。

 

有关快速排序的文章,参见:

http://blog.chinaunix.net/uid-26432915-id-3023291.html

http://blog.csdn.net/v_JULY_v/article/details/6116297
参考文章:

http://blog.chinaunix.net/uid-26432915-id-3023292.html

http://blog.csdn.net/lixiang040330624/article/details/4647512

http://www.matrix67.com/blog/archives/166

http://blog.csdn.net/morewindows/article/details/6678165

抱歉!评论已关闭.