`
阅读更多

选择排序(selection sorts)算法大串讲

本文内容框架:

§1 选择排序

§2 锦标赛排序

  §3 堆排序

§4 Smooth Sort

§5 小结

 

 

 

§1 选择排序

选择排序(Selection sort)

 

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。下图能够帮助很直观的理解出选择排序算法的思想:

 

 

选择排序动画演示

 

void select_sort( int *a, int n)
{
    register int i, j, min, t;
    for( i = 0; i < n - 1; i ++)
    {
        min = i;
        //查找最小值
        for( j = i + 1; j < n; j ++)
            if( a[ min] > a[ j])
                min = j;
        //交换
        if( min != i)
        {
            t = a[ min];
            a[ min] = a[ i];
            a[ i] = t;
        }
    }
}

选择排序的交换操作介于和次之间。选择排序的比较操作为次之间。选择排序的赋值操作介于和次之间。比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。 交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。 交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。

 

 

简单选择排序算法改进

传统的简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。

 

 

 

 

§2 锦标赛排序

 

 

锦标赛排序(tournament iree sort)

 

直接选择排序要执行n-1趟(i=0,1,…,n-2),第i越要从n-i个对象中选出一个具有最小排序码的对象,需要进行n-i-1次排序码比较。当n比较大时,排序码比较次数相当多。这是因为在后一趟比较选择时,往往把前一趟已做过的比较又重复做了 一遍,没有把前一趟比较的结果保留下来。

  锦标赛排序(tournament iree sort)克服了这一缺点。它的思想与体育比赛类似。首先取得n个对象的排序码,进行两两比较,得到[n/2]个比较的优胜者(排序码小者),作 为第一步比较的结果保留下来。然后对这[n/2]个对象再进行排序码的两两比较,……, 如此重复,直到选出一个排序码最小的对象为止。

 

#include <stdio.h> 
#include <stdlib.h> 
int _; 
#define swap(x, y) { _=x;x=y;y=_; } 
//#define max(x, y) ( ((x)>(y))?(x):(y) ) 
//#define min(x, y) ( ((x)<(y))?(x):(y) ) 
#define MAX (int)(((unsigned)(~((int)0)))>>1) 
#define MIN (-MAX-1) 
 
void Adjust(int *b, int x, int n) 
{ 
       int l = x * 2 + 1; 
       int r = l + 1; 
 
       //printf("%d\n", MAX); 
       if (l >= n) { 
              b[x] = MAX; 
              return; 
       } 
       else if (r >= n) { 
              b[x] = b[l]; 
              return; 
       } 
 
       if (b[l] == b[x]) { 
              Adjust(b, l, n); 
       } 
       else { 
              Adjust(b, r, n); 
       } 
       b[x] = min(b[l], b[r]); 
} 
 
void GameSort(int *a, int n) 
{ 
       int i, len, *b; 
       void Out(int *, int); 
 
       len = 1; 
       while (len < n) { 
              len <<= 1; 
       } 
       len = 2 * len - 1; 
       b = (int *)malloc(sizeof(int) * len); 
 
       for (i=len/2; i<len; i++) { 
              b[i] = (i-len/2<n) ? (a[i-len/2]) : (MAX); 
       } 
 
       for (i=len/2-1; i>=0; i--) { 
              b[i] = min(b[2 * i + 1], b[2 * i + 2]); 
       } 
       for (i=0; i<n; i++) { 
              a[i] = b[0]; 
              Out(b, len); //不断跟踪输出完全二叉树b[]状态 
              Adjust(b, 0, len); 
       } 
 
       free(b); 
} 
 
int main() 
{ 
       int a[] = { 21, 25, 49, 25, 16, 8, 63, 63, 100, 1002 }; 
       int i, n = 9; 
 
       for (i=0; i<n; i++) { 
              printf("%5d", a[i]); 
       } 
       printf("\n"); 
 
       GameSort(a, n); 
       for (i=0; i<n; i++) { 
              printf("%5d", a[i]); 
       } 
       printf("\n"); 
 
       return 0; 
} 
 
// ---- 输出部分, 与程序算法无关 ---- 
// ---- 为了打出那个树状, 好看 ---- 
 
#include <math.h> 
 
void Out(int *a, int n) 
{ 
       void _Out(int *, int); 
 
       //printf("%d===\n", n / 2 + 1); 
       _Out(a + (n / 2), n / 2 + 1); 
} 
 
void _Out(int *a, int n) 
{ 
       static int i, j, set = 0; 
       int len = log((double)n) / log((double)2) + 1; 
       int l, r, have; 
 
       int **b = (int **)malloc(sizeof(int *) * len); 
       for (i=0; i<len; i++) { 
              b[i] = (int *)malloc(sizeof(int) * n); 
              for (j=0; j<n; j++) { 
                     b[i][j] = MIN; 
              } 
       } 
 
       //printf("%d\n", MIN); 
       for (i=0; i<n; i++) { 
              b[len - 1][i] = a[i]; 
       } 
       for (i=len-1; i>=1; i--) { 
              have = 0; 
              for (j=0; j<n; j++) { 
                     if (b[i][j] != MIN) { 
                            (++have==1)?(l=j):(r=j); 
                     } 
                     if (have == 2) { 
                            b[i-1][(l+r)/2] = min(b[i][l], b[i][r]); 
                            have = 0; 
                     } 
              } 
       } 
 
       printf("\n ---- Set %d ----\n", set++); 
       for (i=0; i<len; i++) { 
              for (j=0; j<n; j++) { 
                     if (b[i][j] == MIN) { 
                            printf("    "); 
                     } 
                     else if (b[i][j] == MAX) { 
                            printf(" MAX"); 
                     } 
                     else { 
                            printf("  %02d", b[i][j]); 
                     } 
              } 
              printf("\n"); 
       } 
}

 

 

 

§3 堆排序

 

堆排序(heap sort)

锦标赛算法有两个缺点:辅助存储空间较多、最大值进行多余的比较。堆排序就是在锦标赛排序的基础上改进——只需要O(1)的辅助存储空间,减少最大值的比较。

堆排序算法的演示。首先,将元素进行重排,以符合堆的条件。图中排序过程之前简单的绘出了堆树的结构。

 

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最小堆:

 

堆的存储

一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。

 

在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:

最大堆调整(Max_Heapify):将堆的末端子结点作调整,使得子结点永远小于父结点

创建最大堆(Build_Max_Heap):将堆所有数据重新排序

堆排序(HeapSort):移除位在第一个数据的根结点,并做最大堆调整的递归运算

 

 

#include <cstdio>
#include <cstdlib>
#include <cmath>
 
const int HEAP_SIZE = 13; //堆大小
 
int parent(int);
int left(int);
int right(int);
void Max_Heapify(int [], int, int);
void Build_Max_Heap(int []);
void print(int []);
void HeapSort(int [], int);
 
/*父结点*/
int parent(int i)
{
    return (int)floor((i - 1) / 2);
}
 
/*左子结点*/
int left(int i)
{
    return (2 * i + 1);
}
 
/*右子结点*/
int right(int i)
{
    return (2 * i + 2);
}
 
/*从单一子结点创建最大堆*/
void Max_Heapify(int A[], int i, int heap_size)
{
    int l = left(i);
    int r = right(i);
    int largest;
    int temp;
    if(l < heap_size && A[l] > A[i])
    {
        largest = l;
    }
    else
    {
        largest = i;
    }
    if(r < heap_size && A[r] > A[largest])
    {
        largest = r;
    }
    if(largest != i)
    {
        temp = A[i];
        A[i] = A[largest];
        A[largest] = temp;
        Max_Heapify(A, largest, heap_size);
    }
}
 
/*建立最大堆*/
void Build_Max_Heap(int A[])
{
    for(int i = (HEAP_SIZE-1)/2; i >= 0; i--)
    {
        Max_Heapify(A, i, HEAP_SIZE);
    }
}
 
/*输出最大堆*/
void print(int A[])
{
    for(int i = 0; i < HEAP_SIZE;i++)
    {
        printf("%d ", A[i]);
    }
    printf("\n");
}
 
/*利用堆进行排序*/
void HeapSort(int A[], int heap_size)
{
    Build_Max_Heap(A);
    int temp;
    for(int i = heap_size - 1; i >= 0; i--)
    {
        temp = A[0];
        A[0] = A[i];
        A[i] = temp;
        Max_Heapify(A, 0, i);
    }
    print(A);
}
 
/*测试*/
int main(int argc, char* argv[])
{
    int A[HEAP_SIZE] = {19, 1, 10, 14, 16, 4, 7, 9, 3, 2, 8, 5, 11};
    HeapSort(A, HEAP_SIZE);
    system("pause");
    return 0;
}

 

堆的操作

堆的操作主要是插入和删除,插入总是将插入元素放在堆的末尾,然后进行恢复堆次序处理;删除操作是将要删除元素和最后一个元素替换,然后进行恢复堆次序处理。其实归根结底也是堆的调整操作,只是多了对堆大小(元素个数)的修改)。

 

 

§4 Smooth Sort 

Smooth Sort算法

 

Smooth Sort基本思想和Heap Sort相同,但Smooth Sort使用的是一种由多个堆组成的优先队列,这种优先队列在取出最大元素后剩余元素可以就地调整成优先队列,所以Smooth Sort不用像Heap Sort那样反向地构建堆,在数据基本有序时可以达到O(n)复杂度。Smooth Sort算法在维基百科上有详细介绍。

    Smooth Sort是所有算法中时间复杂度理论值最好的,但由于Smooth Sort所用的优先队列是基于一种不平衡的结构,复杂度因子很大,所以该算法的实际效率并不是很好。

 

 

#include <cstdio>
 #include <cstdlib>
 #include <ctime>
 
 static unsigned int set_times = 0;
 static unsigned int cmp_times = 0;
 
 template<typename item_type> void setval(item_type& item1, item_type& item2) {
     set_times += 1;
     item1 = item2;
     return;
 }
 
 template<typename item_type> int compare(item_type& item1, item_type& item2) {
     cmp_times += 1;
     return item1 < item2;
 }
 
 template<typename item_type> void swap(item_type& item1, item_type& item2) {
     item_type item3;
 
     setval(item3, item1);
     setval(item1, item2);
     setval(item2, item3);
     return;
 }
 
 static const unsigned int leonardo[] = {
     1, 1, 3, 5, 9, 15, 25, 41, 67, 109, 177, 287, 465, 753, 1219, 1973,
     3193, 5167, 8361, 13529, 21891, 35421, 57313, 92735, 150049, 242785,
     392835, 635621, 1028457, 1664079, 2692537, 4356617, 7049155, 11405773,
     18454929, 29860703, 48315633, 78176337, 126491971, 204668309, 331160281,
     535828591, 866988873, 1402817465, 2269806339u, 3672623805u,
 };
 
 template<typename item_type> inline void smooth_sort_fix(
         item_type* array, int current_heap, int level_index, int* levels) {
     int prev_heap;
     int max_child;
     int child_heap1;
     int child_heap2;
     int current_level;
 
     while(level_index > 0) {
         prev_heap = current_heap - leonardo[levels[level_index]];
         if(compare(array[current_heap], array[prev_heap])) {
             if(levels[level_index] > 1) {
                 child_heap1 = current_heap - 1 - leonardo[levels[level_index] - 2];
                 child_heap2 = current_heap - 1;
                 if(compare(array[prev_heap], array[child_heap1])) break;
                 if(compare(array[prev_heap], array[child_heap2])) break;
             }
             swap(array[current_heap], array[prev_heap]);
             current_heap = prev_heap;
             level_index -= 1;
         } else break;
     }
 
     current_level = levels[level_index];
     while(current_level > 1) {
         max_child = current_heap;
         child_heap1 = current_heap - 1 - leonardo[current_level - 2];
         child_heap2 = current_heap - 1;
 
         if(compare(array[max_child], array[child_heap1])) max_child = child_heap1;
         if(compare(array[max_child], array[child_heap2])) max_child = child_heap2;
         if(max_child == child_heap1) {
             swap(array[current_heap], array[child_heap1]);
             current_heap = child_heap1;
             current_level -= 1;
         }
         else if(max_child == child_heap2) {
             swap(array[current_heap], array[child_heap2]);
             current_heap = child_heap2;
             current_level -= 2;
         } else break;
     }
     return;
 }
 
 template<typename item_type> void smooth_sort(item_type* array, int size) {
 
     int levels[64] = {1};
     int toplevel = 0;
     int i;
 
     for(i = 1; i < size; i++) {
         if(toplevel > 0 && levels[toplevel - 1] - levels[toplevel] == 1) {
             toplevel -= 1;
             levels[toplevel] += 1;
         } else if(levels[toplevel] != 1) {
             toplevel += 1;
             levels[toplevel] = 1;
         } else {
             toplevel += 1;
             levels[toplevel] = 0;
         }
         smooth_sort_fix(array, i, toplevel, levels);
     }
 
     for(i = size - 2; i > 0; i--) {
         if(levels[toplevel] <= 1) {
             toplevel -= 1;
         } else {
             levels[toplevel] -= 1;
             levels[toplevel + 1] = levels[toplevel] - 1;
             toplevel += 1;
 
             smooth_sort_fix(array, i - leonardo[levels[toplevel]], toplevel - 1, levels);
             smooth_sort_fix(array, i, toplevel, levels);
         }
     }
     return;
 }
 
 int main(int argc, char** argv) {
     int capacity = 0;
     int size = 0;
     int i;
     clock_t clock1;
     clock_t clock2;
     double data;
     double* array = NULL;
 
     // generate randomized test case
     while(scanf("%lf", &data) == 1) {
         if(size == capacity) {
             capacity = (size + 1) * 2;
             array = (double*)realloc(array, capacity * sizeof(double));
         }
         array[size++] = data;
     }
 
     // sort
     clock1 = clock();
     smooth_sort(array, size);
     clock2 = clock();
 
     // output test result
     fprintf(stderr, "smooth_sort:\t");
     fprintf(stderr, "time %.2lf\t", (double)(clock2 - clock1) / CLOCKS_PER_SEC);
     fprintf(stderr, "cmp_per_elem %.2lf\t", (double)cmp_times / size);
     fprintf(stderr, "set_per_elem %.2lf\n", (double)set_times / size);
     for(i = 0; i < size; i++) {
         fprintf(stdout, "%lf\n", array[i]);
     }
     free(array);
     return 0;
 }

 

 

 

§5 小结

这篇博文列举了选择排序的几个算法,管中窥豹,不求甚解。如果你有任何建议或者批评和补充,请留言指出,不胜感激,更多参考请移步互联网。

 

 

 

 

 

参考:

MoreWindows http://blog.csdn.net/morewindows/article/details/6709644

RichSelian http://www.cnblogs.com/richselian/archive/2011/09/16/2179148.html

kapinter http://zdker.blog.163.com/blog/static/584834200659636560/

④更多参考来着维基百科

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论
1 楼 zuhanbing 2013-11-19  

相关推荐

    c#实现各种排序算法动态演示

    在提供的"sorts"压缩包中,可能包含了上述各种排序算法的C#源代码,你可以使用Visual Studio(VS)打开并运行这些代码,查看每个算法的执行效果。这些代码实例是学习和实践排序算法的好资源,特别适合作为毕业设计...

    swift-sorts, Swift中,实现了排序算法的集合.zip

    swift-sorts, Swift中,实现了排序算法的集合 Swift 排序 快速实现的排序算法集合。Read Read ,Apples, ,, ,, 。请参见 objective-c 排序和 c 排序比较。算法快速 sorted()快速排序堆排序规则插入排序规则选择...

    SORTS.zip 全排序

    综上所述,"SORTS.zip 全排序"项目可能涵盖了C#中各种排序算法的实现,以及通过界面展示排序结果和将结果存储为二维数组的功能。对于初学者和经验丰富的开发者来说,这都是一个很好的学习和实践项目。

    排序算法全集锦(java代码实现)

    简单选择排序是一种不稳定的排序算法。其基本思想是从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。...

    日常操作中常见的排序方法有:冒泡排序、快速排序、选择排序、插入排序、希尔排序,甚至还有基数排序、鸡尾_sorts.zip

    日常操作中常见的排序方法有:冒泡排序、快速排序、选择排序、插入排序、希尔排序,甚至还有基数排序、鸡尾_sorts

    Java快速排序+简单选择排序+折半插入排序

    做了个Java Swing 图形界面,选择3中排序方法进行排序。工程用NetBeans 打开,运行Main.java文件或直接点击运行主程序,3种算法在源包中的sorts文件夹...QKSort.java(快速排序算法) SelectSort.java(简单选择排序)

    各种排序算法性能分析

    在提供的压缩包"sorts"中,可能包含了实现这些排序算法的C++源代码。这些代码遵循面向对象的设计原则,结构紧凑,逻辑清晰,是学习和理解这些排序算法的好资源。通过阅读和分析这些代码,我们可以更深入地了解每种...

    内排序 数据结构C++程序设计

    首先,简单选择排序(Simple Selection Sort)是一种直观的排序算法。它的工作原理是每次遍历未排序序列,找到最小(或最大)元素,存放到排序序列的起始位置,直到全部待排序的数据元素排完。虽然简单,但效率较低...

    C++数据结构实现之Sorts.zip

    本篇将深入探讨"Sorts.zip"中的C++数据结构实现,特别是排序算法。 在C++中,排序算法是处理数组、向量等数据集合时的重要工具。它们的目标是按照特定规则(如升序或降序)重新排列元素。以下是几种常见的排序算法...

    数据结构算法演示系统

    此外,系统可能还包括了性能分析,比如时间复杂度和空间复杂度的讨论,以及不同数据规模下的运行效率对比,帮助用户了解在实际应用中如何选择合适的排序算法。 总之,"数据结构算法演示系统"是一个强大的学习工具,...

    python,python-sorts.rar

    在“python,python-sorts.rar”这个压缩包中,我们很可能找到了关于Python排序算法的资料。排序是计算机科学中的基本概念,它涉及到如何有效地对一组数据进行排列,使得它们按照特定的顺序(如升序或降序)呈现。...

    scala-sorts:scala中的排序算法

    `scala-sorts` 项目专注于展示如何在 Scala 中实现不同的排序算法。这里我们将深入探讨这些算法,以及它们在实际编程中的应用。 首先,让我们了解排序算法的基本概念。排序是将一组数据按照特定顺序排列的过程。在...

    java 排序算法

    排序算法可以分为几大类:如冒泡排序、选择排序、插入排序、快速排序、归并排序等。 ### 1. 冒泡排序(Bubble Sort) 冒泡排序是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序...

    sorts:带打字稿的排序算法

    《sorts:带打字稿的排序算法》 在编程领域,排序算法是核心基础知识之一,对于提升程序效率和理解数据处理逻辑至关重要。本资源主要关注的是使用TypeScript实现的各种排序算法,TypeScript是一种强类型、面向对象的...

    北京师范大学数据结构教学资料第9章——排序.ppt

    本章主要讨论五种基本的排序算法:插入排序、交换排序、选择排序、归并排序和基数排序。 1. **插入排序(Insertion Sort)**: 插入排序的基本思想是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描...

    算法ppt.zip

    8. **字符串排序(String Sorts)**:091_51StringSorts.pdf 专门讨论字符串的排序问题,涉及到字符串比较的细节和特定的排序算法,如Trie树、Boyer-Moore排序等。 9. **基础符号表(Elementary Symbol Tables)**:061...

    sorts code

    本文将详细阐述标题“sorts code”中提及的六种排序算法:二分插入排序(bin-sort)、冒泡排序(bubble-sort)、插入排序(insert-sort)、归并排序(merge-sort)、快速排序(quick-sort)和希尔排序(shell-sort)...

    rust-sorts:使用全面的测试和基准测试,在Rust中实现常见的排序算法

    Rust中不同排序算法的比较。 这包括mergesort,quicksort,heapsort,插入排序,选择排序,冒泡排序甚至bogo排序。 该库附带了不同大小的向量和已经排序的向量或所有元素均等的向量的基准。 该库还带有QuickCheck...

    Sorts:一些排序算法的时序测试

    排序一些排序算法的时序测试对不起,界面不是很直观……希望如果你能阅读代码,你就能弄清楚如何运行它。 我建议您将输出重定向到一个文件(扩展名为 .csv)并使用电子表格打开。 或者在 GnuPlot 中绘图! 例如使用...

    C#实现各个算法可视化

    对于**排序算法**,如在文件名为"sorts"的子文件中所暗示的,是算法可视化中的常见主题。常见的排序算法包括: 1. **冒泡排序**:通过重复遍历待排序的元素列表,每次比较相邻两个元素并交换位置,直到列表排序完成...

Global site tag (gtag.js) - Google Analytics