排序算法小结(一)
排序算法的稳定性:
如果一个待排序的序列中,存在2个相等的数,在排序后这2个数的相对位置保持不变,那么该排序算法是稳定的;否则是不稳定的。
稳定的意义:排序算法如果是稳定的,从一个键上排序,然后从另一个键上排序,第一个键排序的结果可以为第二个键排序所用
两个重要定理:
定理1:任意N个不同元素组成的序列平均具有N(N-1)/4个逆序对。
定理2:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为(N^2)
因此要提高排序算法效率,我们必须:
1.每次消去不止1个逆序对!
2.每次交换相隔较远的两个元素!
一、冒泡排序:
#include<bits/stdc++.h> using namespace std; const int maxn=1010; #define inf 0x3fffffff void Bubble_Sort(int num[],int n){ for(int i=n-1;i>0;i--){ int flag=0;//如果序列已经有序,不需要再进行比较 for(int j=0;j<i;j++){ if(num[j]>num[j+1]){ swap(num[j],num[j+1]); flag=1; } } if(flag==0){ break; } } } printff(int num[],int n){ for(int i=0;i<n;i++){ printf("%d ",num[i]); } printf("\n"); } //冒泡排序 //最好情况:O(n); //最坏情况:O(n^2); int main(){ int num[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&num[i]); } Bubble_Sort(num,n); printff(num,n); return 0; }
冒泡算法是稳定的算法,因为排序前后相同元素的相对位置不变。
交换的次数等于序列的逆序数(线代第一章有讲)
二、插入排序
#include<bits/stdc++.h> using namespace std; const int maxn=1010; #define inf 0x3fffffff void Insertion_Sort(int num[],int n){ int temp; int j; for(int i=1;i<n;i++){ temp=num[i];//未排序元素 for(j=i;j>0&&temp<num[j-1];j--){ num[j]=num[j-1];//移出空位 } num[j]=temp;//元素落位 } } void printff(int num[],int n){ for(int i=0;i<n;i++){ printf("%d ",num[i]); } printf("\n"); } //插入排序 //最好情况:O(n); //最坏情况:O(n^2);n(n+1)/2; int main(){ int num[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&num[i]); } Insertion_Sort(num,n); printff(num,n); return 0; }
用一句话理解就是:将未排序的元素插入到已排序的序列中。在插入的过程中,元素往后挪。
交换的次数等于序列的逆序数
三、希尔排序
#include<bits/stdc++.h> using namespace std; const int maxn=1010; #define inf 0x3fffffff void Shell_Sort(int num[],int n){ int j; for(int D=n/2;D>0;D/=2){//设置增量(此处增量不互质) for(int i=D;i<n;i++){//插入排序 int temp=num[i]; for(j=i;j>=D&&temp<num[j-D];j-=D){ num[j]=num[j-D]; } num[j]=temp; } } } void printff(int num[],int n){ for(int i=0;i<n;i++){ printf("%d ",num[i]); } printf("\n"); } //希尔排序 //最好情况:O(n); //最坏情况:O(n^2); int main(){ int num[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&num[i]); } Shell_Sort(num,n); printff(num,n); return 0; }
注:如果增量D不互质,则小增量可能根本不起作用。
可以用Hibbard增量序列:Dk=2^k-1 (相邻元素互质)
四、选择排序
#include<bits/stdc++.h> using namespace std; const int maxn=1010; #define inf 0x3fffffff void Selection_Sort(int num[],int n){ for(int i=0;i<n;i++){ int temp=i; for(int j=i;j<n;j++){//从i到n中找到最小元,并将其位置赋给temp if(num[j]<num[temp]){ swap(j,temp); } } if(temp!=i){//将未排序部分的最小元交换到有序部分的最后位置 swap(num[temp],num[i]); } } } void printff(int num[],int n){ for(int i=0;i<n;i++){ printf("%d ",num[i]); } printf("\n"); } //选择排序 //复杂度:O(n^2) int main(){ int num[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&num[i]); } Selection_Sort(num,n); printff(num,n); return 0; }
五、堆排序
#include<bits/stdc++.h> using namespace std; #define inf 0x3fffffff const int maxn=1010; //heap为堆,n为元素个数 int heap[maxn],n=100; //向下调整 时间复杂度为O(logn) void downAdjust(int low,int high){ int i=low,j=i*2; //i为欲调整结点,j为其左孩子 while(j<=high){ //存在孩子结点 if(j+1<=high&&heap[j+1]>heap[j]){//如果存在右孩子结点,且右孩子的值大于左孩子 j=j+1; //让j存储右孩子的下标 } if(heap[j]>heap[i]){ //如果孩子中最大权值比欲调整结点i大 swap(heap[j],heap[i]); //交换最大权值的孩子与欲调整结点i i=j; //保持i为欲调整结点,j为i的左孩子 j=i*2; } else{ break; //孩子的权值均比欲调整结点i小,调整结束 } } } //建堆 时间复杂度O(n) void creatHeap(){ for(int i=n/2;i>=1;i--){//从最后一个非叶子结点开始枚举 downAdjust(i,n); } } //删除堆顶元素 时间复杂度O(logn) void deleteTop(){ heap[1]=heap[n--]; //用最后一个元素覆盖堆顶元素,并让元素个数减 1 downAdjust(1,n); //向下调整堆顶元素 } //向上调整 时间复杂度为O(logn) void upAdjust(int low,int high){//对heap数组在[low,high]范围进行向上调整,其中low一般设置为1 //high表示欲调整结点的数组下标 int i=high,j=i/2;//i为欲调整结点,j为其父亲 while(j>=low){ //父亲在[low,high] 范围内 if(heap[j]<heap[i]){ //父亲权值小于欲调整结点i的权值 swap(heap[j],heap[i]); //交换父亲和欲调整结点 i=j; //保持i为欲调整结点,j为i的父亲 j=i/2; } else{ break; //父亲权值比欲调整结点i的权值大,调整结束 } } } //添加元素 时间复杂度O(logn) void insert(int x){ heap[++n]=x; //让元素个数加 1 ,然后将数组末位赋值为 x upAdjust(1,n); //向上调整新加入的结点 n } //堆排序 时间复杂度O(nlogn) void heapSort(){ creatHeap(); //建堆 for(int i=n;i>1;i--){ //倒着枚举,直到堆中只有一个元素 swap(heap[i],heap[1]); //交换heap[i]与堆顶 downAdjust(1,i-1); //向下调整堆顶 } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>heap[i]; } heapSort(); for(int i=1;i<=n;i++){ cout<<heap[i]<<" "; } cout<<endl; return 0; } //示例: //5 输入 //3 1 4 5 2 输入 //1 2 3 4 5 输出
详细请看:堆
六、归并排序
#include<bits/stdc++.h> using namespace std; const int maxn=1010; #define inf 0x3fffffff void mort(int num[],int left,int mid,int right){ int num2[maxn]; for(int i=left;i<=right;i++){ num2[i]=num[i]; } int i=left,j=mid+1,k=0; while(i<=mid&&j<=right){ if(num2[i]<=num2[j]){ num[left+k]=num2[i]; k++; i++; } else{ num[left+k]=num2[j]; k++; j++; } } while(i<=mid){ num[left+k]=num2[i]; i++; k++; } while(j<=right){ num[left+k]=num2[j]; j++; k++; } } void GBsort(int num[],int left,int right){ if(left>=right){ return ; } int mid=(left+right)/2; GBsort(num,left,mid);//递归处理左子序列 GBsort(num,mid+1,right);//递归处理右子序列 mort(num,left,mid,right);//合并左右子序列 } void printff(int num[],int n){ for(int i=0;i<n;i++){ printf("%d ",num[i]); } printf("\n"); } //归并排序 //复杂度:nlogn int main(){ int num[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&num[i]); } GBsort(num,0,n-1); printff(num,n); return 0; }
明天继续。。。