1.希尔排序
在上一篇,我们讲了插入排序, 也是一种基于比较的排序。其实还有一个大佬提出一种改进的插入排序:希尔排序(shell sort)。
希尔排序的思想是:
先对序列进行分组,每次寻求一个gap:例如:
2 1 3 5 9 1 6 8
length = 8
gap = length / 2 = 4
就分成4组: 然后对每一组进行插入排序。
例如:
2 1 3 5 9 1 6 8
然后对每一组进行插入排序:
2 9 --- 插入排序
1 1 --- 插入排序
3 6 --- 插入排序
5 8 --- 插入排序
2 1 3 5 9 1 6 8
虽然每一组字面上来说,根本不需要排序。
接着,gap = gap / 2,如此继续执行上述操作。
这样做,可以减少插入排序的次数。
算法复杂度和稳定性:
希尔排序的时间复杂度是:
O
(
n
l
o
g
n
)
O
(
n
2
)
O(nlogn) ~ O(n^2)
O(nlogn) O(n2) 最好情况:
O
(
n
1
−
3
)
O(n^{1-3)}
O(n1−3) 最坏情况:
O
(
n
2
)
O(n^2)
O(n2) 不需要占用多余的空间,空间复杂度O(1);是不稳定排序。
python版本的实现:
def shell_sort(self, list):
"""
希尔排序,希尔排序基于插入排序,不过是先对序列进行分组
然后对分组执行插入排序
:return: 插入排序的结果
"""
n = len(list)
gap = n // 2
while gap >= 1:
## 插入排序
for i in range(0, n - gap):
## 让间隔gap的元素为一组
j = i + gap
while j > 0:
if list[j] < list[j - gap]:
(list[j - gap], list[j]) = (list[j], list[j - gap])
j -= gap
else:
break
## gap折半,直至1
gap = gap // 2
## 返回结果
return list
2. 快速排序
快速排序的思想是一种基于分治的思想:
先找一个枢点,把序列分成两部分,一部分全都不小于该枢点,一部分全都大于该枢点。
然后对这两部分再进行快排
。
这个过程中,就采用了分而治之思想,化复杂为简单。
快排的时间复杂度是: O ( n l o g n ) O(nlogn) O(nlogn) 他的排序过程,就不再演示了。这里直接给出算法复杂度分析:
算法复杂度和稳定性:
希尔排序的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn) 最好情况, O ( n l o g n ) O(nlogn) O(nlogn) 最坏情况: O ( n 2 ) O(n^2) O(n2) 不需要占用多余的空间,空间复杂度O(1);是不稳定排序。
python版本的代码实现:
def quick_sort(self, list, first, end):
"""
快速排序 基于分治的思想,找到一个值,把比这个元素小的,分成左边部分;
把比这个元素大的,分成右边部分。
接着对左边部分和右边部分,分别进行快速排序
:param list:
:param first:
:param end:
:return:
"""
low = first
high = end
pivot = list[low]
if low >= high:
return
while low < high:
while low < high and list[high] >= pivot:
high -= 1
list[low] = list[high]
while low < high and list[low] < pivot:
low += 1
list[high] = list[low]
## 此时,low == high, 把elment放在low指向的元素
list[low] = pivot
print(list)
self.quick_sort(list, first, low - 1)
self.quick_sort(list, low + 1, end)
3.归并排序
归并排序的思想是:先拆分序列,直到不能拆分的单个元素;然后把每个元素当作一个序列,他们分别都是有序的。那么两两合并,非常容易,因为是有序序列:
例如:5 8 1 2,拆分成4个数组: [5], [8] ,[1], [2]。接着两两合并成:
[5 8]
[1 2]
接着,就复杂一点,两个索引:up, down,分别指向 5, 1:
up
|
5 8
down
|
1 2
合并结果: 【1】
down
|
5 8
up
|
2
结果: 【1,2】
把剩余5和8放入数组,结果:【1,2,5,8】
算法复杂度和稳定性:
希尔排序的平均时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 最好情况,
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 最坏情况:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 需要占用最多长度为n的空间,空间复杂度
O
(
n
)
O(n)
O(n)
希尔排序是稳定排序。
python版本的代码实现:
def merge_sort(self, list):
"""
归并排序的实现
:param self:
:param list:
:return:
"""
n = len(list)
if n == 1:
return list
mid = n // 2
print("mid:", mid)
left = self.merge_sort(list[:mid])
right = self.merge_sort(list[mid:])
print("left list:", left)
print("right list:", right)
result = self.merge(left, right)
return result
def merge(self, left_list, right_list):
"""
合并两个列表
:param right_list:
:return:
"""
list = []
left = 0
right = 0
left_len = len(left_list)
right_len = len(right_list)
print("merge, left len:", left_len, "right_len:", right_len)
while left < left_len and right < right_len:
if left_list[left] < right_list[right]:
list.append(left_list[left])
left += 1
else:
list.append(right_list[right])
right += 1
## 把剩余的元素移动到list中
if left < left_len:
while left < left_len:
list.append(left_list[left])
left += 1
if right < right_len:
while right < right_len:
list.append(right_list[right])
right += 1
return list
总结
各种算法的分析和总结:
排序算法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O(n) | O ( n 2 ) O(n^2) O(n2) | O(1) | 稳定 |
选择排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O(1) | 不稳定 |
插入排序 | O ( n 2 ) O(n^2) O(n2) | O(n) | O ( n 2 ) O(n^2) O(n2) | O(1) | 稳定 |
希尔排序 | O(nlogn) ~ O ( n 2 ) O(n^2) O(n2) | O ( n 1 − 3 ) O(n^{1-3)} O(n1−3) | O ( n 2 ) O(n^2) O(n2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O ( n 2 ) O(n^2) O(n2) | O(nlogn) ~ O(n) | 不稳定 |