数据结构与算法Swift1冒泡

假设你的少年棒球队在操场上排成一列,如下图所示。标准的九名队员加一名替补上场训练。你想让队员从低到高(最矮的站左侧)站成一队拍张合照。如何排序呢?

人类比计算机程序有优势。人可以一眼看清所有的队员,马上挑出个子最高的一位,不需要费劲测量比较每个人。而且,队员也不需要站在特定的位置。他们推推挤挤就能让出空间,可以站在别人前面或者后面。经过一番调整,很容易就能按顺序排好。

但计算机程序无法用这种方式迅速浏览数据。程序一次只能比较两个选手,因为比较操作符就是这么工作的。算法的这种“隧道视觉”(指看不到边缘视觉的现象)将一再出现。这种事情对人类看起来很简单,但是,算法无法看到全景,因此,必须专注于细节,遵循某些简单的规则。

冒泡排序

冒泡排序很慢,但是概念简单,所以我们从冒泡排序开始探索各种排序技巧。

用冒泡法对棒球队员排序

设想你眼睛近视(像计算机程序一样),一次只能看到两个棒球队员,这两人相邻,你站得离他们很近。这种情况下,如何对他们排序?假设有N个队员,他们站立的位置从左到右编号,最左侧是0,最右侧是N-1。

冒泡排序这样工作:从队列的最左侧开始,比较位置0和1的两个队员。如果左侧(位置0)的队员高,则交换两人。如果右侧的队员高,则什么都不做。然后向下移动一个位置,比较位置1和位置2上的队员。如果这次左侧队员高,则再次交换。如下图所示。

规则如下:

比较两个队员;

如果左侧的队员高,则交换;

向右移动一个位置;

沿着队列反复执行下去,直到到达最右侧。此时尚未对所有人完成排序,但是最高的队员已经到了最右侧。因为只要遇到了最高的队员,每次比较两个队员时,最高的这名都会被交换,最后就被换到了队列最右侧。冒泡排序由此得名:随着算法往下执行,最大的元素“冒泡”到数组的顶端。

第一轮遍历数据后,进行了N-1次比较,做的交换次数则根据最初队员的排列,介于0到N-1之间。数组末尾的元素已经排好了顺序,不会再变动。

现在重新开始,从队列最左侧开始新一轮遍历。这次还是向右走,两两比较,需要的时候交换位置。不过这次执行到队列的倒数第二个位置N-2即可,因为最后一个位置N-1已经是最高的队员了。

这一规则可以表述为:一旦遇到一个已经排好顺序的队员,就从队列左侧重新开始。

重复执行上述操作,直到所有的队员都排好顺序。

代码

Windows和Linux均可安装Swift,不过可能需要手动设置的地方较多,在OSX上从AppStore安装了Xcode即可使用。

以下是OSXBigSur上的实现,需要安装Xcode来获取Swift工具链,但并不需要了解Xcode用法。简单的编辑器也可以,如VSCode。使用VSCode推荐安装扩展MaintainedSwiftDevelopmentEnvironment。不管使用Xcode还是文本编辑器,第一次使用Swift编译器可能遇到的问题:

xcrun:error:unabletofindutility"xctest",notadevelopertoolorinPATH

这个问题是CommandLineTool未设置。在Xcode菜单中,找到Preferences-Locations-CommandLineTool,在下拉菜单中选中Xcode默认推荐的项即可

error:rootmanifestnotfound

Swift所说的“rootmanifest”是指Swift.Package文件,按照swiftpackageinit创建项目不会出现此问题。

首先创建一个BubbleSort项目:

mkdirBubbleSortcdBubbleSortswiftpackageinit--typeexecutabletouchSources/BubbleSort/BubbleSort.swiftcode.

打开Sources/BubbleSort/BubbleSort.swift:

funcbubbleSort(a:[Int])-[Int]{guarda.count1else{returna}varsortedArray=aforiin(0..sortedArray.count).reversed(){forjin0..i{ifsortedArray[j]sortedArray[j+1]{sortedArray.swapAt(j,j+1)}}}returnsortedArray}

在Source/BubbleSort/main.swift中输入:

print(bubbleSort(a:[77,99,44,55,22,88,11,00,66,33]))

Swift函数的参数是不变量,因此不能直接改动传入的数组中的元素。

编译运行程序有三种方式,在Terminal中的当前项目目录下:

使用swiftc:

swiftc-o.build/BubbleSortSources/BubleSort/main.swiftSources/BubbleSort/BubbleSort.swift

编译好的二进制文件在当前项目下的.build目录下。执行它:./.build/BubbleSort,终端中可以看到排序完成的数组。

使用swiftrun命令编译并运行程序,终端中输出运行结果。

使用swiftbuild--show-bin-path编译到.build目录下,--show-bin-path参数会显示编译完的二进制文件所在位置,如/Users/xxx/BubbleSort/.build/x86_64-apple-macosx/debug。

不变量

很多算法中,有些条件不会随着算法的执行而变化。这些条件称为不变量(invariant)。找出不变量有助于理解该算法。

在冒泡排序中,不变量就是右侧已经排好顺序的数据元素。

冒泡排序的效率

以10个元素为例,第一轮要做9次比较,第二轮8次,以此类推,比较的总数为:

9+8+7+6+5+4+3+2+1=45

概括起来,如果数组中有N个元素,第一次循环要做N-1次比较,第二轮N-2次...,求和公式为:

(N-1)+(N-2)+(N-3)+...+1=N*(N-1)/2

当N等于10时,N*(N-1)/2是45。

因此,排序算法进行了(N^2)/2次比较(忽略-1,不会对结果有多大影响,尤其是N取值非常大的时候)。

交换的次数比比较的次数少,因为只有在必要时才交换。如果数据是随机的,交换的次数大约是比较次数的一半,即(N^2)/4次交换。(不过也有最糟糕的情况,即数组初始时倒着排序的,那么每次比较都需要交换)。

交换和比较的次数都和N^2成比例。BigO中不计算常量,可以忽略掉2和4,冒泡排序需要的时间是O(N^2)。

每当看到类似上述代码中的嵌套循环,就可以认为该算法运行时间为O(N^2)。外部循环执行N次,每次内部循环都需执行N次(也可能是N除以某个常数)。执行时间接近N^2。

预览时标签不可点收录于话题#个上一篇下一篇


转载请注明:http://www.92nongye.com/ksfc/ksfc/204625764.html

  • 上一篇文章:
  •   
  • 下一篇文章: