排序

基于比较的排序

根据排序的原则,内排序可以分为:

  • 插入排序

  • 交换排序

  • 选择排序

  • 归并排序

预备知识:

1.等差数列之和:S=n*(a1+an)/2

等比数列之和:S=a1(1-q^n)/(1-q)

2.使用哨兵提高效率

比如基本的顺序查找我们可以这样做:

 

int search(int a[],int n,int key){
	for(int i=0;i<n;i++)
		if(a[i]==key)
			return i+1;			//返回第几个,而不是返回下标
	return 0;						//返回0说明没有找到
}

注意到每次for循环都对边界进行检查(i<n),使用哨兵就不需要进行边界检查.

 

int search(int a[],int n,int key){
	a[0]=key;
	for(int i=n;a[i]!=key;i--);
	return i;
}

但是使用哨兵的前提是在数组中a[1]--a[n]存储的是实际的元素,a[0]是拿来做哨兵的,a的长度是n+1.

3.time()返回从197011日到现在的秒数,是实际时间。

clock返回开启进程和调用clock()之间的的CPU时钟计时单元(clock tick)数,不包括显式调用sleep()的时间,常用来测试任务执行的速度。

插入排序

简单插入排序

非常的简单,想想你玩牌的时候一边起牌,一边就把牌排好序了,那就是插入排序.

时间复杂度:O(N^2)1+2+...+(N-1)=N^2/2。这是最坏的情况,其实大致上说插入排序的平均情况和最坏情况一样差。

空间上来讲,任一时刻最多只有一个数字在存储数组之外,属于原地排序,O(1)

稳定的排序.

 

/*插入排序*/
template <typename Comparable>
void InsertSort(vector<Comparable> &vec,int begin,int end){
	for(int i=begin+1;i<=end;i++){
		if(vec[i]<vec[i-1]){
			vec[0]=vec[i];			//把vec[i]放入哨兵
			int k;
			for(k=i-1;vec[0]<vec[k];k--)		//从vec[0]<vec[k]看出是稳定排序
				vec[k+1]=vec[k];
			vec[k+1]=vec[0];
		}
	}
}
template <typename Comparable>
void InsertSort(vector<Comparable> &vec){
	InsertSort(vec,1,vec.size()-1);
}

希尔排序

希尔排序利用利用了插入排序的两个特点:

  • 基本有序时直接插入排序最快

  • 对于数据很少的无序数列,直接插入也很快

谢尔排序的时间复杂度在O(nlogn)O(n^2)之间,空间复杂度为O(1).

为了使集合基本有序,而不是局部有序,不能简单地逐段分割,而应将相距为某个”增量”的元素组成一个子序列.通常取增量为d1=n/2,di+1=di/2.

 

/*希尔排序*/
template <typename Comparable>
void ShellSort(vector<Comparable> &vec){
	int n=vec.size()-1;
	for(int inc=n/2;inc>=1;inc/=2){		//以inc为增量,vec.size()-1才是vec里面存储的有效元素的个数
		for(int i=inc+1;i<=n;i++){				//一趟希尔排序
			if(vec[i]<vec[i-inc]){
				vec[0]=vec[i];
				int k;
				for(k=i-inc;k>0&&vec[0]<vec[k];k-=inc)
					vec[k+inc]=vec[k];
				vec[k+inc]=vec[0];
			}
		}
	}
}

冒泡排序

把待排序的序列分为有序区和无序区,每次把无序区最大的数放到无序区的最后面,这样无序区的末元素成为有序区的首元素.

时间复杂度为O(n^2),空间复杂度O(1).

 

/*冒泡排序*/
template <typename Comparable>
void BubbleSort(vector<Comparable> &vec){
	int pos=vec.size()-1;			//用pos标记无序区域的最后一个元素.大数往后排,所以无序区域在前,有序区域在后
	while(pos!=0){					//当pos=0时说明1--n都已经有序了
		int bound=pos;				//本次操作前无序区域的边界
		pos=0;
		for(int i=1;i<bound;i++){
			if(vec[i]>vec[i+1]){
				vec[0]=vec[i];
				vec[i]=vec[i+1];
				vec[i+1]=vec[0];
				pos=i;				//只要发生交换就更改pos
			}
		}
	}
}

快速排序

快速排序是对冒泡排序的改进,由于冒泡排序是不断比较相邻元素然后进行交换,需要比较移动多次才能到达最终位置.而快速排序的比较和移动是从两端向中间进行,因而元素移动的距离较远.

初始主轴的选取采用三元取中法.经过N趟排序,当元素已基本有序后采用直接插入排序法.

 

/*快速排序*/
template <typename Comparable>
void median3(vector<Comparable> & a,int left,int right){
    int center=(left+right)/2;
    if(a[center]<a[left])
		swap(a[center],a[left]);
    if(a[right]<a[left])
		swap(a[left],a[right]);
    if(a[center]>a[right])
		swap(a[center],a[right]);
 
    //place pivot at position left
    swap(a[center],a[left]);
}
template <typename Comparable>
const int Partion(vector<Comparable> & a,int left,int right){
	a[0]=a[left];				//选取基准存放在a[0]中
	while(left<right){
		while((left<right)&&(a[right]>=a[0]))		//右侧扫描
			right--;
		a[left]=a[right];
		while((left<right)&&(a[left]<=a[0]))		//左侧扫描
			left++;
          //注意左右侧扫描时,当a[right]或a[left]等于基准时,left或right指针也要移动。否则当有其他元素和基准相同时,外层的while将成为死循环
		a[right]=a[left];
	}
	a[left]=a[0];
	return left;
}
template <typename Comparable>
void QuickSort(vector<Comparable> &vec,int begin,int end){
	median3(vec,begin,end);
	if(begin+10<end){
		int pivotloc=Partion(vec,begin,end);
		QuickSort(vec,begin,pivotloc-1);
		QuickSort(vec,pivotloc+1,end);
	}
	else{
		InsertSort(vec,begin,end);
	}
}

理想情况下,每次划分左右两侧的序列长度是相同的,长度为n的序列可划分为logn.定位一个元素要对整个序列扫描一遍,所需时间为O(n),总的时间复杂度为O(nlogn).

如果每次都取最左边的元素作主轴,最坏情况下待排序列完全有序(正序或逆序),每次划分只得到比上一次少一个的子序列,总的比较次数为1+2+3+...+(n-1)=O(n^2).

平均来说快速排序的时间复杂度为O(nlogn),栈的深度为O(logn).

快速排序是一种不稳定的排序算法.

选择排序

简单选择排序

简单选择排序和冒泡排序很像,每趟排序把把无序序列中最小的元素放到有序列的最后面,有序序列在前,无序序列在后.但有一个重要的区别:冒泡排序在一趟排序中边比较,边交换;而简单选择排序在一趟排序中只作一次交换.

简单选择排序是稳定的,时间复杂度为O(n^2),空间复杂度为O(1).

 

/*简单选择排序*/
template <typename Comparable>
void SelectSort(vector<Comparable> &vec){
	int n=vec.size()-1;
	for(int begin=1;begin<n;begin++){
		int tmp=begin;			//记录本趟排序的起点
		for(int i=tmp+1;i<=n;i++)
			if(vec[i]<vec[tmp])
				tmp=i;			//tmp跟踪最小的元素
		if(tmp!=begin){
			vec[0]=vec[tmp];
			vec[tmp]=vec[begin];
			vec[begin]=vec[0];
		}
	}
}

堆排序

堆排序是对简单选择排序的改进,在简单选择排序中前一次的比较结果没有保存下来,后一趟排序中反复对以前已经做过的比较重新做了一遍.

时间复杂度为O(nlogn),不稳定的排序.

 

/*堆排序*/
template <typename Comparable>
void percolate(vector<Comparable> & a,int k,int n){		//"渗透",k是我们当前关注的节点,我们要保证它比其左右孩子都要小
    int i=k;			//i是要筛选的节点
    int j=2*i;			//j是i的左孩子
    while(j<=n){
    	if(j<n && a[j]>a[j+1])
    		j++;				//确保a[j]是左右孩子中的较小者
    	if(a[i]<a[j])			//满足小根堆的条件,结束
    		break;
    	else{
    		swap(a[i],a[j]);
    		i=j;
    		j=2*i;
    	}
    }
}
template <typename Comparable>
void HeapSort(vector<Comparable> & a){
	int n=a.size()-1;
    for(int i=n/2;i>=1;i--)			//n/2是第1个有孩子的节点.从下往上渗透,建堆
		percolate(a,i,n);
    for(int j=1;j<n;j++){			
    	//cout<<a[1]<<"  ";			//输出堆顶元素
		swap(a[1],a[n-j+1]);		//C++内置swap()函数
		percolate(a,1,n-j);			//重新建堆
    }
}

归并排序

二路归并排序

 

/*归并排序*/
template<typename Comparable>
void Merge(vector<Comparable> & a,vector<Comparable> & tmpArray,int leftPos,int rightPos,int rightEnd){
    int leftEnd=rightPos-1;
    int tmpPos=leftPos;
    int numElements=rightEnd-leftPos+1;
 
    while(leftPos<=leftEnd && rightPos<=rightEnd)
		if(a[leftPos]<=a[rightPos])
			tmpArray[tmpPos++]=a[leftPos++];
		else
			tmpArray[tmpPos++]=a[rightPos++];
	while(leftPos<=leftEnd)
		tmpArray[tmpPos++]=a[leftPos++];
	while(rightPos<=rightEnd)
		tmpArray[tmpPos++]=a[rightPos++];
	for(int i=0;i<numElements;i++,rightEnd--)
		a[rightEnd]=tmpArray[rightEnd];
}
template<typename Comparable>
void MergeSort(vector<Comparable> & a,vector<Comparable> & tmpArray,int left,int right){
    if(left<right){
		int center=(left+right)/2;
		MergeSort(a,tmpArray,left,center);
		MergeSort(a,tmpArray,center+1,right);
		Merge(a,tmpArray,left,center+1,right);
    }
}

总结

对以上种算法进行一次测试,比一比哪个快.

测试的完整代码:

 

#include<iostream>
#include<ctime>			//time()
#include<vector>
#include<cstdlib>		//srand()和rand()
using namespace std;
/*插入排序*/
template <typename Comparable>
void InsertSort(vector<Comparable> &vec,int begin,int end){
	for(int i=begin+1;i<=end;i++){
		if(vec[i]<vec[i-1]){
			vec[0]=vec[i];			//把vec[i]放入哨兵
			int k;
			for(k=i-1;vec[0]<vec[k];k--)		//从vec[0]<vec[k]看出是稳定排序
				vec[k+1]=vec[k];
			vec[k+1]=vec[0];
		}
	}
}
template <typename Comparable>
void InsertSort(vector<Comparable> &vec){
	InsertSort(vec,1,vec.size()-1);
}
/*希尔排序*/
template <typename Comparable>
void ShellSort(vector<Comparable> &vec){
	int n=vec.size()-1;
	for(int inc=n/2;inc>=1;inc/=2){		//以inc为增量,vec.size()-1才是vec里面存储的有效元素的个数
		for(int i=inc+1;i<=n;i++){				//一趟希尔排序
			if(vec[i]<vec[i-inc]){
				vec[0]=vec[i];
				int k;
				for(k=i-inc;k>0&&vec[0]<vec[k];k-=inc)
					vec[k+inc]=vec[k];
				vec[k+inc]=vec[0];
			}
		}
	}
}
/*冒泡排序*/
template <typename Comparable>
void BubbleSort(vector<Comparable> &vec){
	int pos=vec.size()-1;			//用pos标记无序区域的最后一个元素.大数往后排,所以无序区域在前,有序区域在后
	while(pos!=0){					//当pos=0时说明1--n都已经有序了
		int bound=pos;				//本次操作前无序区域的边界
		pos=0;
		for(int i=1;i<bound;i++){
			if(vec[i]>vec[i+1]){
				vec[0]=vec[i];
				vec[i]=vec[i+1];
				vec[i+1]=vec[0];
				pos=i;				//只要发生交换就更改pos
			}
		}
	}
}
/*快速排序*/
template <typename Comparable>
void median3(vector<Comparable> & a,int left,int right){
    int center=(left+right)/2;
    if(a[center]<a[left])
		swap(a[center],a[left]);
    if(a[right]<a[left])
		swap(a[left],a[right]);
    if(a[center]>a[right])
		swap(a[center],a[right]);
 
    //place pivot at position left
    swap(a[center],a[left]);
}
template <typename Comparable>
const int Partion(vector<Comparable> & a,int left,int right){
	a[0]=a[left];				//选取基准存放在a[0]中
	while(left<right){
		while((left<right)&&(a[right]>=a[0]))		//右侧扫描
			right--;
		a[left]=a[right];
		while((left<right)&&(a[left]<=a[0]))		//左侧扫描
			left++;
		a[right]=a[left];
	}
	a[left]=a[0];
	return left;
}
template <typename Comparable>
void QuickSort(vector<Comparable> &vec,int begin,int end){
	median3(vec,begin,end);
	if(begin+10<end){
		int pivotloc=Partion(vec,begin,end);
		QuickSort(vec,begin,pivotloc-1);
		QuickSort(vec,pivotloc+1,end);
	}
	else{
		InsertSort(vec,begin,end);
	}
}
/*简单选择排序*/
template <typename Comparable>
void SelectSort(vector<Comparable> &vec){
	int n=vec.size()-1;
	for(int begin=1;begin<n;begin++){
		int tmp=begin;			//记录本趟排序的起点
		for(int i=tmp+1;i<=n;i++)
			if(vec[i]<vec[tmp])
				tmp=i;			//tmp跟踪最小的元素
		if(tmp!=begin){
			vec[0]=vec[tmp];
			vec[tmp]=vec[begin];
			vec[begin]=vec[0];
		}
	}
}
/*堆排序*/
template <typename Comparable>
void percolate(vector<Comparable> & a,int k,int n){		//"渗透",k是我们当前关注的节点,我们要保证它比其左右孩子都要小
    int i=k;			//i是要筛选的节点
    int j=2*i;			//j是i的左孩子
    while(j<=n){
    	if(j<n && a[j]>a[j+1])
    		j++;				//确保a[j]是左右孩子中的较小者
    	if(a[i]<a[j])			//满足小根堆的条件,结束
    		break;
    	else{
    		swap(a[i],a[j]);
    		i=j;
    		j=2*i;
    	}
    }
}
template <typename Comparable>
void HeapSort(vector<Comparable> & a){
	int n=a.size()-1;
    for(int i=n/2;i>=1;i--)			//n/2是第1个有孩子的节点.从下往上渗透,建堆
		percolate(a,i,n);
    for(int j=1;j<n;j++){			
    	//cout<<a[1]<<"  ";			//输出堆顶元素
		swap(a[1],a[n-j+1]);		//C++内置swap()函数
		percolate(a,1,n-j);			//重新建堆
    }
}
/*归并排序*/
template<typename Comparable>
void Merge(vector<Comparable> & a,vector<Comparable> & tmpArray,int leftPos,int rightPos,int rightEnd){
    int leftEnd=rightPos-1;
    int tmpPos=leftPos;
    int numElements=rightEnd-leftPos+1;
 
    while(leftPos<=leftEnd && rightPos<=rightEnd)
		if(a[leftPos]<=a[rightPos])
			tmpArray[tmpPos++]=a[leftPos++];
		else
			tmpArray[tmpPos++]=a[rightPos++];
	while(leftPos<=leftEnd)
		tmpArray[tmpPos++]=a[leftPos++];
	while(rightPos<=rightEnd)
		tmpArray[tmpPos++]=a[rightPos++];
	for(int i=0;i<numElements;i++,rightEnd--)
		a[rightEnd]=tmpArray[rightEnd];
}
template<typename Comparable>
void MergeSort(vector<Comparable> & a,vector<Comparable> & tmpArray,int left,int right){
    if(left<right){
		int center=(left+right)/2;
		MergeSort(a,tmpArray,left,center);
		MergeSort(a,tmpArray,center+1,right);
		Merge(a,tmpArray,left,center+1,right);
    }
}
int main(){
	const int N=100000;			//对十万个随机数进行排序
	clock_t begin;
	clock_t end;
	srand((unsigned)time(NULL));
	vector<int> v;
	v.push_back(0);			//数组第1个元素存放哨兵,而不是参与排序的数据
	
	for(int i=0;i<N/10;i++)		
		v.push_back(rand());
	begin=clock();
	InsertSort(v);
	end=clock();
	cout<<"InsertSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N;i++)		
		v.push_back(rand());
	begin=clock();
	ShellSort(v);
	end=clock();
	cout<<"ShellSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N/10;i++)		
		v.push_back(rand());
	begin=clock();
	BubbleSort(v);
	end=clock();
	cout<<"BubbleSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N;i++)		
		v.push_back(rand());
	begin=clock();
	QuickSort(v,1,N);
	end=clock();
	cout<<"QuickSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N/10;i++)		
		v.push_back(rand());
	begin=clock();
	SelectSort(v);
	end=clock();
	cout<<"SelectSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N;i++)		
		v.push_back(rand());
	begin=clock();
	HeapSort(v);
	end=clock();
	cout<<"HeapSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;
	
	
	
	v.clear();
	v.push_back(0);
	for(int i=0;i<N;i++)		
		v.push_back(rand());
	vector<int> vv(v.size());
	begin=clock();
	MergeSort(v,vv,1,v.size()-1);
	end=clock();
	cout<<"MergeSort: "<<(double)(end-begin)/CLOCKS_PER_SEC<<endl;

	return 0;
}

我们来排个序,直接插入,冒泡和简单选择排序都是对1万条数据排序

直接插入:0.73

简单选择:0.78

冒泡:1.88

以下算法是对10万条数据进行排序

快速排序:0.06

归并排序:0.08

堆排序:0.09

希尔:0.12

排序方法

平均情况

最好情况

最坏情况

辅助空间

稳定性

直接插入

O(n^2)

O(n)

O(n^2)

O(1)

希尔

O(nlogn)~O(n^2)

O(n^1.3)

O(n^2)

O(1)

冒泡

O(n^2)

O(n)

O(n^2)

O(1)

快速

O(nlogn)

O(nlogn)

O(n^2)

O(nlogn)~O(n)

简单选择

O(n^2)

O(n^2)

O(n^2)

O(1)

堆排序

O(nlogn)

O(nlogn)

O(nlogn)

O(1)

归并

O(nlogn)

O(nlogn)

O(nlogn)

O(n)

:冒泡排序采用pos来标记已有序的序列位置后,最好情况才是O(n),如果没有采用此改进算法,最好情况也是O(n^2).我们的快速排序每次都把主轴放在vec[0],没用另外使用单独的变量,所以辅助空间为O(1),否则就是O(nlogn)~O(n).

STL排序

STL中的所有排序算法都需要输入一个范围,[begin,end).如果要自己定义比较函数,可以把定义好的仿函数(functor)作为参数传入.

函数名

功能描述

sort

指定区间排序

stable_sort

指定区间稳定排序

partial_sort

对指定区间所有元素部分排序

partial_sort_copy

对指定区间复制并排序

nth_element

找到指给定区间某个位置上对应的元素

is_sorted

判断给定区间是否已排序好

partition

使得符合条件的元素放在前面

stable_partion

相对稳定地使得符合条件的元素放在前面

 

#include<iostream>
#include<algorithm>
#include<functional>
#include<vector>
using namespace std;
class myclass{
public:
	myclass(int a,int b):first(a),second(b){}
	int first;
	int second;
	bool operator < (const myclass &m)const{
		return first<m.first;
	}
};
//自定义仿函数
bool less_second(const myclass &m1,const myclass &m2){
	return m1.second<m2.second;
}
int main(){
	vector<myclass> vec;
	for(int i=0;i<10;i++){
		myclass my(10-i,i*3);
		vec.push_back(my);
	}
	//原始序列
	for(int i=0;i<10;i++)
		cout<<vec[i].first<<"  "<<vec[i].second<<endl;
	
	sort(vec.begin(),vec.end());
	cout<<"按照第1个数从小到大:"<<endl;
	for(int i=0;i<10;i++)
		cout<<vec[i].first<<"  "<<vec[i].second<<endl;
		
	sort(vec.begin(),vec.end(),less_second);
	cout<<"按照第2个数从小到大:"<<endl;
	for(int i=0;i<10;i++)
		cout<<vec[i].first<<"  "<<vec[i].second<<endl;
		
	return 0;
}	

 stable_sort是稳定的排序,或许你会问既然相等又何必在乎先后顺序呢?这里的相等是指提供的比较函数认为两个元素相等,并不是指两个元素真的一模一样.

sort采用的是”成熟”的快速排序(结合了内插排序算法),保证很好的平均性能,时间复杂度为O(nlogn).stable_sort采用的是归并排序,分派内存足够时,其时间复杂度为O(nlogn),否则为O(nlognlogn).

线性时间排序

最好的基于比较的排序算法,其在最坏的情况下做次比较。因此堆排序和归并排序都是渐近最优的比较排序算法。

下面介绍3种不是基于比较的排序算法,它们线性的时间度,但应用范围也受到局限。

计数排序

计数排序仅适用于整数,并且要求待排数据在[0,max]上。

 

 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 
 5 void CountSort(vector<int> &arr,int max){        //max是arr中的最大者
 6     vector<int> C(max+1);
 7     vector<int> B(arr.size());
 8     for(int i=0;i<arr.size();i++)
 9         C[arr[i]]++;
10     for(int i=1;i<C.size();i++)
11         C[i]+=C[i-1];
12     for(int i=arr.size()-1;i>=0;i--){
13         B[C[arr[i]]-1]=arr[i];
14         C[arr[i]]--;
15     }
16     arr=B;
17 }
18 
19 int main(){
20     int arr[8]={2,5,7,9,2,3,0,16};
21     vector<int> A;
22     for(int i=0;i<8;i++)
23         A.push_back(arr[i]);
24     CountSort(A,16);
25     for(int i=0;i<8;i++)
26         cout<<A[i]<<"  ";
27     cout<<endl;
28     return 0;
29 }

 

第8、10、12行有3个for循环,算法复杂度为,当时,算法复杂度为

计算排序是稳定的排序,这一点对下面要讲的基数排序非常重要。因为计算排序是基数排序的子过程,计算排序的稳定性对基数排序的正确性至关重要。

基数排序

同样只适用于整数,并且还要求位数相同。第一次按个位排序,第二次按百位排序,第三次按千位排序……

 

 1 #include<iostream>
 2 #include<cmath>
 3 #include<vector>
 4 using namespace std;
 5 
 6 void RadixSort(vector < int >&arr, int len)
 7 {
 8     vector < int >C(10);
 9     vector < int >B(arr.size());
10     for (int j = 0; j < 3; j++) {
11         C.assign(10, 0);
12         B.assign(arr.size(), 0);
13         for (int i = 0; i < arr.size(); i++){
14             if(j==0)
15                 C[arr[i] % 10]++;
16             else
17                 C[arr[i] / (int)pow(10,j) % 10]++;
18         }
19         for (int i = 1; i < C.size(); i++)
20             C[i] += C[i - 1];
21         for (int i = arr.size() - 1; i >= 0; i--) {
22             if(j==0){
23                 B[C[arr[i] % 10] - 1] = arr[i];
24                 C[arr[i] % 10]--;
25             }else{
26                 B[C[arr[i] / (int)pow(10,j) % 10] - 1] = arr[i];
27                 C[arr[i] / (int)pow(10,j) % 10]--;
28             }
29         }
30         arr = B;
31     }
32 }
33 
34 int main()
35 {
36     int arr[7] = { 329, 457, 657, 839, 436, 720, 355 };
37     vector < int >vec;
38     for (int i = 0; i < 7; i++)
39         vec.push_back(arr[i]);
40     RadixSort(vec, 3);
41     for (int i = 0; i < 7; i++)
42         cout << vec[i] << "  ";
43     cout << endl;
44     return 0;
45 }

 

桶排序

需要要事先知道数据的范围,比如在[1,100]上,则我们用10个桶,把每个数num映射到第num/10桶中去,对每个桶内元素进行排序,最后连接起来就是全局的排序了。

posted @ 2010-08-30 21:51  张朝阳  阅读(1936)  评论(0编辑  收藏  举报