排序1 - 选择排序 & 插入排序
请原谅我没有按照之前图片的分类来介绍排序算法,先说最简单的两种排序算法(冒泡略过),选择排序和插入排序,之前老是容易记混。默认输出升序的序列啊,哈哈。
选择排序
对于输入长度为n的数组,一共比较n-1趟,对第 i 趟比较,从第 i 个位置开始,第n个位置结束。找出其中最小元素并记录其位置x, 然后将第 i 个元素和第 x 个元素交换位置。
说白了,第一趟遍历找第一小的元素,第二趟遍历找第二小的元素。。。 多捞啊!
给出伪代码:
selectSort(A) n ← A.length; for i from 0 to n-1 do j ← i; for k from j to n-1 do if A[k]<A[j] then j ← k; end if end for swap A[i] with A[j]; end for
时间复杂度 = 1+2+...+(n-1)
→ 时间复杂度为 O(n2),非常稳定,没有最好最坏情况,尴尬。由于没有使用额外空间,空间复杂度为O(1)。
每次插入的元素都和之间的所有元素进行了比较,所以为稳定排序。
插入排序
可以分为三步:
- 从第2个元素开始,如第 i 个元素,遍历之前索引从 0 到 i-1 的已排序序列,找到自己插入的位置。
- 将插入位置到第i-1位置的元素 后移一位
- 将第 i 个元素插入。
也即将新元素插入已经排序好的序列中,这个很像机器学习里的在线学习算法,具有学习能力。
给出伪代码:
insertSort(A) n ← A.length; for i from 1 to n-1 do # n-1 times insert insertIndex ← i; for k from i-1 downto 0 do # find insertIndex if A[k]<A[i] then insertIndex ← k+1; Break; end if end for flag ← A[i]; for k from i-1 downto insertIndex do # move back elements swap A[k] with A[k+1] end for A[insertIndex] ← flag; # finish insert end for
在第一步中,寻找插入位置,本来顺着找也可以的。但是请思考,如果顺着找,遍历操作和后移操作加在一起,怎么着都需要O(n2)总的时间复杂度。而如果倒着找,就会有最好情况,如输入序列本身有序,那么时间复杂度即为O(n)。
由于没有用到额外空间,故此处空间复杂度为O(1)。
同样,插入元素同插入位置和原位置之间的元素都进行了比较,为稳定排序。