1. 插入排序 Insertion Sort
插入排序的工作原理是通过构建有序序列,对于未排序的序列,在已排序的序列中从后向前扫描,找到相应的位置并插入。
算法实现:
- 从第一个元素开始,该元素可以认为已经被排序
- 取出下一个元素,在已经排序的元素序列中从后向前扫描
- 如果已排序的元素大于新元素,将已排序元素移动到下一位置
- 重复步骤三,直到找到已排序的元素小于或者等于新元素的位置
- 将新元素插入该位置后
- 重复步骤2~5
def insert_sort(list):
n = len(list)
for i in range(1,n):
temp = list[i]
j = i - 1
while j >= 0 and temp < list[j]:
list[j+1] = list[j]
j -= 1
list[j+1] = temp
return list
2. 冒泡排序 Bubble Sort
冒泡排序重复地走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,
算法实现:
- 比较相邻的元素,如果第一个比第二个大,就交换它们
- 对每一对相邻元素作同样的工作。
def bubble_sorted(list_):
new_list = list(list_)
list_len = len(new_list)
for i in range(list_len - 1):
for j in range(list_len - 1, i, -1):
if new_list[j] < new_list[j - 1]:
new_list[j], new_list[j - 1] = new_list[j - 1], new_list[j]
return new_list
3. 归并排序 Merge Sort
归并排序的基本思想在于把两个已经排序的序列合并成一个序列。其大概可以分为两个步骤:
- 分割: 递归地把当前序列平均分割成两半
- 集成: 在保持元素顺序的同时将子序列归并。
在归并操作中,有两种不同的方法,分别为递归法(Top-Down)和迭代法(Bottom-Up),在此我们主要讨论迭代法。其基本过程如下:
- 申请空间,使其大小为两个已排序子序列之和;
- 设定两个指针,最初位置分别为两个已经排序子序列的起始位置;
- 比较两个指针所指向的元素,选择较小的放入到合并空间,并将指针移动到下一位之;
- 重复步骤3,直到某一指针到达序列末尾
- 将另一列所剩下的元素直接复制到合并序列末尾
算法实现
def merge(left, right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0)) #pop取出元素,并删除序列对应的元素
else:
result.append(right.pop(0))
if left:
result += left
if right:
result += right
# print("result",result) #推荐利用print函数查看输出,进一步理解递归
return result
def merge_sort(L):
if len(L) <= 1:
# When only 1 element, just return it
return L
mid = len(L) // 2
left = L[:mid]
right = L[mid:]
left = merge_sort(left)
right = merge_sort(right)
# conquer sub-problem recursively
return merge(left, right)
# return the answer of sub-problem
>>> test = [8,7,6,5,4,3,2,1]
>>> merge_sort(test)
4. 快速排序 Quick Sort
对于该算法的理解,推荐各位读者参考:https://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html
快速排序的基本思想在于,对于待排序序列中的某一个基准数,找到一个位置k
,并将基准数移动至位置k
.使得以k
为分界点,左边的数都小于这个基准数。下面以序列6 1 2 7 9 3 4 5 10 8
为例,简单说明快速排序的基本原理。
- 首先为了简便起见,我们选择
6
作为我们的基准数,并初始化两个指针分别指向序列起始和末尾位置。为了便于描述,分别命名为指针i
和指针j
。此时指针i
指向6,指针j
指向8。 指针j
首先开始移动,直到遇到第一个小于基准数的元素停下,此时其指向了元素5
。然后指针i
开始自左向右移动,并遇到了第一个大于基准的元素停下,此时其指向了元素7
。- 交换两个指针对应的元素。此时的序列为
6 1 2 5 9 3 4 7 10 8
. - 重复步骤
2~3
,直到两个指针相遇,说明该轮探索结束,交换指针元素和基准数,此时序列为3 1 2 5 4 6 9 7 10 8
。至此,基准元素6的左侧均比6小,右侧均比6大。 - 将旧基准元素的两侧视为新的子序列,重复上述过程。
该例子可以用下图表达:
算法实现
上述例子可以被抽象为:
- 挑选基准值(pivot);
- 分割:重新排序序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面。在这个分割结束后,对基准值的排序结束。
- 递归排序子序列。
需要注意的是,上述算法实现过程主要用了分治法(divide and conquer)的思想,需要 Ω ( n ) \Omega(n) Ω(n)的额外存储空间。所以现在又有原地分割(In-place)的方法对此加以改进。在原地分割中,在移动元素的过程中,基准元素最后的摆放位置也被寻找。故而节省了一定的额外存储空间,其具体代码实现如下:
def quick_sort(lst):
lst = lst[:]
n = len(lst)
def partition(lst,start,end):
i = start - 1
pivotIndex = end
pivot = lst[end]
for j in range(start,end):
if lst[j] < pivot:
i = i + 1
if i != j:
lst[i], lst[j] = lst[j], lst[i]
lst[i+1],lst[pivotIndex] = lst[pivotIndex],lst[i+1]
#print("in part",lst,i+1)
return i+1
def sort(lst,start,end):
if start >= end:
return
p = partition(lst,start,end)
sort(lst,start,p-1)
sort(lst,p+1,end)
#print("in sort",lst)
sort(lst,0,n-1)
return lst