不懂算法的程序员不是好工程师!

640?wx_fmt=gif

时刻提醒自己,技术之路修远兮,予以自勉。

640?wx_fmt=jpeg

作者 |  菜鸟奋斗史

责编 | 胡巍巍


640?wx_fmt=png

算法主要衡量标准


时间复杂度(运行时间)

在算法时间复杂度维度,我们主要对比较和交换的次数做对比,其他不交换元素的算法,主要会以访问数组的次数的维度做对比。

其实有很多同学对于算法的时间复杂度有点模糊,分不清什么所谓的O(n),O(nlogn),O(logn)......等,也许下图对一些人有一些更直观的认识。

640?wx_fmt=jpeg

空间复杂度(额外的内存使用)

排序算法的额外内存开销和运行时间同等重要。就算一个算法时间复杂度比较优秀,空间复杂度非常差,使用的额外内存非常大,笔者认为它也算不上一个优秀的算法。

结果的正确性

这个指标是笔者自己加上的,我始终认为一个优秀的算法最终得到的结果必须是正确的。就算一个算法拥有非常优秀的时间和空间复杂度,但是结果不正确,又有什么意义呢?


640?wx_fmt=png

原理


在起始位置右侧(或左侧)找出最小的那个元素,然后和起始位置的元素交换。选择排序是一个不稳定的排序算法。

具体步骤如下:

  • 在一个数据列表中找到最小的那个元素,将它和列表的第一个元素交换位置。

  • 在第二个元素位置开始再次寻找最小的那个元素,然后和列表的第二个位置的元素交换。

  • 在第三个元素位置开始再次寻找最小的那个元素,然后和列表的第三个位置的元素交换

  • 如此反复,直到开始查找起始位置到达列表末尾。

如果查找过程中最小的元素就是起止位置的元素,那么它就和它自己交换。

因为这种算法总是在不断的选择剩余元素中最小者,因此得名选择排序。


640?wx_fmt=png

复杂度


时间复杂度

1.比较次数

对于长度为N的列表,选择排序需要大约n² /2次比较.即:O(n²)平方级别。

2.交换次数

对于长度为N的列表,选择排序需要大约N次交换.即:O(N) 线性级别。


640?wx_fmt=png

性能和特点


总体来说,选择排序是一种比较简单的排序算法,很容易理解也很好用代码实现,当然他的特点也很明显:

运行时间和数据初始状态无关

为什么这么说呢?算法进行中为了查找最小的元素而遍历列表并不能为下次遍历带来任何信息,这个特性在大部分情况下是缺点。

如果一个数据列表初始状态是有序的或者部分有序的,选择排序仍然需要全部扫描一次和交换。因此和一个完全无序的列表排序所花的时间相差不大。

数据移动次数是最少的

每次交换只会改变两个列表元素,因此长度为N的列表只会发生N次交换,交换次数和列表的长度是线性关系,其他算法都不具备这个特性。


640?wx_fmt=png

适用场景


由于选择排序的对比次数在平方级别,但是移动次数在线性级别,所以当N比较小的时候比较适用。


640?wx_fmt=png

其他


为什么选择排序不稳定呢?

首先我们要明白算法稳定是什么意思呢?

在待排序的数据中,存在多个相同的数据,经过排序之后,他们的相对顺序依旧保持不变,实际上就是说array[i]=array[j],i<j.就是array[i]在array[j]之前。

那么经过排序之后array[i]依旧在array[j]之前,那么这个排序算法稳定,否则,这个排序算法不稳定。

根据以上定义很容易可以得出这样的结论:

我们举出一个实例,序列5 8 5 2 9, 这个在执行选择排序的时候,第一遍,肯定会将array[0]=5,交换到2所在的位置,也就是 2 8 5 5 9。

那么很显然,之后的排序我们就会发现,array[2]中的5会出现在原先的array[0]之前,所以选择排序不是一个稳定的排序。


640?wx_fmt=png

实现案例


c#
 
  

static void Main(string[] args)

        
{

            List<int> data = new List<int>() ;

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

            {                

                data.Add(new Random(Guid.NewGuid().GetHashCode()).Next(1100));

            }

            //打印原始数组值

            Console.WriteLine($"原始数据: {string.Join(",", data)}");

            int n = data.Count;

            //此处可以直接从第二个元素开始

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

            {               

                //查找最小的元素的索引

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

                {

                    if (data[j] < data[j - 1])

                    {

                        //异或法 交换两个变量,不用临时变量

                        data[j] = data[j] ^ data[j-1];

                        data[j-1] = data[j] ^ data[j - 1];

                        data[j ] = data[j] ^ data[j - 1];

                    }                    

                }

            }

            //打印排序后的数组

            Console.WriteLine($"排序数据: {string.Join(",", data)}");

            Console.Read();

        }

运行结果:

  • 原始数据:97,85,61,22,62,12,67,22,68,42

  • 排序数据:12,22,22,42,61,62,67,68,85,97

作者简介:一个奔走在通往互联网更高之路的工程师,热衷于互联网技术。目前就职于某互联网教育公司,应用服务端主要负责人。拥有10年+互联网开发经验。热衷于高性能、高并发、分布式技术领域的研究。 主要工作语言为C#和Golang 。

推荐阅读:

640?wx_fmt=gif

640?wx_fmt=gif

猜你喜欢

转载自blog.csdn.net/csdnnews/article/details/84939109