文章目录
08 排序
8.0 各排序算法比较
关于稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,A1=A2,且A1在A2之前,而在排序后的序列中,A1仍在A2之前,则称这种排序算法是稳定的;否则称为不稳定的。例如简单选择排序中,序列5 8 5 2 9, 第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以该算法不稳定。
(1)不稳定算法:快(快速排序)些(shell 希尔排序)选(选择排序)队(堆排序);
快(快排)归(归并)空间大。
(2)每一次排序之后都能确定至少一个元素位置的排序方法包括:
1.选择排序:每次将最大的数放到最后。所以最大的数排一次序后位置就确定了。
2.冒泡排序:同选择排序。每一次排序最大的值位置确定。
3.快排:每一次排序pivot的位置确定。
4.堆排序:每一次排序时,都是将堆顶的元素和最后一个节点互换,然后调整堆,再将堆大小减1。所以每一次排序堆顶元素确定。
不能至少确定一个元素的位置的方法包括:
1.插入排序:不到最后一步求的都是相对位置。
2.shell排序:对简单插入排序的改进。不到最后一步,是无法确定每个元素位置的。
3.归并排序:局部有序,并不能确定任一元素在全局的位置。
4.基数排序,计数排序:利用桶排序的思路,不是基于比较的排序,也无法在一次排序中确定某个元素的位置。因为每一次排序都是整体处理。
(3)其他
- 元素的移动次数与关键字的初始排列次序无关的是:基数排序
- 元素的比较次数与初始序列无关是:选择排序、折半插入排序
- 算法的时间复杂度与初始序列无关的是:选择排序、堆排序、归并排序、基数排序
- 算法的排序趟数与初始序列无关的是:插入排序、选择排序、基数排序
8.1 插入排序
8.1.1 直接插入排序
直接插入排序是一种最简单的排序算法,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。
定义数据元素类型
typedef struct
{
KeyType key; // 关键字域
... // 其他域
}ElemType;
定义表的顺序存储结构
typedef struct
{
ElemType r[MAXSIZE+1]; // r[0] 为监视哨
int length;
}SqList;
void InsertSort(SqList L)
{
int i, j;
for(i = 2; i < L.length; i++)
{
if(L.r[i].key < L.r[i-1].key)
{
L.r[0] = L.r[i]; // 放入监视哨
for(j=i-1; L.r[j].key>L.r[0].key ;j--)
L.r[j+1] = L.r[j];
L.r[j+1] = L.r[0];
}
}
}
8.1.2 折半插入排序
用 折半查找 方法确定插入位置的排序。
void BinSort(SqList &L)
{
int i, j, high, low, mid;
for(i=2; i<L.length; i++)
{
L.r[0] = L.r[i]; // 设置监视哨
low = 1; high = i-1; // i 之前都是有序的
while(low<=high)
{
mid = (high+low)/2;
if(L.r[0].key<L.r[mid].key)
high = mid-1;
else low = mid+1;
}
for(j=i-1; j>=low; j--)
L.r[j+1] = L.r[j]; // 记录后移
L.r[low] = L.r[0]; // 插入
}
}
8.1.3 希尔排序
先将整个待排序记录分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
8.2 交换排序
两两比较待排序记录的关键值,交换不满足顺序要求的记录,直到全部满足顺序要求为止。
8.2.1 冒泡排序
void BubbleSort(SqList &L)
{
int m, j, flag=1;
m = L.length-1; // 最多进行 length -1 次排序
while(m>0 && flag==1)
{
flag = 0; // 没有发生交换,直接退出
for(j=1, j<m; j++)
{
if(L.r[j].key > L.r[j+1].key)
{
flag = 1;
L.r[0]=L.r[j];
L.r[j]=L.r[j+1];
L.r[j+1]=L.r[0];
}
}
m--;
}
}
8.2.2 快速排序
通常选择第一个元素作为枢轴。
int Partition(SqList &L, int low, int high)
{
KeyType pivotkey;
L.r[0] = L.r[low];
pivotkey = L.r[low].key; // 保存枢轴
while(low<high)
{
while(L.r[high].key>pivotkey && low<high)
high--; // 不发生交换
if(low<high)
{
L.r[low]=L.r[high];
low++;
}
while(L.r[low].key<pivotkey && low<high)
low++;
if(low<high)
{
L.r[high]=L.r[low];
h--
}
}
L.r[low]=L.r[0];
return low;
}
排序程序为
void QuickSort(SqList &L, int low, int high)
{
int pivotloc;
if(low<high)
{
pivotloc = Partition(L, low, high);
QuickSort(L, low, pivotloc-1);
QuickSort(L, pivot+1, high)
}
}
8.2.3 归并排序
将子序列依次排序合并,最终完全排序。
8.3 选择排序
每次从待排序记录中选出关键字最小的记录,顺序放在已排序的记录序列的后面,直到全部排完为止。
8.3.1 简单选择排序
void SelectSort(SqList &L)
{
int i, j, k;
for(i=1; i<L.length; i++)
{
k=i; // 先假设 i 即最小值的下标
for(j=i+1, j<L.length; j++)
{
if(L.r[j].key<L.r[k].key)
k = j;
}
if(i!=k) // 交换
{
L.r[0]=L.r[i];
L.r[i]=L.r[k];
L.r[k]=L.r[0];
}
}
}
8.3.2 堆排序
8.3.3 基数排序
多关键字排序。
“分配-收集”:分别按个位、十位、百位分配收集。