程序员必须掌握的十大排序算法「下」

1. 内部排序和外部排序

排序算法可以分为内部排序和外部排序两种。内部排序指的是排序的数据量很小,全部可以加载到内存里面进行排序。外部排序则指的是排序的数据量太大,无法一次性全部加载到内存中进行排序,需要借助外部存储器(如硬盘)来排序。

1.1 内部排序

内部排序算法基于数组的随机访问特性,按顺序处理排序数据的元素,实现排序。常见的内部排序算法包括:

冒泡排序(Bubble Sort)

选择排序(Selection Sort)

插入排序(Insertion Sort)

希尔排序(Shell Sort)

归并排序(Merge Sort)

快速排序(Quick Sort)

堆排序(Heap Sort)

计数排序(Counting Sort)

桶排序(Bucket Sort)

基数排序(Radix Sort)

1.2 外部排序

外部排序算法基于磁盘文件的顺序读写特性,将大量数据分成若干块,按顺序处理每个块,再对块间进行归并排序,最终实现排序。常见的外部排序算法包括:

多路归并排序(Multiway Merge Sort)

置换选择排序(Replacement Selection Sort)

2. 常用排序算法

以下列举了一些常用的排序算法,按照算法复杂度递增的顺序,依次为:

2.1 冒泡排序(Bubble Sort)

冒泡排序的基本思想是,从头到尾依次比较相邻两个元素的大小,若前者大于后者,则交换其位置,直到序列排好为止。

def bubble_sort(arr):

n = len(arr)

for i in range(n - 1):

for j in range(n - i - 1):

if arr[j] > arr[j + 1]:

arr[j], arr[j + 1] = arr[j + 1], arr[j]

return arr

冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1),是一种稳定的排序算法。

2.2 选择排序(Selection Sort)

选择排序的基本思想是,从未排序的序列中选择最小的元素,将其放在已排序序列的末尾。不断重复该操作,直到整个序列排好为止。

def selection_sort(arr):

n = len(arr)

for i in range(n - 1):

min_idx = i

for j in range(i + 1, n):

if arr[j] < arr[min_idx]:

min_idx = j

arr[i], arr[min_idx] = arr[min_idx], arr[i]

return arr

选择排序的时间复杂度为O(n^2),空间复杂度为O(1),是一种不稳定的排序算法。

2.3 插入排序(Insertion Sort)

插入排序的基本思想是,将待排序元素插入到已排序序列的恰当位置(从后往前依次比较和交换),不断重复该操作,直到整个序列排好为止。

def insertion_sort(arr):

n = len(arr)

for i in range(1, n):

j = i

while j > 0 and arr[j] < arr[j - 1]:

arr[j], arr[j - 1] = arr[j - 1], arr[j]

j -= 1

return arr

插入排序的时间复杂度为O(n^2),空间复杂度为O(1),是一种稳定的排序算法。

2.4 希尔排序(Shell Sort)

希尔排序的基本思想是,采用分组策略,先进行简单排序,然后逐渐缩小组的范围,不断重复该过程,最终进行全局排序。比较复杂,但能够显著提高时间效率。

def shell_sort(arr):

n = len(arr)

gap = n // 2

while gap > 0:

for i in range(gap, n):

temp = arr[i]

j = i

while j >= gap and arr[j - gap] > temp:

arr[j] = arr[j - gap]

j -= gap

arr[j] = temp

gap //= 2

return arr

希尔排序的时间复杂度为O(n^1.3),空间复杂度为O(1),是一种不稳定的排序算法。

2.5 归并排序(Merge Sort)

归并排序的基本思想是,将待排序序列分为若干个子序列,对每个子序列进行归并排序,将已排序的子序列合并成完整的有序序列。

def merge_sort(arr):

if len(arr) > 1:

mid = len(arr) // 2

left_arr = arr[:mid]

right_arr = arr[mid:]

merge_sort(left_arr)

merge_sort(right_arr)

i = j = k = 0

while i < len(left_arr) and j < len(right_arr):

if left_arr[i] < right_arr[j]:

arr[k] = left_arr[i]

i += 1

else:

arr[k] = right_arr[j]

j += 1

k += 1

while i < len(left_arr):

arr[k] = left_arr[i]

i += 1

k += 1

while j < len(right_arr):

arr[k] = right_arr[j]

j += 1

k += 1

return arr

归并排序的时间复杂度为O(nlogn),空间复杂度为O(n),是一种稳定的排序算法。

2.6 快速排序(Quick Sort)

快速排序的基本思想是,首先选择一个元素作为基准值,然后将比基准值小的元素移动到基准值左边,比基准值大的元素移动到基准值右边,最后以基准值为分界线将数组分成两半,不断重复该过程,最终实现排序。

def quick_sort(arr):

if len(arr) <= 1:

return arr

pivot = arr.pop()

left_arr = []

right_arr = []

for x in arr:

if x <= pivot:

left_arr.append(x)

else:

right_arr.append(x)

return quick_sort(left_arr) + [pivot] + quick_sort(right_arr)

快速排序的时间复杂度为O(nlogn),空间复杂度为O(logn),是一种不稳定的排序算法。

2.7 堆排序(Heap Sort)

堆排序的基本思想是,构建一个大根堆或小根堆,每次取出堆顶元素(即最大值或最小值),再重新调整堆的结构,直到整个序列排好为止。

def heap_sort(arr):

def build_max_heap(arr):

for i in range(len(arr) // 2, -1, -1):

heapify(arr, i, len(arr))

def heapify(arr, i, size):

left = i * 2 + 1

right = i * 2 + 2

max_index = i

if left < size and arr[left] > arr[max_index]:

max_index = left

if right < size and arr[right] > arr[max_index]:

max_index = right

if max_index != i:

arr[i], arr[max_index] = arr[max_index], arr[i]

heapify(arr, max_index, size)

build_max_heap(arr)

for i in range(len(arr) - 1, 0, -1):

arr[0], arr[i] = arr[i], arr[0]

heapify(arr, 0, i)

return arr

堆排序的时间复杂度为O(nlogn),空间复杂度为O(1),是一种不稳定的排序算法。

2.8 计数排序(Counting Sort)

计数排序的基本思想是,统计序列中每个元素出现的次数,然后从小到大依次输出每个元素。

def counting_sort(arr):

n = len(arr)

max_val = max(arr)

count = [0] * (max_val + 1)

for i in range(n):

count[arr[i]] += 1

for i in range(1, len(count)):

count[i] += count[i - 1]

res = [0] * n

for i in range(n - 1, -1, -1):

res[count[arr[i]] - 1] = arr[i]

count[arr[i]] -= 1

return res

计数排序的时间复杂度为O(n + k),空间复杂度为O(k),是一种稳定的排序算法,其中k为桶的数量,取决于序列中数据的范围。

2.9 桶排序(Bucket Sort)

桶排序的基本思想是,将序列中的元素划分到不同的桶中,对每个桶中的元素进行排序,最后将所有桶中的元素依次输出。

def bucket_sort(arr):

n = len(arr)

max_val, min_val = max(arr), min(arr)

bucket_size = (max_val - min_val) // n + 1

bucket = [[] for _ in range(bucket_size)]

for i in range(n):

idx = (arr[i] - min_val) // bucket_size

bucket[idx].append(arr[i])

res = []

for i in range(bucket_size):

if bucket[i]:

bucket[i].sort()

res += bucket[i]

return res

桶排序的时间复杂度为O(n + k),空间复杂度为O(n + k),是一种稳定的排序算法。

2.10 基数排序(Radix Sort)

基数排序的基本思想是,从低位到高位逐个排序,根据每个元素对于某一位的数字进行分类和排序,最终得到一个有序序列。

def radix_sort(arr):

def counting_sort_for_radix(arr, radix):

n = len(arr)

count = [0] * 10

for i in range(n):

idx = arr[i] // radix % 10

count[idx] += 1

for i in range(1, len(count)):

count[i] += count[i - 1]

res = [0] * n

for i in range(n - 1, -1, -1):

idx = arr[i] // radix % 10

res[count[idx] - 1] = arr[i]

count[idx] -= 1

return res

max_val = max(arr)

radix = 1

while radix <= max_val:

arr = counting_sort_for_radix(arr, radix)

radix *= 10

return arr

基数排序的时间复杂度为O(d * (n + k)),空间复杂度为O(n + k),是一种稳定的排序算法,其中d为数字位数,k为桶的数量。

3. 总结

排序算法是算法中最基础、最基本的算法之一,也是每个程序员必须掌握的核心算法之一。不同的排序算法有着不同的优劣势,需要根据具体情况灵活选择。在实际应用中,我们有时需要根据数据规模、数据分布等不同情况进行选择。比如,对于小数据集,可以选择冒泡排序、插入排序等交换排序或插入排序算法。对于大数据集,可以选择快速排序、堆排序等分治排序或选择排序算法。对于数据分布均匀的情况,可以选择计数排序、桶排序等线性排序算法。对于数字较大的情况,可以选择基数排序等排序算法。

要想成为一名优秀的程序员,首先需要掌握算法和数据结构,其次需要不断练习和思考。在实际应用中,我们需要深入了解各种排序算法的原理、特点和应用场景,了解算法复杂度的概念,并在实际实现中注意一些常见的问题,如空间复杂度、递归调用等。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签