选择排序
基本方法:每次从待排序的文件中选择出排序码最小的记录,将该记录放到已排序文件的最后一个位置,直到已排序文件记录个数等于初始待排序文件的记录个数。
直接选择排序
定义
从所有n个待排序记录值红选择出排序码最小的记录,将该记录与第一个记录交换,再从剩下的n-1个记录中选择排序码最小的记录与第二个记录交换……,直到只剩下一个记录,排序完成。
图解
图源:https://www.cnblogs.com/hokky/p/8529042.html
代码
c
void simpleseselectsort(table *tab)
{
int i,j,k;
for(i=0;i<tab->length;i++)
{
k=i;
for(j=i+1;j<tab->length;j++)
{
if(tab->r[j].key<tab->r[k].key) k=j;
}
if(k!=i)
{
tab->r[0]=tab->r[k];
tab->r[k] = tab->r[i];
tab->r[i]=tab->r[0];
}
}
}
Java
public static void main(String[] args) {
int[] elem = {0,7,9,1,5,8,3,15,2,6};
int i,k,temp;
for (i = 0; i <elem.length ; i++) {
k=i;
for (int l = i+1; l <elem.length ; l++) {
if(elem[l]<elem[k]){
k=l;
}
}
if(k!=i){
temp=elem[k];
elem[k]=elem[i];
elem[i]=temp;
}
}
for (int j = 0; j <elem.length ; j++) {
System.out.println(elem[j]);
}
}
python
def select_insert_sort(demo):
print('排序前{}'.format(demo))
length = len(demo)
for i in range(0, length):
k=i
for j in range(i+1,length):
if demo[j]<demo[k]:
k=j
if k!=i:
temp = demo[k]
demo[k] = demo[i]
demo[i] = temp
print("最终排序{}".format(demo))
算法要点
两重循环:
- 外层循环控制整体数组是否有序
- 内层循环找寻待排序序列中最小数放入已排序序列中
时间空间复杂度稳定性
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
- 不稳定
举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
树形选择排序
定义
在直接选择排序中,有许多排序码之间进行了不止一次比较。所以树形选择排序在选择排序码最小记录过程中,把其中的排序码比较的结果保存下来,使以后需要的时候直接看比较结果,而不再进行比较。
类似于生活中体育比赛的淘汰制。
图解
时间空间复杂度稳定性
- 时间复杂度:O(nlogn)
- 空间复杂度:O(n)
- 不稳定
然而虽然树形选择比较能够减少比较次数,却增加了辅助空间的使用。为了弥补此缺憾,威廉姆斯于1964年提出了堆排序。
堆排序(大根堆)
既要保存中间比较结果,减少后面的比较次数,又不占用大量的附加存储空间,使算法有较好的性能。威廉姆斯于1964年提出了堆排序。
堆排序的关键问题是如何将待排序记录的排序码建成一个堆,后排序。
大顶堆:所有父节点大于子节点
小顶堆:所有父节点小于子节点
详情见:https://www.bilibili.com/video/BV1Eb41147dK
代码
c
#include <stdio.h>
#include <stdlib.h>
void swap(int arr[],int i,int j)
{
int temp=arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//从上而下
//单纯使用从上而下并不能保证最后一定是大顶堆
//例外:1,4,5,3,2,10 这样执行单纯从上到下无法将10送入顶端
void heapify(int tree[],int n,int i)
{
int c1 =2*i+1;
int c2= 2*i+2;
int max=i;
if(c1<n &&tree[c1]>tree[max])
{
max=c1;
}
if(c2<n&&tree[c2]>tree[max])
{
max=c2;
}
if(max!=i)
{
swap(tree,max,i);
heapify(tree,n,max);
}
}
//从下而上
//单纯从下而上也不能最终得到大顶堆
//例外:1,4,5,3,2,10 最后1即便是最小也无法处于叶子节点上
void build_heap(int tree[],int n)
{
int last_node = n-1;
int parent = (last_node-1)/2;
int i;
//所以我们需要先从下而,在从下而上处理的子树中从上而下再调整一遍。
for(i=parent;i>=0;i--)
{
heapify(tree,n,i);
}
//最后即可得出正确的大顶堆
}
//大顶堆排序
void heap_sort(int tree[],int n)
{
build_heap(tree,n);//先构成大顶堆
int i;
for(i=n-1;i>=0;i--)
{
swap(tree,i,0);
//其实使用build_heap也同样达到效果,但是更繁琐。
//build_heap(tree,i);
heapify(tree,i,0);//交换之后,除了最上面的结点,其他部分都是堆,符合heapify标准,所以可以直接自上而下就可以成堆。
}
}
int main()
{
int tree[] = {1,4,5,3,2,10};
int size = 6;
heap_sort(tree,size);
int i;
for(i=0;i<size;i++)
{
printf("%d\n",tree[i]);
}
return 0;
}
Java
public class HelloWorld {
public static void main(String[] args) {
int[] tree = {1,4,5,3,2,10};
int size = tree.length;
heap_sort(tree,size);
for (int j = 0; j <tree.length ; j++) {
System.out.println(tree[j]);
}
}
public static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void heapify(int[] tree,int n,int i){
int c1=2*i+1;
int c2=2*i+2;
int max = i;
if(c1<n&&tree[c1]>tree[max]){
max = c1;
}
if(c2<n && tree[c2]>tree[max]){
max=c2;
}
if(max!=i){
swap(tree,max,i);
heapify(tree,n,max);
}
}
public static void build_heap(int[] tree,int n){
int last_node = n-1;
int parent = (last_node-1)/2;
int i;
for(i=parent;i>=0;i--){
heapify(tree,n,i);
}
}
public static void heap_sort(int[] tree,int n){
build_heap(tree,n);
int i;
for(i=n-1;i>=0;i--){
swap(tree,i,0);
heapify(tree,i,0);
}
}
}
python
def swap(tree, i, j):
temp = tree[i]
tree[i] = tree[j]
tree[j] = temp
def heapify(tree, n, i):
c1 = 2*i+1
c2 = 2*i+2
max = i
if c1 < n and tree[c1] > tree[max]:
max = c1
if c2 < n and tree[c2] > tree[max]:
max = c2
if max != i:
swap(tree, max, i)
heapify(tree, n, max)
def build_heap(tree, size):
last_node = size-1
parent = (last_node-1)//2
for i in range(parent, -1, -1):
heapify(tree, size, i)
def heap_sort(num_list, size):
build_heap(num_list, size)
for i in range(size-1, -1, -1):
swap(num_list, i, 0)
heapify(num_list, i, 0)
if __name__ == '__main__':
num_list = [1, 4, 5, 3, 2, 10, 6, 8, 18]
length = len(num_list)
heap_sort(num_list, length)
print("最终排序{}".format(num_list))
算法要点
- 自上而下heapify(C1和C2和i三者找最大然后交换,递归自上而下执行)
- 交换swap
- 自下而上build_heap(从n-1到0,循环执行自上而下heapify
- 排序heap_sort(先建堆)
时间空间复杂度稳定性
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
- 不稳定
我们知道堆的结构是节点i的孩子为(计数从0 开始)2 i + 2和2 i + 1节点。在一个长为n 的序列,堆排序的过程是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, … 1这些个父节点选择元素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。