赞
踩
排序过程:
def BubbleSort(a):
if len(a) == 0:
return None
for i in range(len(a) - 1):
exchange = False # 标志位,第i趟如果没有发生交换,则排序已经完成,不需要再进行后面的冒泡
for j in range(len(a) - i - 1):
if a[j] > a[j + 1]:
a[j], a[j + 1] = a[j + 1], a[j]
exchange = True
if exchange is False: # 没有发生交换,返回
return
复杂度分析:
排序过程:
def SelectionSort(a):
if len(a) == 0:
return None
for i in range(len(a) - 1):
min_index = i # 记录无序区最小数的位置
for j in range(i, len(a)):
if a[j] < a[min_index]:
min_index = j
if min_index != i:
a[i], a[min_index] = a[min_index], a[i]
复杂度分析:
排序过程:
def InsertionSort(a):
if len(a) == 0:
return None
for i in range(1, len(a)):
temp = a[i] # 取无序区的第一个数
j = i - 1 # 有序区的倒数第一个数索引为i-1
while j >= 0 and temp < a[j]: # 遍历有序区
a[j + 1] = a[j]
j -= 1
a[j + 1] = temp # 找到temp的位置
复杂度分析:
排序过程:
def partition(a, left, right): if len(a) == 0: return None temp = a[left] while left < right: # 从右边找比temp小的数 while left < right and a[right] >= temp: right -= 1 a[left] = a[right] # 从左边找比temp大的数 while left < right and a[left] <= temp: left += 1 a[right] = a[left] # 找到了temp的位置 a[left] = temp # 返回temp1的位置, return right也可以,因为left和right重合 return left def QuickSort(a, left, right): if left < right: # 选取a的第一个元素P,归位 mid = partition(a, left, right) # 递归P的左边 QuickSort(a, left, mid - 1) # 递归P的右边 QuickSort(a, mid + 1, right)
复杂度分析:
堆是一种特殊的完全二叉树。大根堆: 完全二叉树的任一节点都比其孩子节点大。小根堆: 完全二叉树的任一节点都比其孩子节点小。
堆的向下调整: 假设根节点的左右子树都是堆,但根节点不满足堆的性质,可以通过一次向下调整将其变成一个堆。
排序过程:
def adjustHeap(a, low, high): i = low # 指向根节点 j = 2 * i + 1 # 指向根节点的左孩子 temp = a[i] while j <= high: # 如果有右孩子,并且右孩子比左孩子大 if j + 1 <= high and a[j+1] > a[j]: j += 1 # 指向右孩子 # 如果子节点比temp大,交换a[i]和a[j] if a[j] > temp: a[i] = a[j] i = j j = 2 * i + 1 else: break a[i] = temp def HeapSort(a): if len(a) == 0: return None n = len(a) # 从底向上地建立大根堆,从最后一个非叶子节点开始,即(n-2)//2 for i in range((n - 2) // 2, -1, -1): adjustHeap(a, i, n-1) # 从最后一个元素开始,与堆顶调换 for i in range(n-1, -1, -1): a[0], a[i] = a[i], a[0] # 堆顶与a[i]调换 adjustHeap(a, 0, i-1)
复杂度分析:
python里堆的内置模块:heapq
# 堆的内置模块
import heapq
a = list(range(100))
random.shuffle(a)
heapq.heapify(a) # 建堆
# 依次出数
for i in range(len(a)):
print(heapq.heappop(a), end=',')
top-k问题: 有n个数,取前k大的数(k<n)
# 调整小根堆 def adjustHeap(a, low, high): i = low # 指向根节点 j = 2 * i + 1 # 指向根节点的左孩子 temp = a[i] while j <= high: # 如果有右孩子,并且右孩子比左孩子小 if j + 1 <= high and a[j+1] < a[j]: j += 1 # 指向右孩子 # 如果子节点比temp小,交换a[i]和a[j] if a[j] < temp: a[i] = a[j] i = j j = 2 * i + 1 else: break a[i] = temp def top_k(a, k): if len(a) == 0: return None heap = a[:k] # 建堆 for i in range((k - 2) // 2, -1, -1): adjustHeap(heap, i, k-1) # 遍历 for i in range(k, len(a) - 1): if a[i] > heap[0]: heap[0] = a[i] adjustHeap(heap, 0, k-1) # 出数 for i in range(k - 1, -1, -1): heap[0], heap[i] = heap[i], heap[0] adjustHeap(heap, 0, i - 1) return heap
归并: 将两段有序列表合并成一个有序列表
排序过程:
def merge(a, low, mid, high): # a的左右半边子序列已为有序 i, j = low, mid + 1 temp = [] # 依次比较 while i <= mid and j <= high: # 只要两个子序列还有数 if a[i] < a[j]: temp.append(a[i]) i += 1 else: temp.append(a[j]) j += 1 # 上面循环结束后至少有一个子序列已被完全遍历 # 下面将可能剩下的部分添加到temp temp.extend(a[i:mid+1]) temp.extend(a[j:high+1]) #print(len(a[i:mid+1]), len(a[j:high]), len(a[low:high+1]), len(temp)) # 替换原数组 a[low:high+1] = temp def MergeSort(a, low, high): if len(a) == 0: return None if low < high: # low为a的最左边,high为a的最右边,mid为左半边子序列的最右边 mid = (low + high) // 2 # 归并左半边子序列 MergeSort(a, low, mid) # 归并右半边子序列 MergeSort(a, mid+1, high) # 合并两个有序子序列 merge(a, low, mid, high)
复杂度分析:
希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破 O ( n 2 ) O(n^2) O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序。
排序过程:
def InsertSortWithGap(a, gap): if len(a) == 0: return None for i in range(1, len(a)): temp = a[i] # 取无序区的第一个数 j = i - gap # 有序区的倒数第一个数索引为i-gap while j >= 0 and temp < a[j]: # 遍历有序区 a[j + gap] = a[j] j -= gap a[j + gap] = temp # 找到temp的位置 def ShellSort(a): if len(a) == 0: return None d = len(a) // 2 while d >= 1: InsertSortWithGap(a, d) d //= 2
复杂度分析: 希尔排序的复杂度分析是个难题,根据选取的分组整数序列 d 1 , d 2 , ⋯ , d i d_1, d_2, \cdots, d_i d1,d2,⋯,di的不同,其时间复杂度也不同,有的复杂度由于数学上的难题仍然没有定论。对于我们介绍的一直除2的这种方式:
基数排序要求已知列表中数的范围在0到 k k k之间
排序过程:
def CountingSort(a): if len(a) == 0: return None # 找最大最小值 min_, max_ = a[0], a[0] for num in a: if num > max_: max_ = num if num < min_: min_ = num # 初始化计数数组 C = [0] * (max_ - min_ + 1) # 遍历数组 for num in a: C[num] += 1 # 填充原数组 a.clear() for i, c in enumerate(C): for j in range(c): a.append(i)
复杂度分析:
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序
排序过程:
def BucketSort(a, bins): if len(a) == 0: return None # 初始化桶 buckets = [[] for _ in range(bins)] # 找到最大值最小值 min_, max_ = a[0], a[0] for num in a: if num > max_: max_ = num if num < min_: min_ = num # 遍历 for num in a: i = min(num // (max_ // bins), bins-1) # 第几号桶 buckets[i].append(num) # 保持桶内顺序 for j in range(len(buckets[i]) - 1, 0,-1): if buckets[i][j] < buckets[i][j - 1]: # 桶内下小上大 buckets[i][j], buckets[i][j - 1] = buckets[i][j - 1], buckets[i][j] else: break # 从桶内拿出 sorted_a = [] for bucket in buckets: sorted_a.extend(bucket) return sorted_a
复杂度分析:
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
排序过程:
def RadixSort(a): if len(a) == 0: return None max_ = max(a) it = 0 while 10 ** it <= max_: # 按倒数第it位分桶 buckets = [[] for _ in range(10)] for num in a: digit = (num // 10 ** it) % 10 # 取倒数第it位数 buckets[digit].append(num) it += 1 # 出桶 a.clear() for bucket in buckets: a.extend(bucket)
复杂度分析:
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定 |
---|---|---|---|---|---|---|
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O(n) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | in-place | 稳定 |
选择排序 | O ( n 2 ) O(n^2) O(n2) | O(n^2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | in-place | 不稳定 |
插入排序 | O ( n 2 ) O(n^2) O(n2) | O(n) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | in-place | 稳定 |
快速排序 | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( n 2 ) O(n^2) O(n2) | O ( log n ) O(\log n) O(logn) | in-place | 不稳定 |
堆排序 | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( 1 ) O(1) O(1) | in-place | 不稳定 |
归并排序 | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( n log n ) O(n \log n) O(nlogn) | O ( n ) O(n) O(n) | out-place | 稳定 |
希尔排序 | O ( n log n ) O(n \log n) O(nlogn) | O ( n log 2 n ) O(n \log^2 n) O(nlog2n) | O ( n log 2 n ) O(n \log^2 n) O(nlog2n) | O ( 1 ) O(1) O(1) | in-place | 不稳定 |
计数排序 | O ( n + k ) O(n+k) O(n+k) | O(n+k) | O ( n + k ) O(n+k) O(n+k) | O ( k ) O(k) O(k) | out-place | 稳定 |
桶排序 | O ( n + k ) O(n+k) O(n+k) | O(n+k) | O ( n 2 ) O(n^2) O(n2) | O ( n + k ) O(n+k) O(n+k) | out-place | 稳定 |
基数排序 | O ( n × k ) O(n \times k) O(n×k) | O ( n × k ) O(n \times k) O(n×k) | O ( n × k ) O(n \times k) O(n×k) | O ( n + k ) O(n+k) O(n+k) | out-place | 稳定 |
上表中,in-place表示占用常数内存,不占额外内存(递归内存除外),out-place表示占用额外内存; k k k为桶的个数;稳定表示如果a原本在b前面且a==b,排序后a仍然在b前面,不稳定则是a可能出现在b后面。
[1] https://blog.csdn.net/weixin_41190227/article/details/86600821
[2] https://edu.csdn.net/course/detail/24449
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。