-
算法总结
01、算法分类
- 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
- 内部排序:所有排序操作都在内存中完成。
- 外部排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行。
一、冒泡排序
思想:两两比较相邻记录的关键字,如果反序则交换,直到没有反序记录为止。
时间复杂度O(n^2)
def sortDate(self, num):
length = len(num)
for i in range(0, length):
for j in range(0, length - i - 1):
if num[j] > num[j+1]:
num[j], num[j+1] = num[j+1], num[j]
return num
冒泡排序改进算法, 设置flag,当一轮比较中未发生交换动作,则说明后面的元素其实已经有序排列了。 对于比较规整的元素集合,可提高一定的排序效率。
def sortDate(self, num):
length = len(num)
i = 0
flag = True
while i < length and flag:
flag = False
for j in range(0, length - i - 1):
if num[j] > num[j + 1]:
num[j], num[j+1] = num[j+1], num[j]
flag = True
i += 1
return num
二、快速排序
思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后分别对这两部分继续进行排序,以达到整个记录集合的排序目的。时间复杂度O(nlog(n))。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
# -*- coding: utf-8 -*-
def partion(nums, left, right):
key = nums[left]
while left < right:
while left < right and nums[right] >= key:
right -= 1
if left < right:
nums[left], nums[right] = nums[right], nums[left]
else:
break
while left < right and nums[left] < key:
left += 1
if left < right:
nums[left], nums[right] = nums[right], nums[left]
else:
break
return right
def quick_sort_standord(nums, left, right):
if left < right:
key_index = partion(nums, left, right)
quick_sort_standord(nums, left, key_index - 1)
quick_sort_standord(nums, key_index + 1, right)
三、简单选择排序
思想:通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录进行交换。
def selectSort(nums):
for i in range(0, len(nums)):
minIndex = i
for j in range(i+1, len(nums)):
if nums[j] < nums[minIndex]:
minIndex = j
if nums[i] != nums[minIndex]:
nums[i], nums[minIndex] = nums[minIndex], nums[i]
四、插入排序(Insertion-Sort)
思想:插入排序的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
def insertSort(nums):
for i in range(1, len(nums)):
fg = i
tp = nums[i]
while fg > 0:
if nums[fg-1] < tp:
break
else:
nums[fg] = nums[fg-1]
fg -= 1
nums[fg] = tp
return nums
五、希尔排序
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
- 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
- 按增量序列个数k,对序列进行k 趟排序;
- 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
def shellSort(nums):
step = len(nums)/2
while step > 0:
for i in range(step, len(nums)):
while i >= step and nums[i] < nums[i - step]:
nums[i],nums[i-step] = nums[i-step], nums[i]
i -= step
step = step / 2
return nums
六、堆排序
堆排序思想:建立大根堆,把根节点和最后一个叶节点交换得到最后一个元素有序,然后调整剩余序列为大根堆,把根节点和倒数第二个元素交换得到最后两个元素有序,依次循环进行直到整个序列有序。
-*- coding: utf-8 -*-
from collections import deque
def heap_adjust(L, start, end):
#调整子树为大根堆
i = start
j = 2 * i
temp = L[start]
while j <= end:
if j < end and L[j] < L[j+1]:# 先比较两个子节点大小,选择最大的
j += 1
if L[j] > temp:
L[i] = L[j]
i = j
j = 2 * i
else:
break
L[i ] = temp
def heap_sort(L):
length = len(L) - 1
start = length / 2
for i in range(start):
#从length/2(最后一个父节点)向前一依次调整子树为大根堆,得到最终序列为大根堆
heap_adjust(L, start - i, length)
for i in range(length - 1):
#把堆顶元素和堆尾元素交换,然后把剩下的元素调整为大根堆
L[1], L[length - i] = L[length - i], L[1]
heap_adjust(L, 1, length - i - 1)
return [L[i] for i in range(1,len(L))]
L = deque([5, 9, 30, 8, 6, 10, 2, 4, 70])
L.appendleft(0)
print heap_sort(L)
七、归并排序
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
def merge(a, b):
#将两个有序列表合并成一个列表返回
c = []
i = j = 0
while i < len(a) and j < len(b):
if a[i] < b[j]:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
if i == len(a):
c += b[j:]
else:
c += a[i:]
return c
def merge_sort(nums):
if len(nums) <= 1:
return nums
mid = len(nums) / 2
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:])
return merge(left, right)