一、选择排序
【算法】每次从余下待排序记录中通过比较找到最小的,通过交换,放在待排序记录的首位。直到全部记录排好序。
具体如下:
(1)第1趟,从a[0..n-1]中找到最小的记录,与a[0]交换;
(2)第2趟,从a[1..n-1]中找到最小的记录,与a[1]交换;
……
(n-1)第n-1趟,从a[n-2..n-1]中找到最小的记录,与a[n-2]交换。
【稳定性】不稳定。
【时间复杂度】O(n^2)
不管什么情况,都需要做(n-1)+(n-2)+...+1=n*(n-1)/2次比较。是性能最差的排序方法。
【示例代码】
void selectsort(int n){
for(int i=0;i<n-1;i++){
int k=i;
for(int j=i+1;j<n;j++)
if(a[k]>a[j]) k=j;
if(k!=i){//三行代码交换a[i]与a[k]
a[i]=a[i]+a[k];
a[k]=a[i]-a[k];
a[i]=a[i]-a[k];
}
}
}
二、堆排序
【堆的相关知识】
小根堆定义:小根堆是一棵完全二叉树,每个节点值小于等于它的儿子值(如果有),所以根结点一定是最小值,但左、右儿子大小不一定。
大根堆定义:大根堆是一棵完全二叉树,每个节点值大于等于它的儿子值(如果有),所以根结点一定是最大值,但左、右儿子大小不一定。
有三种操作需要掌握:
1、插入(put):
(1)在堆尾加入一个元素,并把这个结点作为当前结点。
(2)比较当前结点与它的父结点。
如果当前结点值小于父结点,则交换,并把父结点作为当前结点。转到(2)。
否则,结束。
2、读取并删除(get)(涉及向下调整):
(1)取出堆的根结点的值。
(2)把堆的最后一个结点放到根的位置上,堆长度减1。
(3)把根结点作为now结点。
(4)如果now结点没有子结点,是结束。否则,比较左、右儿子结点值(如果有右儿子),记小的为next结点。
(5)如果now结点值小于等于next结点值,则结束。否则,交换now结点与next结点,并把next结点作为now结点,回到(4)。
3、调整(adjust)(涉及向上调整):
(1)把最后一个元素作为now结点。
(2)如果 now结点是根结点,则结束。
(3)记now结点为son结点。
(4)如果son结点值小于它的父结点值,则交换,并将父结点作为son结点,如果新的son结点是根结点,则转到(5),否则回到(4)。
(5)将now结点前一个结点作为新的now结点,如果新的now结点是根结点,则结束,否则回到(4)。
【稳定性】不稳定。
【时间复杂度】O(nlogn)。最好、最坏情况区别在于有没有交换及比较次数有不同,但时间复杂度都是O(nlogn),
【示例代码】
1、边读记录边建小根堆,建好后,再边读取边输出。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,d,heap[100],heap_size=0;
void heap_put(int d){//插入元素。涉及向上调整
int now,next;
heap[++heap_size]=d;
now=heap_size;
while(now>1){
next=now>>1;
if(heap[now]>=heap[next])break;
swap(heap[now],heap[next]);
now=next;
}
}
int heap_get(){//读取堆根元素并删除,同时维护小根堆。涉及向下调整
int now,next,res;
res=heap[1];
heap[1]=heap[heap_size--];
now=1;
while(now*2<=heap_size){
next=now*2;
if(next<heap_size&&heap[next+1]<heap[next])next++;
if(heap[now]<=heap[next])break;
swap(heap[now],heap[next]);
now=next;
}
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&d);
heap_put(d);
}
for(int i=1;i<=n;i++) printf("%d ",heap_get());
return 0;
}
2、用数组保存待排序记录后,调整成小根堆,再边读取边输出。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,d,heap[100],heap_size=0;
void adjust_heap(int now){//涉及向上调整
if(now*2>heap_size) return;//叶子结点则返回。也可以写成if(now>heap_size/2)return;
int next;
while(now*2<=heap_size){
next=now*2;
if(next<heap_size&&heap[next+1]<heap[next])next++;
if(heap[now]<=heap[next])break;
swap(heap[now],heap[next]);
now=next;
}
}
int heap_get(){//涉及向下调整
int now,next,res;
res=heap[1];
heap[1]=heap[heap_size--];
now=1;
while(now*2<=heap_size){
next=now*2;
if(next<heap_size&&heap[next+1]<heap[next])next++;
if(heap[now]<=heap[next])break;
swap(heap[now],heap[next]);
now=next;
}
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&heap[i]);
heap_size=n;
for(int i=n;i>=1;i--) adjust_heap(i);
for(int i=1;i<=n;i++) printf("%d ",heap_get());
return 0;
}
【参考文献】
1、https://blog.csdn.net/K346K346/article/details/50791102
2、c++信息学奥赛一本通