【python】Sort and Search


在这里插入图片描述
图片来源:十大经典排序算法(动图演示)

在这里插入图片描述
图片来源:十大经典排序算法(动图演示)

1 Sort

sort():对原始列表进行排序
sorted():返回一个新的排序列表

groceries = ['milk', 'bread', 'tea']
new_groceries = sorted(groceries)

# new_groceries = ['bread', 'milk', 'tea']
print(new_groceries)

# groceries = ['milk', 'bread', 'tea']
print(groceries)

groceries.sort()

# groceries = ['bread', 'milk', 'tea']
print(groceries)

配合 operator,来自 python中的operator.itemgetter函数

import operator
a = [1,2,3] 
fun_b = operator.itemgetter(1) # 定义函数 fun_b,获取对象的第1个域的值
fun_c = operator.itemgetter(2,1,0)
print(fun_b(a)) 
print(fun_c(a))

a = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
b = sorted(a, key=operator.itemgetter(1,2))
print(b)

1.1 冒泡排序

在这里插入图片描述
图片来自于:https://www.bilibili.com/video/av53583801/?p=35

在这里插入图片描述

动图来源:十大经典排序算法(动图演示)

思路(升序):
n-1 趟,每一趟确定一个数的最终位置,
每一趟两两元素(未确定最终位置)比较,大的往后放

nums = [3,5,6,7,8,9,2,1,4]

def bubble(nums):
    n = len(nums)
    for i in range(n-1): # 排序 n-1 趟,每一趟最少排好一个数
        count = 0 # 记录交换的次数
        for j in range(n-1-i): # 两个元素比较的次数
            if nums[j] > nums[j+1]: # 这里的符号改变一下就是逆序了
                nums[j],nums[j+1] = nums[j+1],nums[j]
                count+=1
        if count==0: # 某一趟中,没有元素交换,说明排序结束了,退出
            break
    return nums
    
print(bubble(nums))

output

[1, 2, 3, 4, 5, 6, 7, 8, 9]

注意:没有 count 的统计和判断,那么最好最坏的复杂度都是 O ( n 2 ) O(n^2) O(n2),有了以后最好的是 O ( n ) O(n) O(n)(原来的数组就有序),最坏的 O ( n 2 ) O(n^2) O(n2)(逆序)!算法是稳定的

1.2 选择排序

在这里插入图片描述
动图来源:十大经典排序算法(动图演示)

思路(升序):将数组分为两部分,取后面部分的最小值放在前面!每一趟确认前面部分的一个值!

nums = [3,5,6,7,8,9,2,1,4]

def select_sort(nums):
    n = len(nums)
    for i in range(n-1):
        min_index = i # 初始最小值下标为 i
        for j in range(i+1,n): # 遍历i+1到n,找最小值的下标
            if nums[j] < nums[min_index]:
                min_index = j
        nums[i],nums[min_index] = nums[min_index],nums[i] # 交换初始化最小值下标和比较后的最小值下标
    return nums
    
print(select_sort(nums))

分析:最坏最好都是 O ( n 2 ) O(n^2) O(n2),不稳定的,如果算法改为,把从前面部分选最大值放在后面部分,发现相同元素的位置调换了

eg 3(1) 3(2) 1 2
第一趟:3(2) 1 2 | 3(1)
第二趟:1 2 | 3(2) 3(1)
第三趟:1 | 2 3(2) 3(1)

可以看出算法是不稳定的

1.3 插入排序

在这里插入图片描述
动图来源:十大经典排序算法(动图演示)

同选择排序,也是将数组分为两个部分,从后面部分选择元素插入到第一部分中,插入的过程是逐个比较,比前部分小就交换位置!

nums = [3,5,6,7,8,9,2,1,4]

def insert_sort(nums):
    n = len(nums)
    for i in range(1,n):
        for j in reversed(range(1,i+1)): # 反向遍历 i 到 1
            if nums[j] < nums[j-1]: # 和前一个元素比较大小
                nums[j],nums[j-1] = nums[j-1], nums[j] #小的话交换位置
            else: # 大的话直接退出,保证了最优为 O(n)
                break
    return nums

print(insert_sort(nums))

最好 O ( n ) O(n) O(n),就是有序的时候都 break 了,最坏的是 O ( n 2 ) O(n^2) O(n2)(两层循环),平均的是 O ( n 2 ) O(n^2) O(n2),是稳定的!

1.4 希尔排序

在这里插入图片描述
动图来源:十大经典排序算法(动图演示)

插入排序的改进版,将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序(直接插入排序的步长变为了gap,而不是1了)!不同的分割策略会影响算法的时间复杂度,gap 为1的时候就是插入排序,eg 4,2,1。

nums = [3,5,6,7,8,9,2,1,4]

def shell_sort(nums):
    n = len(nums)
    gap = n//2
    while(gap>0):
        for i in range(gap,n):
            for j in reversed(range(1,i+1,gap)): # 反向遍历 i 到 1,步长变为了 gap
                if nums[j] < nums[j-1]: # 和前一个元素比较大小
                    nums[j],nums[j-1] = nums[j-1], nums[j] #小的话交换位置
                else: # 大的话直接退出,保证了最优为 O(n)
                    break
        gap //=2 # gap 的策略,这里是 1/2
    return nums

print(shell_sort(nums))

1.5 快排

介绍可以参考 python】Leetcode(Data Structure / Algorithm) 中的 215. 数组中的第K个最大元素(快排)

partion 函数,把小于 base 的放在左边,把大于base 的放在右边,然后递归下去!

nums = [3,5,6,7,8,9,2,1,4]

def quick_sort(nums,start,end):
    if start<end:
        base = partion(nums,start,end)
        quick_sort(nums,start,base-1)
        quick_sort(nums,base+1,end)
    return nums

def partion(nums,start,end):
    l = start
    r = end
    base = nums[l]
    while(l<r):
        while(l<r and nums[r]>=base):
            r-=1
        nums[l] = nums[r]
        while(l<r and nums[l]<=base):
            l+=1
        nums[r] = nums[l]
    nums[l] = base
    return l

print(quick_sort(nums,0,len(nums)-1))

1.6 归并排序

稳定的,最好最坏的平均的时间复杂度都是 O ( n l o g n ) O(nlogn) O(nlogn),但是会生成同样大小的数组,思想是,一直分下去(eg 2,4,8……),然后排序后合起来!排序的过程用到了双指针,一个指向二分左部分的,一个指向二分右部分的,比较大小移动指针来合并成一个排序的新数组!
在这里插入图片描述
图片来自于:https://www.bilibili.com/video/av53583801/?p=46

在这里插入图片描述
图片来自于:https://www.bilibili.com/video/av53583801/?p=35

nums = [3,5,6,7,8,9,2,1,4]

def merge_sort(nums):
    if len(nums)<=1: # 递归终止条件
        return nums

    mid = len(nums)//2
    l_list = merge_sort(nums[:mid]) # 分成左右两部分
    r_list = merge_sort(nums[mid:]) # 分成左右两部分

    result = [] # 存放排序的结果
    l,r = 0,0 # 用两个指针对左右两部分进行排序
    while(l<len(l_list) and r<len(r_list)): # 左右有一个到了尽头的时候退出
        if l_list[l]<=r_list[r]:
            result.append(l_list[l])
            l+=1
        else:
            result.append(r_list[r])
            r+=1
    result+=l_list[l:] # 把剩下的挂在result的后面
    result+=r_list[r:] # 把剩下的挂在result的后面
    return result # 返回排序后的结果

print(merge_sort(nums))

2 Search

二分查找
递归版本

nums = [1,2,3,4,5,6,7,8,9]

def binary_search(nums,item):
    if len(nums)>0: # 这个条件很关键
        mid = len(nums)//2
        if item == nums[mid]:
            return True
        elif item < nums[mid]:
            return binary_search(nums[:mid],item)
        elif item > nums[mid]:
            return binary_search(nums[mid+1:], item)
    return False

print(binary_search(nums,5))
print(binary_search(nums,10))

output

True
False

非递归版本

nums = [1,2,3,4,5,6,7,8,9]

def binary_search(nums,item):
    l = 0
    r = len(nums)-1
    while(l<=r):
        mid = (l+r) // 2
        if item == nums[mid]:
            return True
        elif item < nums[mid]:
            r = mid - 1
        elif item > nums[mid]:
            l = mid + 1
    return False

print(binary_search(nums,5))
print(binary_search(nums,10))

output

True
False

最优 O ( 1 ) O(1) O(1)
最坏 O ( l o g n ) O(logn) O(logn)


3 Insert

3.1 bisect

Python中bisect的使用方法

用 bisect.insort 插入新元素

排序很耗时,因此在得到一个有序序列之后,我们最好能够保持它的有序。bisect.insort就是为这个而存在的

insort(seq, item) 把变量 item 插入到序列 seq 中,并能保持 seq 的升序顺序

import random
from random import randint
import bisect
 
lst = []
SIZE = 10
random.seed(5)
for _ in range(SIZE):
    item = randint(1, SIZE)
    bisect.insort(lst, item)
    print('%2d ->' % item, lst)

在这里插入图片描述

A 附录

A.1 key 参数的应用

(1)根据字符串中出现的数字大小排序

直接排序的话,会按照字符串排序的规则,从左到右比 ASCII 大小,不符合我们的期望

s = ["f1", "e2", "d3", "c4", "b5", "a6"]
print(sorted(s))

output

['a6', 'b5', 'c4', 'd3', 'e2', 'f1']

此时,我们可以借助正则表达式(参考 【python】Regular Expression(13)),指定排序算法根据字符串中的数字来排序

import re
s = ["f1", "e2", "d3", "c4", "b5", "a6"]

def sort_key(s):
    if s:
        try:
            c = re.findall(r"\d+", s)[0]
        except:
            c = -1
    return int(c)

print(sorted(s, key=sort_key))

output

['f1', 'e2', 'd3', 'c4', 'b5', 'a6']

参考 python 根据字符串内数字排序


(2)根据列表最后一个元素的大小进行排序

boxes = [[1,3],[2,2],[3,1]]
boxes = sorted(boxes, key=lambda x:x[-1])
print(boxes)

output
在这里插入图片描述

A.2 中文排序

Python中实现读取与windows资源管理器中相同顺序文件,顺序排列

安装 natsort

from natsort import natsorted

用法同 sorted

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值