- 冒泡排序:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。时间复杂度: n(n-1)/2 = O(n²)
计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的
运行时间
。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。
func bubbleSort(_ arr : Array<Int>) -> Array<Int> {
var array = arr
var num = 0
//理论上i应该小于array.count-1,因为n个元素只需要进行n-1轮循环比较就能确定n-1个元素,剩下的哪一个元素无需比较就已经确认啦.但是由于内部条件有限制,实际上第n轮循环时i=n-1,这时内部的循环就不再进行啦,所以也不会有问题,但不够严谨.
for i in 0..<array.count {
//每完成一次内部循环,就会获取当前循环中的最大值,放在最后位,下次就不用再比较,这就是-i的原因
for j in 0..<array.count-1-i {
if array[j] > array[j+1] { //如果是倒叙就<,正序就>
array.swapAt(j, j+1)
// let temp = array[j+1]
// array[j+1] = array[j]
// array[j] = temp
// print(array)
}
num = num + 1
print(num)
}
}
return array
}
-
选择排序 : Selection sort是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。
选择排序算法十分简单易懂,但是它的效率比较低,是O(n^2)。它比插值排序更低效但比冒泡排序更高效。在未排序数组中寻找最小值的过程很慢,而这个过程又是不断被重复的。
//选择排序
func selectionSort(_ arr : Array<Int>) -> Array<Int>{
var array = arr;
//有n个数字只需要循环n-1轮就能确定n-1个数字的位置,那么剩下的那一个也就确定了
for i in 0..<array.count-1{
//m是用来获得最小(大)数的脚标
var m = i
for j in i+1..<array.count{
if array[j] < array[m]{ //小于号就每次循环获得最小数,大于号就是每次循环获得最大数.
m = j
print(m)
}
}
//m是每轮内部循环后标记最小数的脚标,i是要插入的顺序,如果不等就说明该轮需要替换两个数的位置.如果相等就说明当前循环最前的就是最小的数字,不需要替换位置.
if i != m{
array.swapAt(i, m)
print(array)
}
}
return array;
}
关键点在于m
是标记每轮最小数的脚标,i
是当前已经排序的当前内部循环获得最小数要插入的位置(当前轮最小脚标).
- 插入排序 : Insertion sort是一种简单直观且稳定的排序算法.选取一个数字,并将其插入在数组里正确的排序位置。比如一个无序的数组A,我先取出第一个元素放入空数组B中,再选取第二个元素,如果比B中元素大就放在后面,否则放在前面,第三次就取A中的第三个元素,和B中的第一个元素比较,如果小就插入到最前面,如果大就和B中的第二个元素比较,如果小就插入到B中的第一个元素和第二个元素之间,如果大就插入到B中的第2个元素后面,依次类推.这个为了便于理解分成了两个数组,其实可以是一个数组,只是要标记哪一部分是已经排序过的,哪一部分是为排序的.具体可参考插入排序. 最坏情况下插入排序的平均情况性能是 O(n ^ 2) (最坏的情况就相当于冒泡排序)
func insertionSort(_ arr: [Int]) -> [Int] {
var array = arr
// i相当于分割,即将排序的那一个,0-x是已经排序的d,0-count-1是未排序的,因为第一个元素移过去不需要比较排序所以从1开始,还是需要n-1次外部循环
for i in 1..<array.count {
var index = i
//在while循环之前index是未排序的,index-1及以前都是已经排序后的,循环之后index位就是已排序的了,因为是从后往前比,一但找到合适的位置(array[index] < array[index - 1]),就不在会继续往下比较了,就插入进去(array.swapAt(index - 1, index))
while index > 0 && array[index] < array[index - 1] { //小于号是正序排列,大于号是倒序排列
array.swapAt(index - 1, index)
print(array)
//防止数组取值脚标越界,-1是不能为数组脚标的
index -= 1
}
}
return array
}
- 折半查找: 也叫折半搜索(英语:half-interval search),又称二分搜索(英语:binary search)、对数搜索(英语:logarithmic search),是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半.
func binarySearch<T: Comparable>(_ a: [T], key: T) -> Int? {
//小脚标
var lowerBound = 0
//大脚标
var upperBound = a.count
//循环查找的条件是小脚标必须小于大脚标
while lowerBound < upperBound {
//取中间脚标 lowerBound + (upperBound - lowerBound) / 2
let midIndex = (lowerBound+upperBound)/2
//如果中间脚标的值等于目标值,则返回该脚标
if a[midIndex] == key {
return midIndex
//如果中间脚标的值比目标值小,则将最小脚标设置为比现在的中间脚标大1,然后进行下一次循环
} else if a[midIndex] < key {
lowerBound = midIndex + 1
//如果中间脚标的值比目标值大,则将最大脚标设置为现在的中间脚标,为什么不减1,因为这样就可能出现找不到边界值的情况,比如a = [1,22],key = 1,这时候如果是upperBound = midIndex- 1返回的就是nil
} else {
upperBound = midIndex
}
}
return nil
}
注意:这边查找的前提是数组已经是有序的,并且不能有重复数字.