常用排序(一)

来源:互联网 发布:centos设置ip地址 编辑:程序博客网 时间:2024/06/05 07:21
排序根据元素是否完全在在内存中分为内部排序和外排序,内部排序指的是元素完全存放在内存中,外部排序是不断在内外存3之间移动的排序

基本的排序算法 中:直接插入排序、气泡排序、选择排序中时间复杂度都是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表示的就是基准位置


时间复杂度是O(nlog2 n),和折半插入算法时间复杂度是一样的
对于快排法还有改进:
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);}


0 0