地精排序(Gnome Sort) 最简单的排序算法

地精排序(Gnome Sort)——最简单的排序算法

写的GLSL程序要用中值滤波,于是想写一个简单的排序算法,看了维基百科的选择排序、插入排序,发现了一个有意思,号称最简单的排序算法,地精排序(Gnome Sort)

The simplest sort algorithm is not Bubble Sort..., it is not Insertion Sort..., it's Gnome Sort!

Gnome Sort is based on the technique used by the standard Dutch Garden Gnome (Du.: tuinkabouter). Here is how a garden gnome sorts a line of flower pots. Basically, he looks at the flower pot next to him and the previous one; if they are in the right order he steps one pot forward, otherwise he swaps them and steps one pot backwards. Boundary conditions: if there is no previous pot, he steps forwards; if there is no pot next to him, he is done.

Dick Grune

// Gnome sort

// http://www.cs.vu.nl/~dick/gnomesort.html

void gnomesort(int n, int ar[]) {

    int i = 0;

 

    while (i < n) {

       if (i == 0 || ar[i-1] <= ar[i]) i++;

       else {int tmp = ar[i]; ar[i] = ar[i-1]; ar[--i] = tmp;}

    }

}

 

代码很短,只有一层循环,相当简单。下面改写成C++函数模板。

// Gnome sort version 1

template<class T>

void gnome_sort_1(T data[], int n, bool comparator(T, T))

{

    int i = 1;

    while (i < n)

    {

       if (i > 0 && comparator(data[i], data[i-1]))

       {

           swap(data[i], data[i-1]);

           i--;

       }else

       {

           i++;

       }

    }

}

 

算法还可以优化。

Optimization

The gnome sort may be optimized by introducing a variable to store the position before traversing back toward the beginning of the list. This would allow the "gnome" to teleport back to his previous position after moving a flower pot. With this optimization, the gnome sort would become a variant of the insertion sort.

http://en.wikipedia.org/wiki/Gnome_sort

Gnome sort version 2,地精在回头检查花盘顺序前记下当前位置,整理完就瞬间转移到之前位置的下一个位置。瞬间转移,很强大,哈哈。

// Gnome sort version 2

template<class T>

void gnome_sort_2(T data[], int n, bool comparator(T, T))

{

    int i = 1, previous_position = -1;

    while (i < n)

    {

       if (i > 0 && comparator(data[i], data[i-1]))

       {

           // Mark the Gnome's previous position before traverse backward

           if (previous_position == -1)

           {

              previous_position = i;

           }

           swap(data[i], data[i-1]);

           i--;

       }else

       {

           if (previous_position == -1)

           {

              i++;

           }else

           {

              // After traverse backward, go to the position next to the previous

              i = previous_position + 1;

              previous_position = -1;

           }

       }

    }

}

 

Gnome sort version 2类似插入排序,不断把列表后面待排序的元素插入到列表前面已排序的部分中,但类似于冒泡排序那样比较并交换相邻元素。因此其算法复杂度与插入排序相当,但比插入排序需要更多的复制操作(用于交换相邻元素)。
Gnome sort version 3,搬了无数花盘后,地精想到一个新方法。如果交换相邻两个花盘,每次要搬花盘三次(复制三次),如果先拿起来当前位置的花盘(temp=data[i]),就有空位给其他花盘移动,每次只搬一次花盘(复制一次,data[i]=data[i-1])。这次地精先拿一个花盘在手(temp),如果手中花盘(temp)应放到上一个花盘(i-1)前面,就用脚(地精一般不高,脚够长吗?^_^)将上一个花盘(i-1)踢到当前位置(i),然后拿着花盘(temp)走到上一个位置(i=i-1),重复此过程。

// Gnome sort version 3

template<class T>

void gnome_sort_3(T data[], int n, bool comparator(T, T))

{

    int i = 0, previous_position = -1;

    T temp;

    while (i < n)

    {

       if (i > 0 && comparator(temp, data[i-1]))

       {

           // Mark the Gnome's previous position before traverse backward

           if (previous_position == -1)

           {

              previous_position = i;

           }

           data[i] = data[i-1];

           i--;

       }else

       {

           if (previous_position == -1)

           {

              i++;

           }else

           {

              // Put the previous value here

              data[i] = temp;

              // After traverse backward, go to the position next to the previous

              i = previous_position + 1;

              previous_position = -1;

           }

           temp = data[i];

       }

    }

}

 

Gnome sort version 3已经和插入排序一样了,呵呵。
下面是插入排序,选择排序,比较一下。

// Insertion sort

// http://en.wikipedia.org/wiki/Insertion_sort

template<class T>

void insertion_sort(T data[], int n, bool comparator(T, T))

{

    for (int i=1; i<n; i++)

    {

       T temp = data[i];

       int j;

       for (j=i-1; j>=0; j--)

       {

           if (comparator(temp, data[j]))

           {

              data[j+1] = data[j];

           }else

           {

              break;

           }

       }

       data[j+1] = temp;

    }

}

 

// Selection sort

// http://en.wikipedia.org/wiki/Selection_sort

template<class T>

void selection_sort(T data[], int n, bool comparator(T, T))

{

    for (int i=0; i<n; i++)

    {

       // Select the minimum

       int min = i;

       for (int j=i+1; j<n; j++)

       {

           if (comparator(data[j], data[min]))

           {

              min = j;

           }

       }

       if (min != i)

       {

           swap(data[min], data[i]);

       }

    }

}

 

// Comparator

template<class T>

bool smaller(T v1, T v2)

{

    return v1 < v2;

}

 

地精排序最显著的特点是代码只有一层循环,原版算法代码极短,但效率不高;改进后相当于插入排序的变种,不过条件判断多了,代码比标准的插入排序长。
以类似方式,选择排序,冒泡排序也可以改成一层循环。
在简单的基于比较的排序中(不包括快速排序,归并排序,堆排序等),插入排序总体性能不错,建议作为首选;而选择排序有最少的元素复制次数,当元素复制代价较大时(如存储介质的写入速度比读取速度慢很多)是个好选择。
另外,只做部分的选择排序,就成了选择第n小元素的选择算法。正如部分的快速排序成了quickselect选择算法。
Copyright (c) 2010 Ark. All rights reserved. J
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值