目录
1.1基础入门
1.1.1算法效率的衡量
时间复杂度和空间复杂度
from timeit import Timer
def test():
for i in range(400000):
pass
t=Timer('test()','from __main__ import test')
print(t.timeit(100),'second')
1.1.2排序算法图谱
1.1.3算法效率比较
2.1交换排序
2.1.1冒泡排序
1.算法描述
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
2.代码实现
python代码:
def bubbling_sort(lis):
start = time.time()
for i in range(len(lis)):
for j in range(len(lis)-i-1):
if lis[j]>lis[j+1]:
lis[j],lis[j+1]=lis[j+1],lis[j]
end = time.time()
return lis,end-start
java代码:
public class BullingSort {
public static void main(String[] args){
int[] arr={5,3,6,1,7,9,4,2}; // 创建一个数组
int minPos=0;
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
// 打印我们的数组
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");
}
}
}
2.1.2快速排序
1.算法描述
从数组中随机找到一个基准(也就是参考,一般为了方便起见,把第一个数作为基准),我们把大于基准的数据放在前面,把小于基准的数据放在基准数据的后面,排序完成,完成之后将基准前面的数据看成一个整体,基准后面的数据看成一个整体,通过递归的方式分别进行上述从操作.
2.代码实现
import random
import time
def product_random(n):
lis=[]
for i in range(n):
lis.append(random.randint(0,10000))
return lis
def quick_sort(alist, start=0, end=None):
s=time.time()
if start >= end:
return
mid = alist[start]
low = start
high = end
while low < high:
while low < high and alist[high] >= mid:
high -= 1
alist[low] = alist[high]
while low < high and alist[low] < mid:
low += 1
alist[high] = alist[low]
alist[low] = mid
quick_sort(alist, start, low-1)
quick_sort(alist, low+1, end)
e=time.time()
return alist,e-s
if __name__ == '__main__':
lis=product_random(100000)
print('原始数据:{}'.format(lis))
result,times=quick_sort(lis,0,len(lis)-1)
3.应用场景
时间复杂度和空间复杂度都是nlogn,不稳定;
3.1插入排序
3.1.1直接插入排序
1.算法描述
我们将原数组分成两份,前一份数组认为是有序的,后一份数组认为是无序的,我们不断从后一份数据的第一个数开始,与前一份数组中的数据依次进行比较,通过遍历找到合适的位置(将该数插入其中,该数组有序),大于该数的数据集体后移,然后插入其中.依次类推.
2.代码实现
import random
import time
def product_random(n):
lis=[]
for i in range(n):
lis.append(random.randint(0,10000))
return lis
"""
前面有序,后面无序,将后面的数据插入到前面有序的数据中
"""
def insertion_sort(lis):
start = time.time()
for i in range(len(lis)):
for j in range(i):
if lis[i]<lis[j]:
lis[i],lis[j]=lis[j],lis[i]
end = time.time()
return lis,end-start
if __name__ == '__main__':
lis=product_random(10)
print('原始数据:{}'.format(lis))
result,times=insertion_sort(lis)
print('排序后数据:', result)
# 5.558953523635864-10000
# 605.1861779689789-100000
print('花费时间:',times)
3.1.2希尔排序
1.算法描述
是插入排序的一种优化,假如我们有[3,6,2,8,9,1,4,5,7,0]这10个数,将这10个数分成3组,第一组下标为0-3,第二组4-7,第三组8-9;可以间隔(增量)为gap=4,第一次我们将下标为0,0+gap,0+gap*2数据进行排序,第二次将1,1+gap,1+gap*2进行排序,如果说后面的数据不够了,我们可以忽略不用管,第三次2,2+gap,这里的2+gap*2不存在,因此第三轮只有2和2+gap两个数.第四轮...完成之后;将间隔变为gap=3(gap=gap//2),按照上述的方法依次进行,直到间隔gap=1的时候停止.
2.代码实现
是插入排序的一种优化;
def shell_sort(alist):
n = len(alist)
# 初始步长
gap = n//2
while gap > 0:
# 按步长进行插入排序
for i in range(gap, n):
j = i
# 插入排序
while j>=gap and alist[j-gap] > alist[j]:
alist[j-gap], alist[j] = alist[j], alist[j-gap]
j -= gap
# 得到新的步长
gap = gap // 2
alist = [54,26,93,17,77,31,44,55,20]
shell_sort(alist)
print(alist)
3.应用场景
- 时间复杂度n**1.3
- 空间复杂度:1
- 稳定性:不稳定
4.1选择排序
4.1.1直接选择排序
1.算法描述
我们将一个数组分成两份,前一份[0,i],后一份[i+1,n],我们不断从后面拿出最大的数据依次放置到次大的前面的数据中;也可以看做是从后一个数组中得到最大值与后一个数中的第一个数据进行交换,交换完成之后,后一个数组长度减少1,前一个数组的长度增加1.
2.代码实现
python代码实现
import random
import time
def product_random(n):
lis=[]
for i in range(n):
lis.append(random.randint(0,100))
return lis
"""
遍历[i,n-i),选出最小值存放在i-1的位置
前面有序,后面无序,选出无序中的最大值与前面的数据进行交换,前面的第i个数与后面的第n-i-1个数进行位置的交换
"""
def selection_sort(lis):
start = time.time()
for i in range(len(lis)):
for j in range(i+1,len(lis)):
if lis[i]>lis[j]:
lis[i],lis[j]=lis[j],lis[i]
end = time.time()
return lis,end-start
if __name__ == '__main__':
lis=product_random(100000)
print('原始数据:{}'.format(lis))
result,times=selection_sort(lis)
print('排序后数据:', result)
# 3.8099334239959717-10000
# 414.0644586086273- 100000
print('花费时间:',times)
4.1.2堆排序
实现:
取出堆顶,得到最大值,将最末尾的元素移动到堆顶,一次类推.切记在交换完成之后我们还要检查交换之后的两个节点是否满足大顶堆
5.1归并排序
5.1.1归并排序
1.算法描述
将数组中的每个元素看成一个组,然后进行合并,例如元素0和1排序之后合并为一组,元素2和元素3排序后合并为一组,一次类推;第一次合并结束之后,将第一组中的第一个元素与第二组中的第1个元素进行比较,大的放前面小的就与另一个组中的另一个元素进行比较,依然后大的放前面小的继续和另一个组中的其他元素进行比较,直到另一个组中的元素个数为0,最后就将该组中剩下的元素放入合并后的组中,这时该组合并结束,其他依次类推.
2.代码实现
def merge_sort(alist):
if len(alist) <= 1:
return alist
# 二分分解
num = len(alist)/2
left = merge_sort(alist[:num])
right = merge_sort(alist[num:])
# 合并
return merge(left,right)
def merge(left, right):
'''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组'''
#left与right的下标指针
l, r = 0, 0
result = []
while l<len(left) and r<len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += left[l:]
result += right[r:]
return result
alist = [54,26,93,17,77,31,44,55,20]
sorted_alist = mergeSort(alist)
print(sorted_alist)
6.1其他排序
6.1.1桶排序
优点:
- 桶排序是稳定的
- 桶排序是常见排序里最快的一种,比快排还要快…大多数情况下
- 桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法
步骤
- 1. 设置桶的数量为5个空桶,找到最大值110,最小值7,每个桶的范围20.8=(110-7+1)/5 。
- 2. 遍历原始数据,以链表结构,放到对应的桶中。数字7,桶索引值为0,计算公式为floor((7 – 7) / 20.8), 数字36,桶索引值为1,计算公式floor((36 – 7) / 20.8)。
- 3. 当向同一个索引的桶,第二次插入数据时,判断桶中已存在的数字与新插入数字的大小,按照左到右,从小到大的顺序插入。如:索引为2的桶,在插入63时,桶中已存在4个数字56,59,60,65,则数字63,插入到65的左边。
- 4. 合并非空的桶,按从左到右的顺序合并0,1,2,3,4桶。
- 5. 得到桶排序的结构
6.1.2基数排序
1.排序思路
将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序
2.实例
问题描述:排序342 58 576 356
结果:58 342 356 576
为什么要从低位开始向高位排序?
如果要从高位排序, 那么次高位的排序会影响高位已经排好的大小关系. 在数学中, 数位越高,数位值对数的大小的影响就越大.从低位开始排序,就是对这种影响的排序. 数位按照影响力从低到高的顺序排序, 数位影响力相同则比较数位值.
为什么同一数位的排序子程序要使用稳定排序?
稳定排序的意思是指, 待排序相同元素之间的相对前后关系,在各次排序中不会改变.比如实例中具有十位数字5的两个数字58和356, 在十位排序之前356在58之前,在十位排序之后, 356依然在58之前.
稳定排序能保证,上一次的排序成果被保留,十位数的排序过程能保留个位数的排序成果,百位数的排序过程能保留十位数的排序成果.
6.1.3计数排序
小结:各种排序算法比较