目录
一、排序分类
二、插入排序 —— 只能顺序结构
1、直接插入排序 —— 稳定
时间复杂度:平均O(n^2),最好O(n), 最坏O(n^2)
空间复杂度:O(1),因为只需要一个记录的辅助空间 r[0]
默认第一个元素有序,从第二个元素开始,把该元素存入监视哨,把该元素前的所有元素排好序列
void InsertSort(int A[],int n)
{
int i,j;
for(i=2;i<n;i++) //第一个位置默认有序 0号位为哨兵位
if(A[i] < A[i-1]) //如果出现顺序有问题
{
A[0] = A[i];
for( j=i-1 ; A[j] > A[0] ; j--)
A[j+1] = A[j]; //比哨兵大的元素后移
A[j+1] = A[0]; //将哨兵的值插入正确位置
}
}
2、折半插入排序 —— 稳定
时间复杂度:O(n2)
空间复杂度:仍是只需要一个记录的辅助空间r[0],所以时间复杂度为O(1)
在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high]。
将待插入元素与a[mid]相比较,如果比参考元素小,则选择左区间,否则选择右区间,如此直至low<=high不成立,即将high+1位置及之后所有元素后移一位,并将新元素插入a[high+1]。
void BInsertSort(int a[],int n)
{
int i,j,low,high,mid;
for(i=2 ;i<n ;i++) //依次将a[2]~a[n]插入到前面已经排好序的列表
{
a[0] = a[i]; //监视哨存放要插入的元素
low=1,high=i-1;
while(low<=high)
{
mid = (low+high)/2;
if(a[mid]<a[0]) low=mid+1;
else high=mid-1;
}
for(j=i-1;j>=high+1;j--) a[j+1]=a[j]; //找到要插入的位置(即high+1) 把[要插入位置,待插入元素前的元素]该区间的元素整体后移一位
a[high+1]=a[0]; //在要插入位置插入待插入元素
}
}
3、希尔排序 —— 不稳定
时间复杂度:O(n(log2n)2)
空间复杂度:也是只需要一个辅助空间 r[0] ,所以时间复杂度为O(1)
void ShellSort(int a[],int n) //希尔排序,使用L.length/2作为增量dlta
{
int i,j,dk=L.length/2;
while(dk>0)
{
for(i=1+dk;i<=n;i++)
{
if(a[i]<a[i-dk])
{
a[0]=a[i];
for(j=i-dk;j>0&&a[j]>a[0];j-=dk)
a[j+dk]=a[j];
a[j+dk]=a[0];
}
}
dk/=2;
}
}
三、交换排序
1、冒泡排序 —— 稳定 —— 可链式
时间复杂度:O(n2),但若是一开始就是有序的,则时间复杂度为O(n)
void BubbleSort(int a[],int n)
{
for(int i=0;i<n-1;i++) //n-1是因为不用与自己比较,所以比的数就少一个
{
int flag=0;
for(int j=0;j<n-1-i;j++) //n-1-i是因为每一趟就会少一个数比较
if(a[j]>a[j+1]) //升序
{
int t=a[j];
a[j]=a[j+1];
a[j+1]=t;
flag=1;
}
if(!flag) break; //如果某趟没有交换位置 说明已经排好序 直接跳出大循环
}
}
2、快速排序 —— 不稳定 —— 顺序
时间复杂度:O(nlog2n)
空间复杂度:最好O(log2n),最坏O(n)
下图是课本方法!!!
acw方法:
void quick_sort(int q[], int l, int r)
{
if(l>=r) return 0;
int x = q[l], i = l-1, j = r+1;
while(i<j)
{
while(q[++i] < x); //如果满足≤x 则指针右移
while(q[--j] > x); //如果满足≥x 则指针左移
if(i<j) swap(q[i],q[j]); //如果遇到俩不满足的 交换他们的位置
}
//递归处理左右区间
quick_sort(q,l,j); //左区间
quick_sort(q,j+1,r); //右区间
}
课本方法:
int Partition(SqList &L, int low, int high) //一趟快速排序算法
{
L.r[0]=L.r[low];
int piv=L.r[low].key; //基准值
while(low<high)
{
while(low<high && L.r[high].key>=piv) high--;
L.r[low]=L.r[high];
while(low<high && L.r[low].key<=piv) low++;
L.r[high]=L.r[low];
}
L.r[low]=L.r[0];
return low;
}
void QSort(SqList &L, int low, int high)
{
if(low<high)
{
int pos=Partition(L,low,high);
QSort(L,low,pos-1);
QSort(L,pos+1,high);
}
}
void QuickSort(SqList &L) //快速排序
{
QSort(L,1,L.length);
}
四、选择排序
1、简单选择排序 —— 不稳定 —— 可链式
时间复杂度:O(n2)
空间复杂度:O(1)
void SelectSort(int a[],int n)
{
for(int i=1;i<=n;i++)
{
int min=i;
for(int j=i+1;j<=n;j++)
if(a[j]<a[min]) min=j;
if(min!=i)
{
int t=a[i];
a[i]=a[min];
a[min]=t;
}
}
}
2、堆排序 —— 不稳定 —— 顺序
时间复杂度:O(nlog2n)
空间复杂度:O(1)
堆是具有以下性质的完全二叉树:
①每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆。
②每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
- 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
- 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
- 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
eg:
调整成为大顶堆的过程略
-
将堆顶元素9和末尾元素4进行交换,并且将9标记为已排序状态
-
重新调整结构,使其继续满足堆定义
-
再将堆顶元素8与末尾元素5进行交换,得到第二大元素8,并将8也标记为已标记状态
-
继续进行调整,交换,如此反复进行,最终使得整个序列有序
五、归并排序 —— 稳定
时间复杂度:O(nlogn)
void merge_sort(int q[],int l,int r)
{
if(l>=r) return;
int mid = (l+r)/2;
//递归排序左右区间
merge_sort(q,l,mid);
merge_sort(q,mid+1,r);
//双指针归并左右有序区间
int k=0,i=l,j=mid+1;
while(i<=mid && j<=r)
if(q[i]<=q[j]) temp[k++]=q[i++];
else temp[k++]=q[j++];
//如果左右区间有剩下的,补在末尾
while(i<=mid) temp[k++]=q[i++];
while(j<=r) temp[k++]=q[j++];
//把归并好的序列复制给q[]
for(int i=l,j=0;i<=r;i++,j++) q[i] = temp[j];
}
六、习题
1、在所有排序方法中,关键字比较的次数与记录得初始排列次序无关的是( )
(A)希尔排序 (B)起泡排序 (C)插入排序 (D)选择排序
2、设有1000个无序的元素,希望用最快的速度挑选出其中前10个最大的元素,最好( )排序法。
(A)起泡排序(B)快速排序(C)堆排序(D)基数排序
3、在待排序的元素序列基本有序的前提下,效率最高的排序方法是( )
(A)插入排序(B)选择排序(C)快速排序(D)归并排序
4、一组记录的排序码为(46,79,56,38,40,84),则利用堆排序的方法建立的初始堆为( B )。
(A)79,46,56,38,40,80 (B)84,79,56,38,40,46
(C)84,79,56,46,40,38 (D)84,56,79,40,46,38
5、一组记录的关键码为(46,79,56,38,40,84),则利用快速排序的方法,以第一个记录为基准得到的一次划分结果为( C )。
(A)38,40,46,56,79,84(B)40,38,46,79,56,84
(C)40,38,46,56,79,84(D)40,38,46,84,56,79
6、一组记录的排序码为(25,48,16,35,79,82,23,40,36,72),其中含有5个长度为2的有序表,按归并排序的方法对该序列进行一趟归并后的结果为( A )。
(A)16,25,35,48,23,40,79,82,36,72(B)16,25,35,48,79,82,23,36,40,72
(C)16,25,48,35,79,82,23,36,40,72(D)16,25,35,48,79,23,36,40,72,82
7、排序方法中,从未排序序列中依次取出元素与己排序序列(初始时为空)中的元素进行比较,将其放入己排序序列的正确位置上的方法,称为( C )
(A)希尔排序(B)起泡排序(C)插入排序(D)选择排序
8、排序方法中,从未排序序列中挑选元素并将其依次放入己排序序列(初始为空)的一端的方法,称为( )
(A)希尔排序(B)归并排序(C)插入排序(D)选择排序
9、用某种排序方法对线性表(25,84,21,47,15,27,68,35,20)进行排序时,元素序列的变化情况如下:
(1)25,84,21,47,15,27,68,35,20 (2)20,15,21,25,47,27,68,35,84 (3)15,20,21,25,35,27,47,68,84 (4)15,20,21,25,27,35,47,68,845
则所采用的排序方法是( D )。
(A)选择排序(B)希尔排序(C)归并排序(D)快速排序
10、下述几种排序方法中,平均查找长度最小的是( )
(A)插入排序(B)选择排序(C)快速排序(D)归并排序
11、下述几种排序方法中,要求内存量最大的是( )。
(A)插入排序(B)选择排序(C)快速排序(D)归并排序
12、快速排序方法在情况下最不利于发挥其长处。( C )
(A)要排序的数据量太大 (B)要排序的数据中含有多个相同值
(C)要排序的数据已基本有序(D)要排序的数据个数为奇数
13、下列四种排序方法中,不稳定的方法是( D )
(A)直接插入排序 (B)冒泡排序 (C)归并排序 (D)直接选择排序
14、堆排序是直到最后一趟排序结束之前所有元素才能在其最终的位置上。( T )
15、在对一组记录(4,38,96,23,15,72,60,45,83)进行直接插入排序时,当把第7个记录60插入到有序表时,为寻找插入位置需比较___2____次。
16、在堆排序和快速排序中,若原始记录接近正序或反序,则选用__堆排序__若原始记录无序则最好选用__快速排序___。
17、在插入和选择排序中,若初始数据基本正序,则选用__插入排序__若初始数据基本反序,则选用__选择排序___。
18、对n个元素的序列进行起泡排序时,最少的比较次数是___n-1___
19、若只从最坏情况下排序最快并且要节省内存考虑,则应选取 堆排序