基本的排序算法 中:直接插入排序、气泡排序、选择排序中时间复杂度都是n的二次方,高效排序中 :快排、堆排、归并排序都是O(n log2 n)
起泡排序
他是从后倒着来,先比较n-1和n-2元素的大小,如果n-1元素小,就将他两交换位置,接下俩再向前移动,比较n-2和n-3的大小,如果n-2小则继续交换并且前移,这样经过一轮之后就将最小的元素放在了第一个的位置上,然后在进行下一趟排序,这个时间复杂度是n的平方,因为要每一趟比较都是N的时间单位,而要比较N-1趟
最原始的起泡排序就不附上代码了,接下来就是对起泡排序进行改进,可以先增加一个exchange的标志,如果发生了互换就exchange=true。否则就是exchange= false,表示全部元素已经排好序了,可以终止了,这样可以使得减去一些步骤,因为有的时候排序已经好了但是还是在进行着一趟趟的排序,但是并没有交换元素,浪费了
void BubbleSort(int V[],int n){
bool exchange,int i ,int j ;
for(i=0;i<n;i++){
exchange = false;
for(j=n-1;j>=0;j--){
if(V[j-1]>V[j]){
int temp = V[j];
V[j] = V[j-1];
V[j-1] = temp;
exchange = true;
}
}
if(exchange == false) break; //本趟没有逆序的,终止
}
}
插入排序
当插入新的元素时,原来的是有顺序的,前面的i-1个元素都是排好的,找到新元素的第i号位置后,原来后面的元素向后移动,然后把新元素插进去 ,时间复杂度是O(n*n)
插入排序中还有一种就是折半插入排序,又叫做二分法插入排序,在插入第i个元素的时候采用的是二分法查找元素的位置,时间复杂度是O(nlog2 n),相比起来要好很多
void BinaryInsertSort(dataList<int>& L,int low,int high){
Element<int> temp;int i,middle,k;
for(i=low+1;i<=high;i++){
//插入第 i 个数,前面一共有i-1个数,寻找区间
temp = L[i];low = 0;high = i-1;
while(low<high){
middle = (low+high)/2;
if(temp<L[middle]) high = middle-1;
else low = middle+1;
}
//整体后移
for(k = i-1;k>=low;k--) L[k+1] = L[k];
L[low] = temp;
}
}
希尔排序
又叫做缩小增量排序,待排序的有n个,取一个整数作为间隔gap<n,将全部元素分为gap个子序列,所有间隔是gap的放在同一子序列中在么一个子序列中执行插入排序,然后缩小gap,重复子序列的划分和排序,然后不断减小gap直到gap==1,所有元素放在同一个序列中,实行排序,最终得到结果
对于规模较大的序列,希尔排序有较高的效率,应用不同的gap效率差得很多,时间复杂度也没有一个定义公式,通常取gap = gap/3 +1
void ShellSort(dataList<int>& L,int left,int right){
Element<int> temp;int i,gap = right-left+1;
do{
gap = gap/3+1;
for(i= left+gap;i<=right;i++){ //遍历序列的,子序列一个一个处理
if(L[i] < L[i-gap]){ //逆序,插排
temp = L[i];
j = i-gap;
do{
L[j+gap] = L[j]; //后移
j = j-gap; //比较前一元素
}while(j>=left && temp<L[i]);
L[j+gap] = temp;//这要加gap是再循环中,j=j-gap < left才跳出来的
}
}
}while(gap>1)
}
这里有几个小的细节需要注意一下,第一个就是,在第一次for循环中,i=left+gap,为什么要设置这样呢,因为在子序列中需要比较,出现逆序的话是需要后移的,后移的方向是从后向前这样移动,不会冲掉数据
快速排序
又叫做分区排序,是使用最广泛的排序,他运行快,使用的空间少,是一种不稳定的排序方法
他采用的是分治法,将所有元素分为两个左右子序列,在左边的子序列中的值都小于基准值,右边序列的元素都大于等于基准值,然后对两个子序列分别执行上面说的方法,直到排完,采用递归的方法
void QuickSort(dataList<int>& L,int left,int right){
if(left<right){
int pivotpos = L.Partition(left,right); //划分
QuickSort(L,left,pivotpos-1); //对左子序列处理
QuickSort(L,pivotpos+1,right); //对右子序列处理
}
}
int Partition(int low,int high){
int pivotpos = low;
Elemenet<int> pivot = Vector[low]; //基准元素
for(int i = low+1;i<=high;i++){
if(Vector[i]<pivot){ //元素小于基准元素
pivotpos++;
if(pivotpos!=i) Swap(Vector[pivotpos],Vector[i]);//把小的放在前面
}
}
Vector[low] = Vector[pivotpos];
Vector[pivotpos] = pivot;
return pivotpos;
}
图中wei表示的就是基准位置
对于快排法还有改进:
1.在递归的过程中,如果带排序的子序列的规模小于M值时,直接使用插入排序对子序列排序,而不是递归,这样不用递归,效率就会高很多
void QuickSort(dataList<int>& L,int left,int right){
if(right-left<=M){
InsertSort(L,left,right);
}else{
int pivotpos = L.Partition(left,right); //划分
QuickSort(L,left,pivotpos-1); //对左子序列处理
QuickSort(L,pivotpos+1,right); //对右子序列处理
}
}
2.在划分子序列时,不进行排序直接跳过,划分之后整体上市已经排好序的,这样就用插入排序
上述中if(right-left<=M),直接return,然后在快排之后在进行一遍插排,这样之后就是排好序的序列了
void HybridSort(dataList<int>& L,int left,int right){
QuickSort(L,left,right);
InsertSort(L,left,right);
}
快排中,基准元素选择的好不好直接影响到排序的效率,所以改进中,选取基准元素时,在left和right还有中间位置元素middle中取中间值,并交换到right位置上
Element<int>& median(dataList<int>& L,int left,int right){
int middle = (left + right)/2;
int k = left; //k代表的是最小的
if(L[middle]<L[k]) k = middle;
if(L[k]>L[right]) k = right;
if(k!=left) Swap(L[k],L[left]); //最小的到left
//L[middle]是中间值,交换到right位置上
if(middle!=right && L[middle]<L[right]) Swap(L[middle],L[right]);
return L[right];
}
int Partition(dataList<int>& L,int left,int right){
int i=left;j=right-1;
if(left<right){
Elemenet<int> pivot = median(L,left,right);
for(;;){
while(i<j && L[i]<pivot) i++; //正向走,小于pivot的留在左边
while(i<j && pivot<L[j]) j--; //逆向走,大于pivot的留在右边、
if(i<j) Swap(L[i],L[j]);i++;j--;
else break;
}
if(L[i]>pivot) L[right] = L[i];L[i] = pivot;
}
}
这样先是快排,接着对基本的进行插排
对于有大量重复元素,快排效率非常的低,可以实行三路划分的方法来做,划分为大于小于以及等于基准元素三部分,在扫描左子序列时把等于的放在最左边,右子序列中等于的放在最右边
void QuickSort(dataList<int>& L,int left,int right){
int i,j,p,q,k;
Element<int> pivot = L[right];
if(right<=left) return;
i = left-1;j = right;p = left-1;q = right;
while(1){
while(L[++i]<pivot) if(i==j) break;
while(L[--j]>pivot) if(j==i) break;
if(i>=j) break;
Swap(L[i],L[j]);
if(L[i] == pivot) p++;Swap(L[p],L[i]);
if(L[j] == pivot) q--;Swap(L[q],L[j]);
}
if(L[i]>L[right]) Swap(L[i],L[right]);k = right-1;
else k = right;
j--;i++;
while(k>=q) Swap(L[k],L[i]);k--;i++;
for(k = left;k<=p;k++;j--) Swap(L[k],L[j]);
QucikSort(L,left,j);
QucikSort(L,i,right);
}