“算法”的入门,从“排序算法”开始,但愿经过“排序算法”这一部分的学习,可以让咱们认识到“算法”的威力,“算法”不只仅只存在与咱们的面试中(那时只是由于我不知道“算法”而已),“算法”无处不在,“算法”颇有用。java
下面是一些说明:python
一、会直接使用“空间复杂度”和“时间复杂度”的概念,不妨先有个印象,实在纠结的话,能够去翻翻书,“空间复杂度”和“时间复杂度”最多的应用就在于比较不一样算法的优劣;面试
二、“排序算法”这一章节为了方便说明,使用的例子都是以“整数数组”为例,而且是“升序排序”,学习过 Java 语言的朋友就知道,待排序的也能够是对象,只要实现了相关的接口,实现了相应的比较规则,就能够进行排序。算法
咱们选择“选择排序”做为算法入门的开篇。理由以下:编程
一、“选择排序”算法的思想十分简单,很是接近咱们的思惟方式:先找最小的数、再找第 2 小的数,依次类推,最后剩下的就是数组中最大的元素;数组
二、“选择排序”的实现也很简单。编程语言
思想:不断地选择剩余元素之中的最小者。函数
一、每一轮交换都能排定一个元素,交换的总次数是固定的;学习
说明:“交换的总次数”等于“元素的总数 - 1”,所以算法的时间复杂度取决于比较的次数;测试
二、运行时间和输入无关,即:一个“已经有序”的数组、一个全部的元素都相等的数组、一个元素随机排列的数组所用的排序时间是同样的;
说明:后续咱们会编写一些测试用例,比较不一样的算法在不一样的测试用例上的运行时间。这些测试用例中,就有如下 $3$ 种。
(1)一个“已经有序”的数组:例如:[4, 5, 6, 8, 9, 10]
,之后咱们学习的排序算法中,就有一种算法名叫“插入排序”就能检测出数组是否是有序的,极端状况下,“插入排序”算法看一遍数组中的元素,就知道数组已经有序了,后续就什么都不用作了。而“选择排序”得一遍又一遍看数组的元素好几遍,“几乎是”有多少个数,就会看数组多少遍,每一遍选出当前没有排定元素中的最小者;
(2)一个全部的元素都相等的数组,例如:[6, 6, 6, 6, 6, 6]
;
(3)一个元素随机排列的数组,就是咱们通常意义下,杂乱无序的数组,例如:[8, 18, 10, 6, 5, 4, 20]
。
三、数据移动是最少的。
这点应该说是“选择排序”的优势了,若是咱们的排序任务对交换操做很是敏感,不妨考虑“选择排序”。
例如:咱们待排序的是码头上的集装箱,交换集装箱的成本是很高的,此时“选择排序”就是最好的选择。
小贴士:这一部份内容不须要记住,等到后面接触了“插入排序”、“归并排序”、“快速排序”等其它排序算法之后,再与“选择排序”进行比较,就不难理解了。
Python 实现1:
def swap(nums, idx1, idx2): if idx1 == idx2: return temp = nums[idx1] nums[idx1] = nums[idx2] nums[idx2] = temp def select_sort(nums): """ 选择排序,记录最小元素的索引,最后才交换位置 :param nums: :return: """ l = len(nums) for i in range(l): min_index = i for j in range(i + 1, l): if nums[j] < nums[min_index]: min_index = j swap(nums, i, min_index)
说明:交换两个数组中的元素,在 Python 中有更简单的写法,这是 Python 的语法糖,其它语言中是没有的。
Python 实现2:主体部分和“Python 实现1”是同样的。
def select_sort(nums): """ 选择排序,记录最小元素的索引,最后才交换位置 :param nums: :return: """ l = len(nums) for i in range(l): min_index = i for j in range(i + 1, l): if nums[j] < nums[min_index]: min_index = j nums[i], nums[min_index] = nums[min_index], nums[i]
这就是“选择排序”算法。
若是你看到本身编写的程序不正确,能够在程序中增长打印输出,帮助你调试程序:
分析:第 1 轮要看 n 个元素;
第 2 轮要看 n-1 个元素;
第 3 轮要看 n-2 个元素;
……
第 n 轮要看 1 个元素;
对它们求和,用等差数列的通项公式。不过其实你也不用计算它,“时间复杂度”的计算咱们只看次数最高的,因此“选择排序”是平方时间复杂度。
分析:咱们在交换两个数组元素位置的时候,使用了 1 个辅助的空间。
是否是以为很简单,后面难度会一点一点加上来。此时,咱们不妨作一些热身的练习,咱们后面会用到。这些练习只是减轻一点咱们后面编写测试用例的工做量,本身设计函数参数就好。
练习1:编写三个函数,分别生成上文中提到的 3 种类型的数组,要求可以自定义生成数组的大小,这样咱们之后编写测试用例的时候,就可使用这些函数了。
练习2:编写一个函数,判断一个数组是不是升序排序。这个函数用于判断咱们的算法是否正确。
如下补充的知识是针对零基础的朋友们的,由于我也是零基础过来的,以为这些东西能够说一下。
交换两个变量的值,在排序中是常见的操做,而且也是程式化的,特别好记。先给出 Java 的写法,再给出 Python 的写法,最后给出“不是人的写法”。
Java 写法:
int temp = a; a = b; b = temp;
说明:这段代码其实很好理解,要交换两个变量的值,给要让变量 a 把位置让出来,即 int temp = a
,而后把另外一个变量 b 的值复制给 a,即 a = b
,最后把以前 a 放在 temp 里的值赋给 b。这么说比较拗口,但其实我每次写这段代码的时候,都不用想这个过程的。由于这段代码有规律可循:首先引入一个辅助变量 temp,这是必要的,而后就开始“首尾相接”了,大家看一下,是否是这个特色,最后接回 temp,记住这个规律就能够了。在 Python 中是这样写的:
Python 写法1:
temp = a a = b b = temp
不过,Python 是一门神奇的编程语言,它提供了语法糖。
a, b = b, a
就能够交换两个变量的值,不妨动手验证一下:
是否是很酷,Python 的写法有的时候更像伪代码,更符合人的思惟,但我没有说 Python 更好的意思。其实 Python 解释器在后台也是引入了辅助变量完成两个变量的交换。其实,交换两个变量的值,有更高效的作法,下面给出两个交换变量的代码,这两种方法都不用引入辅助变量,相信聪明的你必定不难理解。
这里利用到了异或运算的特色:异或运算能够理解成不进位的加法。那么一个数两次异或同一个数,就和原来的数相等。上面基于异或运算交换两个变量的值就利用这个性质。若是你还不熟悉异或运算,不妨查阅一些资料。
前面咱们说到了,咱们为了突出排序算法的思想,将全部的例子仅限在数组排序中。事实上 Java 和 Python 这些面向对象的编程语言都支持对象的排序,只要给它们定义相应的比较规则便可。有两种方式,Python 和 Java 都是支持的:
(1)为对象添加用于比较的函数
def __cmp__(self, other): pass
定义这个魔法函数,就可使用对象集合进行排序了。
Comparable
接口中的 compareTo
方法。若是你以为给对象添加用于比较的函数,这种作法的侵入性比较强(由于修改了类),那么你能够在排序的方法中,传入比较规则。
(2)在排序的方法中,传入比较规则
Comparator
接口的对象。