python 算法

44 阅读4分钟
############################# 快速排序 #############################
import random,time,sys
sys.setrecursionlimit(10000)

def partition(li,left,right):  # 自定义一个分隔的函数,将列表分割成左右变更个区域
    '''以第一个元素为分割值,将小于的放左边,大于的放右边'''
    # val = li[left]  # 如果最坏情况出现,直接程序崩溃
    val = li[random.randint(0,len(li)-1)]
    while left < right:
        while left < right and li[right] >= val:     # 首先取出的是第一个元素,顾第一个位置有空位,那么就需要从最右边开始比较
            right -= 1                               # 如果满足上面的条件,就往左走一步
        li[left] = li[right]                         # 如果跳出上面这个循环 就执行当前这个步骤,此时列表会出现两个比val小的数值。把右边的值写道左边空位
        while left < right and li[left] <= val:
            left += 1
        li[right] = li[left]                            # 把左边的值写进右边的空位
    li[left] = val                                  # 最后在 left 和 right 交界处 放上最开始取到的 val 值
    return left                                      # 返回切割值得索引

def quick_sort(li,left,right):
    '''
    快速排序,用到了递归方法
    次方法 会重复得调用 partition 这个函数,每次返回的 left 这个索引都不同
    间接的 再次调用本身,那么此时的列表也会足渐缩小
    也就是 递归方法
    '''
    if left < right:  # 这个条件可以确定,至少会有两个元素
        mid = partition(li,left,right)
        quick_sort(li,left,mid-1)
        quick_sort(li,mid+1,right)

'''
- 快速排序  时间复杂度:O(nlogn)
            - 问题:
                - 递归: 最大深度为 99 ,而且会消耗一定的系统资源
                    - 修改最大深度方法: import sys
                                        sys.setrecursionlimit(1000000)  想递归多少层,就在括号内输入对应数字
                - 最坏情况:时间复杂度为 O(n**2) 但是此几率极低
                    - 还有一种就是 9 8 7 6 5 4 3 2 1 这种情况效率也会极低
                    - 尽可能的降低这种可能出现的情况,我们设置第一个值 val 为随机取值。但是还是有可能出现最坏情况
'''

############################# 珠排序 #############################
# 珠排序:只能对正int类型进行排序。
从小到大
ddlist = [1,33,6,33,4,2,12,6,7]
def xxx(res: list[int]):
    if any(not isinstance(x,int) or x < 0 for x in res):
        raise TypeError('参数中不是正int类型!')
    for _ in range(len(res)):
        for i,(left,right) in enumerate(zip(res,res[1:])):
            if left > right:
                res[i] -= left - right
                res[i+1] += left + right
    return res

print(xxx(ddlist))

def xxx2(res: list[int]):
    if any(not isinstance(x,int) or x < 0 for x in res):
        raise TypeError('参数中不是正int类型!')
    for _ in range(len(res)):
        for i,(left,right) in enumerate(zip(res,res[1:])):
            if left < right:
                res[i] += right - left
                res[i+1] -= right - left
    return res



############################# 双调排序 #############################
intlist = [-3,-4,23,-4,5,-54,75,34]

def sort_b(array,direction):
    '''
    direction 大于1 为 升序
    direction 下于1 为 降序
    '''
    if direction == 1:
        for _ in range(len(intlist)):
            for  i,(left,right) in enumerate(zip(array,array[1:])):
                if left > right:
                    array[i],array[i+1] = right,left
    else:
        for _ in range(len(intlist)):
            for i,(left,right) in enumerate(zip(array,array[1:])):
                if left < right:
                    array[i],array[i+1] = right,left
    return array

sss = sort_b(intlist,0)
print(sss)
'''
这个方法运行一次存在没有完全排序好的可能,可以多运行一次。
arr2 = [12, 34, 92, -23, 0, -121, -167, 145,1,2,3,4,5,6,7,8,-8,-7]
    print(sort_b(arr2,1))
    print(sort_b(arr2,1))
    [-167, -121, -23, 0, 1, 2, 3, 4, -8, -7, 5, 6, 7, 8, 12, 34, 92, 145]
	[-167, -121, -23, -8, -7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 34, 92, 145]
'''


############################# 博格排序 ############################# 
'''
这是 bogosort 算法的纯 Python 实现,
也称为排列排序、愚蠢排序、慢排序、霰弹枪排序或猴子排序。
Bogosort 生成随机排列,直到猜出正确的排列。
'''
# 垃圾排序,不适用元素多的情况
def bogosort(array):
    import random

    def is_sort(array):
        if len(array) < 2:
            return True
        for i in range(len(array) - 1):
            if array[i] > array[i+1]:
                return False
        return True

    while not is_sort(array):
        random.shuffle(array) # 打散序列
    return array
    
    
############################# 冒泡排序 ############################# 
# 冒泡排序
# 列表相邻的两个数,如果前面比后面大,则交换这两个数
# 一趟排序完成后,则无序区减少一个数,有序区增加一个数。 一个列表有n个值,那么就需要n-1趟,i代表第几趟
# 关键点:趟,无序区范围
# 加上标志位之后,在特殊情况下就不需要运行 n-1 趟了,变成最多需要 n-1 趟

'''
len(li)-i-1解析:列表li = [2,3,4,8,1,6,9,7,5]长度len(li)为9
                此时最先开始 2 和 3比较 2的索引为 0
                for i in range(len(li))  i 的值 依此 为 0,1,2,3,4,5,6,7,8
                假如第一个数字不是2,是9,那么9一定会排到最后一位,也就是说,当9在在比较 9-1 趟之后 一定会在最后的位置
                那么循环一趟是 n-1  ,而且 无序区减少一个数,有序区增加一个数,也就是说,运行完一趟,接下来的一趟就会减一次
                最后得出:n-i-1
'''
def bubble_sort(li,reverse=False):
    count = 0
    for i in range(len(li)-1):  # i 表示第几趟
        exchange = False   # 加一个标志位
        for j in range(len(li)-i-1): # 这个代表相邻的两个数字进行比较
            if not reverse:
                if li[j] > li[j+1]:
                    li[j] , li[j+1] = li[j+1] , li[j]  # 交叉复制, li[j] = li[j+1] , li[j+1] = li[j] 原序列里的值就会变动
                    exchange = True
                    count +=1
            else:
                if li[j] < li[j+1]:
                    li[j] , li[j+1] = li[j+1] , li[j]  # 交叉复制, li[j] = li[j+1] , li[j+1] = li[j] 原序列里的值就会变动
                    exchange = True
                    count += 1

        print(li)
        if not exchange:
            break
    return li
    
    
############################# 选择排序 #############################
# 这个不推荐  时间复杂度:O(n**2),而且占用内存,因为同时出现了两个列表
def select_sort_simple(li):
    '''简单的选择排序'''
    new_li = []
    for i in range(len(li)):
        val_min = min(li)
        new_li.append(val_min)
        li.remove(val_min)
    return new_li


# 方法二:
def select_sort(li):
    for i in range(len(li)-1):
        min_loc = i
        for j in range(i+1,len(li)):
            if li[min_loc] > li[j]:
                min_loc = j
        li[i] , li[min_loc] = li[min_loc] , li[i]
    return li
'''
1.一趟排序记录最小值,放到第一位
2.再一趟排序记录列表无序区的最小值,放到第二位
3....
4.算法关键:有序区和无序区,无序区最小数的位置
'''


############################# 归并排序 ############################# 
# python中的内置排序模块sorted就是 基于 归并排序实现的,使用的不是 归并排序

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

def merge(li,low,mid,high):
    '''
    归并排序,前提是原列表可以分成左右两个有序列表
    :param li: 原列表
    :param low: 开头的值的索引
    :param mid:  左边有序列表的最后一个值得索引
    :param high: 右边有序列表的最后一个值的索引
    :return:
    '''
    i= low
    j = mid +1
    lis = []
    while i <= mid and j <= high: # 保证左右两边都有数
        if li[i] > li[j] :
            lis.append(li[j])
            j += 1
        else:
            lis.append(li[i])
            i += 1
    # 上面的while 执行完之后,肯定有一边的有序列表没元素了
    while i <= mid :
        lis.append(li[i])
        i += 1
    while j <= high:
        lis.append(li[j])
        j+= 1
    li[low:high+1] = lis

# merge(li,0,4,8)
# print(li)

'''
归并排序----使用归并
分解:将列表越分越小,直至分成一个元素
终止条件:一个元素是有序的
合并:将两个有序列表归并,列表越来越大
'''

def merge_sort(li,low,high):
    '''
    归并排序
    li: 数组
    low:开始索引
    high:结束索引
    '''
    if low < high:
        mid = (low+high)//2 # 取列表中间值,将列表拆分成两个列表
        merge_sort(li,low,mid)  # 将左边的列表变成有序列表
        merge_sort(li,mid+1,high) # 将右边的列表变成有序列表
        # print('eee',li[low:mid+1])
        merge(li,low,mid,high)

la = list(range(10000))
import random,time
random.shuffle(la)
start = time.time()
merge_sort(la,0,len(la)-1)
end = time.time()
print(end - start)
print(la)