一、选择排序
(1)基本思想:每一趟从无序区的关键字中选择最小的元素放在有序区的最后,直到全部元素排列完成。
(2)分类:简单选择排序、堆排序。
1.简单选择排序
(1)基本思想:在第i趟排序开始,从无序区R[i,...,n-1]中对比选出最小的元素,与无序区第一个元素R[i]交换位置,使R[0,...,i]成为新的有序区。
(2)注意:简单选择排序每趟产生的有序区都是全局有序,也就是说有序区的所有元素都已归位。
(3)算法:
void Selectsort(Elemtype R[],int n)
{
int i,j,k;
for(i=0;i<n-1;i++) //R[n-1]是最后一个元素不需要比较肯定是最大的
{
k=i;
for(j=i+1;j<n;j++)
if(R[j].key<R[k].key)
k=j;
if(k!=i)
swap(R[i],R[k]); //交换所有,而不是key
}
}
(4)算法分析:
空间复杂度:O(1)
时间复杂度:O(n²)
不稳定的算法:相同元素的相对位置会发生改变。
2.堆排序
(1)定义:
堆排序——一种树形选择排序,特点是将序列看做一棵完全二叉树的顺序存储结构。
堆——当一个序列R[1,...,n-1]满足Ki>=K2i && Ki>=K2i+1是大根堆;Ki<=K2i && Ki<=K2i+1是小根堆。
(2)注意:简单选择排序每趟产生的有序区都是全局有序,也就是说有序区的所有元素都已归位。
(3)算法:
void sift(Elemtype R[],int low,int high)
{
int i=low;
int j=2*i;
Elemtype tmp=R[i];
while(j<high)
{
if(j<high && R[j].key<R[j+1]) //比较左右孩子谁大,再与父节点比较
j++;
if(R[j].key>tmp.key)
{
R[i]=R[j]; //没有交换,只是覆盖,所以需要tmp
i=j; //原父节点的位置改变,观察是否继续“下坠”
j=2*i;
}
else
break;
}
R[i]=tmp;
}
void HeapSort(Elemtype R[],int n)
{
int i;
for(i=n/2;i>=1;i--)
sift(R,i,n); //建立初始堆,把乱序变为有序,之后的操作由于根节点发生了交换只需要下坠根节点即可
for(i=n;i>=1;i--)
{
swap(R[1],R[i]); //最大值永远是R[1],与最后一个节点交换,从小到大排序
sift(R,1,i-1); //对R[1,...,i-1]进行筛选
}
}
(4)算法分析:
空间复杂度:O(1)
时间复杂度:O(nlog2n)
建堆时间复杂度:O(n)
调整时间复杂度:O(h)
不稳定排序算法。
适用性:关键字较多的情况。
(5)堆的插入和删除
插入:将新元素插入到表尾,与父节点依次比较,若新元素比父节点大(大根堆)则交换,一路“上升”。
删除:删除某元素,将表尾元素补在空中,再与左右子树比较,一路“下坠”,直到到达位置。
下坠:每一层关键字对比2次:左右子树对比+父节点与大子树对比
二、习题总结
1.李春葆例题
2.王道选择题
(1)堆中次大值一定在根的下一层,因为所有子树都是堆。
(2)具有n个节点的堆中插入或删除一个新元素的时间复杂度为O(log2n),因为需要向上或向下变动O(h)
(3)与基数排序有关:【不明白】