赞
踩
sorted in place
):空间复杂度为O(1)
的排序算法(n-1)
,交换的次数为0
,即O(n)
。(n-1)+(n-2)+...+1=n*(n-1)/2
,交换的次数n*(n-1)/4
,即
O
(
n
2
)
O(n^2)
O(n2)。i
,j
,元素发生交换的标记flag
,交换元素的临时变量temp
,额外空间复杂度为O(1)
def bubble_sort(self, nums): """冒泡排序 从前到后依次比较前后两个元素的值,若前面元素大于后面元素的值,则交换两个元素的位置。每一轮比较都会至少确定一个元素的最终位置,在每一轮比较之后检查当前轮是否发生元素交换,若无则说明序列已排好序,无需后续操作。时间复杂度O(n^2),空间复杂度O(1),原地排序,稳定排序 :return: 排好序的数组 """ if len(nums) <= 1: return nums for i in range(len(nums)): flag = False # 标记当前一趟排序是否有元素发生交换,若无,提前完成排序 for j in range(len(nums)-1-i): if nums[j] > nums[j+1]: # 保证稳定排序 nums[j], nums[j+1] = nums[j+1], nums[j] flag = True if not flag: break return nums
1+2+...+(n-1)=n*(n-1)/2
,即O(n^2)
;对于从头到尾的比较顺序,比较次数为1+1+...+1=(n-1)
,交换次数为0+0+...+0=0
,即O(n)
.故对于待排序元素逆序对远远多于正序对的情况,应该使用从头到尾的比较顺序,反之应该使用从尾到头的比较顺序。1+1+...+1=(n-1)
,交换次数为0+0+...+0=0
,即O(n)
;对于从头到尾的比较顺序,比较和交换的次数为1+2+...+(n-1)=n*(n-1)/2
,即O(n^2)
O(n)
,n次插入操作的平均时间复杂度为O(n^2)
i
,j
,交换元素的临时变量temp
,额外空间复杂度为O(1)
def insert_sort(nums): """插入排序 将原始排序元素划分为已排序部分和未排序部分,每次取未排序部分的首个元素依次和已排序部分的元素进行比较,将其插入合适的位置,当未排序部分没有元素时结束。 """ if len(nums) <= 1: return nums # 从第二个值开始,依次和前面的比较 for i in range(1, len(nums)): flag = False for j in range(i, 0, -1): if nums[j] < nums[j-1]: nums[j], nums[j-1] = nums[j-1], nums[j] flag = True if not flag: break return nums
(n-1)+(n-2)+...+1=n*(n-1)/2
。最好情况下元素交换的次数为0
,最坏情况下元素交换的次数为1+1+...+1=(n-1)
,平均情况下元素交换的次数为(n-1)/2
。总的时间复杂度为O(n^2)
i
,j
,每轮中获得的最小值min
及其下标index
,交换元素的临时变量temp
,额外空间复杂度为O(1)
def select_sort(nums):
"""选择排序
将原始待排序数组划分为已排序部分和未排序部分,每次遍历未排序部分,从中选出最小值,将其添加到已排序部分,实为与未排序部分第一个元素交换位置(不稳定),并将此位置划分为已排序部分。时间复杂度为O(n^2),空间复杂度为O(1),不稳定
"""
if len(nums) <= 1:
return nums
for i in range(len(nums)):
# 找最小值
index_min = -1
for j in range(i, len(nums)):
if nums[j] <= nums[index_min]:
index_min = j
# 交换位置
nums[i], nums[index_min] = nums[index_min], nums[i]
return nums
temp
,额外空间复杂度为O(n)
,不是原地排序算法def merge_sort(nums): """归并排序 将待排序数组平均划分为两个数组,分别进行归并排序,再将两个排好序的数组合并。 """ if len(nums) <= 1: return nums mid = len(nums)//2 nums1 = self.merge_sort(nums[:mid]) nums2 = self.merge_sort(nums[mid:]) return self._merge(nums1, nums2) def _merge(nums1, nums2): """合并两个已排序数组 1.有一个为空;2.有一个数组元素全部小于另一个数组;3. """ nums = [] i, j = 0, 0 # 对两个数组按照元素大小链接,直到其中一个为空 while i < len(nums1) and j < len(nums2): if nums1[i] <= nums2[j]: nums.append(nums1[i]) i += 1 else: nums.append(nums2[j]) j += 1 # 将剩下的数组添加到结果尾部 if i == len(nums1): nums.extend(nums2[j:]) else: nums.extend(nums1[i:]) return nums
p:r
的一段数据,选择任意一个数据作为分区点pivot。遍历p:r之间的数据,小于pivot的放在左边,大于pivot的放在右边,pivot放在中间。再对p:q-1和q+1:r的数据分别重复这个过程。对于将数组按照分区点左右划分的过程,可以使用两个数组分别存储小于pivot和大于pivot的数据,空间复杂度为O(n);一种更加巧妙的做法是:遍历数组中的元素,若小于分区点则将其与数组第i个元素交换(i初始化为0),i++,遍历完成后将a[i]与pivot交换(pivot一般设为数组最后一个元素)。start,mid,end
,分区点的值pivot
,两层循环次数i,j
,额外空间复杂度为O(1)
,是原地排序算法def quick_sort(nums): """ 快速排序,将划分函数独立出去,使用O(1)的空间复杂度 """ self._quick_sort_between(nums, 0, len(nums) - 1) def _quick_sort_between(a, start, end): """对数组a在[start: end]区间进行快速排序""" if start < end: mid = self._partition(a, start, end) self._quick_sort_between(a, start, mid - 1) self._quick_sort_between(a, mid + 1, end) def _partition(a, start, end): """划分函数""" pivot = a[end] i = start for j in range(start, end): if a[j] < pivot: a[i], a[j] = a[j], a[i] i += 1 a[i], a[end] = a[end], a[i] return i
n,n/2,n/4,...,1
,此等比数列的和为
(
1
−
2
(
l
o
g
n
+
1
)
)
/
(
1
−
2
)
=
2
n
−
1
(1-2^{(logn+1)})/(1-2)=2n-1
(1−2(logn+1))/(1−2)=2n−1,时间复杂度为
O
(
n
)
O(n)
O(n)。[0,1)
上。桶排序的思想就是把区间[0,1)
划分为n
个相同大小的子区间,再把区间内的元素划分到对应的桶里面,对每个桶的数据单独进行排序(如使用快速排序算法)。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成序列。n
个数据均匀的划分到m
个桶里,每个桶里有k=n/m
个元素。每个桶内部使用快速排序,时间复杂度为O(k*logk)
,m
个桶排序的时间复杂度为O(m*k*logk)
,即O(n*log(n/m))
。当m接近n时,将log(n/m)
看做非常小的常量,这时桶排序的时间复杂度接近O(n)
。O(nlogn)
的排序算法了。def bucket_sort(nums, bins = 10):
"""桶排序,每个桶内部使用快速排序 """
num_max = nums.max()
num_min = nums.min()
step = (num_max - num_min)/bins
ans = []*bins
for i in range(len(nums)):
bucket_index = int((nums[i]-num_min)//step)
ans[bucket_index].append(nums[i])
# 对每个桶运行排序算法
res = []
for j in range(bins):
ans[j] = quick_sort(ans[j])
res.extend(ans[j])
return res
O(n)
,空间复杂度为O(1)
O(n)
,空间复杂度为O(k)
O(n)
,空间复杂度为O(1)
O(n)
,空间复杂度为O(n)
O(n)
,空间复杂度为O(1)
O(n+k)
,空间复杂度为O(n+k)
def count_sort(nums): """计数排序 按照元素取值范围划分,统计每个桶的数量,拼接为最终结果。适用于元素取值范围有限的输入数组。时间复杂度O(n),空间复杂度O(n) """ # 确定数组范围 num_max = nums.max() num_min = nums.min() # 统计元素个数 count = np.zeros((num_max - num_min + 1,), dtype=int) for num in nums: count[num-num_min] += 1 # 结果拼接 res = [] for i in range(len(count)): if count[i] != 0: res.extend([num_min+i]*count[i]) return res
O(k*n)
排序算法 | 最坏 | 平均 | 最好 | 稳定性 | 原地排序 | 是否基于比较 | 代码复杂度 |
---|---|---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | O(n) | 稳定 | 是 | 是 | 简单 |
插入排序 | O(n^2) | O(n^2) | O(n^2) | 稳定 | 是 | 是 | 简单 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | 不稳定 | 是 | 是 | 简单 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | 稳定 | 否 | 是 | 较复杂 |
快速排序 | O(n^2) | O(nlogn) | O(nlogn) | 不稳定 | 是 | 是 | 较复杂 |
桶排序 | O(nlogn) | O(nlog(n/m)) | O(n) | 稳定 | 否 | 否 | 较复杂 |
计数排序 | O(n+k) | O(n+k) | O(n+k) | 稳定 | 否 | 否 | 较复杂 |
基数排序 | O(k*n) | O(k*n) | O(k*n) | 稳定 | 否 | 否 | 较复杂 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。