暴力破解的泡泡。
冒泡排序是一种简单的比较排序算法,它重复地遍历要排序的列表,一次比较两个元素,并且如果它们的顺序错误就将它们交换过来。这个过程一直持续,直到没有再需要交换的元素,即列表已经排序完成。
基本思想是通过不断地交换相邻的元素,将较大的元素逐渐“浮”到数组的末端,而较小的元素逐渐沉到数组的前端。就像是泡泡不断浮动。
比较相邻元素:从列表的开头开始,比较相邻的两个元素。 交换元素:如果顺序不正确,交换这两个元素。 遍历列表:重复以上两步,直到整个列表排序完成。每一轮遍历都能确定一个未排序部分的最大值或最小值。 时间复杂度:
冒泡排序的时间复杂度为 O(n^2),其中 n 是数组的长度。在最坏情况下,即列表完全逆序时,需要进行 n*(n-1)/2 次比较和交换。在最好情况下,即列表已经有序时,只需要进行一次遍历,但仍然需要进行 n 次比较。
是稳定的排序算法,即相等元素的相对位置在排序前后不发生改变。
是一种简单但效率较低的排序算法,对于小型数据集或者基本有序的数据集来说可能是合适的选择。
由于其简单的实现方式,冒泡排序通常用于教学和理解排序算法的基本原理。但是其实我觉得在代码实现上不是很好理解,至少我需要打印出来进行观察。
冒泡排序是可以优化的,可以通过设置一个标志来进行优化,如果在一轮遍历中没有进行过交换,说明列表已经有序,可以提前结束排序。
def bubbleSort(arr): # O(n^2)
count = 0
for i in range(len(arr) - 1):
for j in range(len(arr) - i - 1):
count += 1
if array[j] > array[j + 1]:
array[j], array[j + 1] = array[j + 1], array[j]
return (f'{array} \nNumber of comparisons = {count}')
也是一种简单的排序算法,其基本思想是通过不断选择未排序部分的最小(或最大)元素,将其放到已排序部分的末尾。总之就是:选择和swap
选择排序的时间复杂度是 O(n^2),其中 n 是数组的长度。在每一轮选择过程中,需要进行 n 次比较,找到最小元素。总共需要进行 n-1 轮选择操作。
它是一种不稳定的排序算法,即相等元素的相对位置可能发生改变。
选择排序对于小型数据集可能是一种适用的选择,但在大型数据集上其性能相对较差。
总体而言,选择排序是一种简单但不是最优的排序算法,它的主要优点在于实现简单,但在大规模数据集上通常不如其他高效的排序算法。
一种解决方案:
def findSmallest(array):
smallest = array[0]
smallest_index = 0
for i in range(1, len(array)):
if array[i] < smallest:
smallest = array[i]
smallest_index = i
return smallest_index
# 这个方法是重新填充一个新的数组
def selectionSort(array):
newarr = []
for i in range(len(array)):
smallest = findSmallest(array)
newarr.append(array.pop(smallest))
return newarr
另一种(from educative)更加选择排序一些:大O是n的平方级别
def swap(array, firstIndex, secondIndex):
temp = array[firstIndex]
array[firstIndex] = array[secondIndex]
array[secondIndex] = temp
def indexOfMinimum(array, startIndex):
minValue = array[startIndex]
minIndex = startIndex
for i in xrange(minIndex + 1,len(array)):
if array[i] < minValue:
minIndex = i
minValue = array[i]
return minIndex
def selectionSort(array):
for i in range(len(array)):
min_index = indexOfMinimum(array, i)
swap(array, i, min_index)
return array
通过遍历数组,每次都对子数组的最后一个元素进行(和他之前的每个元素)比较操作,然后插入正确位置,最终得到结果。
是一个稳定的排序算法(当出现相同元素的时候,排序后他们的相对位置不发生改变),时间复杂度是O(n)。但是注意n的时间复杂度不是最坏的情况,最坏可能会是n方复杂度。
def insertionSort(arr):
for i in range(1, len(arr)):
j = i - 1
while j >= 0 and arr[j + 1] < arr[j]:
arr[j + 1], arr[j] = arr[j], arr[j + 1]
j -= 1
return arr
将一部分代码分开的写法如下,看起来似乎比较好理解一点。
def insert(array, rightIndex, value):
j = rightIndex
while j >= 0 and array[j] > value:
array[j + 1] = array[j]
j = j - 1
array[j + 1] = value
def insertionSort(array):
for i in range(1, len(array)):
rightIndex = i - 1
value = array[i]
insert(array, rightIndex, value)
return array
是一种最常用的高效算法。Divide&Conquer是很有名的算法思想。
不停地将数组一分为二,直到不能再分为止,然后递归地拿回结果进行合并。在合并的时候,每次都顺序从,两个数组的第一个开始取数并比较。
时间复杂度的计算:
代码步骤:
其实有很多代码的写法,我个人比较喜欢下面这样的写法,对我来说很好理解。
def merge(left, right): # O(n)
result = []
leftindex = 0
rightindex = 0
while leftindex < len(left) and rightindex < len(right):
if left[leftindex] < right[rightindex]:
result.append(left[leftindex])
leftindex += 1
else:
result.append(right[rightindex])
rightindex += 1
return result + left[leftindex:] + right[rightindex:]
def mergeSort(arr): # O(logn)
if len(arr) == 1:
return arr
middle = len(arr) // 2
left = arr[:middle]
right = arr[middle:]
return merge(mergeSort(left), mergeSort(right))
快速排序也是一种分治思想的算法,和合并算法一样,时间复杂度也是O(nlogn)。但是最坏的情况(选择的基准元素正好是最大,或者最小)出现的话,时间复杂度将增加为O(n^2)。因此其实选择一个随机的元素是最好的选择。
是一种不稳定的排序算法,因为在分区的过程中,相对位置会发生变化。
基本方法是选择一个基准元素pivot,然后根据每个元素和这个基准元素的大小比较,而分成两个部分,一部分比基准小,一部分比基准大。
然后重复以上步骤,对分开的两个部分,递归的进行同样的操作。
快速排序是一种高效的算法,很适合大型数据集。
def quickSort(arr, start, end):
if end - start + 1 <= 1:
return
pivot = arr[end]
left = start
for i in range(start, end):
if arr[i] < pivot:
arr[left], arr[i] = arr[i], arr[left]
left += 1
arr[end] = arr[left]
arr[left] = pivot
quickSort(arr, start, left - 1)
quickSort(arr, left + 1, end)
return arr
还有一种不考虑原地排序很好理解的写法。
def quickSort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
less = [x for x in arr[1:] if x <= pivot]
greater = [x for x in arr[1:] if x > pivot]
return quickSort(less) + [pivot] + quickSort(greater)
桶排序适用于输入数据均匀分布在某个范围内的情况,而且适用于外部排序,即数据量很大,无法一次性加载到内存中进行排序的情况。
假设有一组数据在范围 [0, 1) 内,可以将这个范围划分成若干个桶,每个桶对应一个区间,然后将数据分到各个桶中,对每个桶中的数据进行排序,最后合并所有桶的数据即可得到有序结果。
假设一个数组里面只有0,1,2也就是他们在一个范围内。可以看出代码活用了index和相对元素的各种变换进行排序。
def bucketSort(arr):
counts = [0, 0, 0]
for n in arr:
count[n] += 1
i = 0
for n in range(len(counts)):
for j in range(counts[n]):
arr[i] = n
i += 1
return arr
这个排序方法用的挺少的,除了特定场合很少用到。