几种搜索、排序算法的python实现

一些复杂度的实例

n对数阶 log2n log 2 ⁡ n 线性阶n平方阶 n2 n 2 指数阶 2n 2 n
100710010000超标
10001010001000000超标
10000002010000001000000000000严重超标

搜索算法

搜索最小值

def indexOfMin(lyst):
    '''返回最小项的索引'''
    minIndex=0
    currentIndex = 1
    while currentIndex<len(lyst):
        if lyst[currentIndex]<lyst[minIndex]:
            minIndex = currentIndex
        currentIndex +=1
    return minIndex        

这个算法的复杂度为O(n)

顺序搜索一个列表

def sequentialSearch(target,lyst):
    '''如果找到目标返回他在列表中的索引否则返回-1'''
    position = 0
    while position<len(lyst):
        if target == lyst[position]:
            return position
        position += 1
    return -1

顺序搜索的分析要考虑如下这三种情况:

  1. 在最坏的情况下,目标位于列表的末尾,或者根本不在列表中。那么,算法必须访问每一项,并且对大小为n的列表要执行n次迭代。因此顺序搜索的最坏情况的复杂度为O(n)
  2. 在最好的情况下,算法只进行了一次迭代就在第一个位置找到目标项,复杂度为O(1)
  3. 要确定平均情况,把在每一个可能的位置找到目标项所需要的迭代次数相加,并用总和除以n。因此算法执行了(n+1)/2次迭代。复杂度仍为O(n).

有序列表的二叉树搜索

# 假设列表升序排列
def binarySearch(target, sortedLyst):
    left = 0
    right = len(sortedLyst) - 1
    while left <= right:
        midpoint = (left + right) // 2
        if target == sortedLyst[midpoint]:
            return midpoint
        elif target < sortedLyst[midpoint]:
            right = midpoint - 1
        else:
            left = midpoint + 1
    return -1

二叉搜索的最坏情况的复杂度为O( log2n log 2 ⁡ n )

基本排序算法

选择排序

工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

def selectionSort(lyst):
    i = 0
    while i < len(lyst) - 1:
        minIndex = i
        j = i + 1
        while j < len(lyst):              # 寻找第i项以后的最小项与第i项交换
            if lyst[j] < lyst[minIndex]:
                minIndex = j
            j += 1
        if minIndex != i:
            lyst[i], lyst[minIndex] = lyst[minIndex], lyst[i]
        i += 1

选择排序的复杂度的为O( n2 n 2 )

冒泡排序

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
def bubbleSort(lyst):
    n = len(lyst)
    while n > 1:
        swapped = False  #记录每轮循环是否一次都没有交换过数据(即已经排好序了)以便尽早退出循环。
        i = 1
        while i < n:
            if lyst[i] < lyst[i - 1]:   # 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
                lyst[i], lyst[i - 1] = lyst[i - 1], lyst[i]
                swapped = True
            i +=1
        if not swapped:
            return None
        n -=1

冒泡排序复杂度为O( n2 n 2 )

插入排序

类似于人们排列手中扑克牌的顺序。也就是说,如果你按照顺序排 好了前i-1张牌,抓取第i张牌并且与手中的这些牌进行比较,直到找到合适的位置

假设有一组无序序列 R0,R1,...,Rn1 R 0 , R 1 , . . . , R n − 1

  1. 我们先将这个序列中下标为 0 的元素视为元素个数为 1 的有序序列。
  2. 然后,我们要依次把 R1,R2,...,Rn1 R 1 , R 2 , . . . , R n − 1 插入到这个有序序列中。所以,我们需要一个外部循环,从下标 1 扫描到 n-1 。
  3. 接下来描述插入过程。假设这是要将 Ri R i 插入到前面有序的序列中。由前面所述,我们可知,插入 Ri R i 时,前 i-1 个数肯定已经是有序了。

所以我们需要将 Ri R i R0 R 0 ~ Ri1 R i − 1 进行比较,确定要插入的合适位置。这就需要一个内部循环,我们一般是从后往前比较,即从下标 i-1 开始向 0 进行扫描。

def insertionSort(lyst):
    i = 1
    while i < len(lyst):
        itemToInsert = lyst[i]
        j = i - 1
        while j >= 0:
            if lyst[j] > itemToInsert:
                lyst[j + 1] = lyst[j]
                j -= 1
            else:
                break
        lyst[j + 1] = itemToInsert
        i += 1

更快的排序

快速排序

快速排序所使用的策略可以概括如下:

  1. 首先,从列表的中点位置选取一项。在这一项叫做基准点。

  2. 将列表中的项分区,以便小于基准点的所有项都移动到基准点左边,而剩下的项都移动到基准点的右边。根据相关的实际项,基准点自身的最终位置也是变化的。例如,如果基准点自身是最大的项,它会位于列表的最右边,如果基准点是最小值,它会位于最左边。但是,不管基准点最终位于何处,这个位置都是它在 完全排序的列表中的最终位置。

  3. 分而治之。对于基准点分割列表而形成的子列表,递归地重复该过程。一个子列表包含了基准点左边所有的项(现在是较小的项),另一个子列表包含了基准点右边的所有的项(现在是较大的项)。

  4. 每次遇到少于两个项的一个子列表,就介绍这个过程。

步骤列表
假设子列表是由数字组成的,其中包含一个基准点1412 19 17 18 14 11 15 13 16
将基准点和最后一项交换12 19 17 18 16 11 15 13 14
在第一项之前建立一个边界* 12 19 17 18 16 11 15 13 14
扫描小于基准点的第一项* 12 19 17 18 16 11 15 13 14
将这一项和边界之后的第一项交换。在这个例子中,
该项是与自身交换
* 12 19 17 18 16 11 15 13 14
将边界向后移动12 * 19 17 18 16 11 15 13 14
扫描小于基准点的第一项12 * 19 17 18 16 11 15 13 14
将这一项和边界之后的第一项交换12 * 11 17 18 16 19 15 13 14
将边界向后移动12 11 * 17 18 16 19 15 13 14
扫描小于基准点的第一项12 11 * 17 18 16 19 15 13 14
将这一项和边界之后的第一项交换12 11 * 13 18 16 19 15 17 14
将边界向后移动12 11 13 * 18 16 19 15 17 14
扫描小于基准点的第一项;然而这次没有这样的一项12 11 13 * 18 16 19 15 17 14
将这一项和边界之后的第一项交换。此时小于基准
项的所有项都在基准项的左边;剩余的项在基准点
的右边
12 11 13 * 14 16 19 15 17 18
import random
def quicksort(lyst):
    quicksortHelper(lyst, 0, len(lyst) - 1)
def quicksortHelper(lyst, left, right):
    if left < right:
        pivolocation = partition(lyst, left, right)
        quicksortHelper(lyst, left, pivolocation - 1)
        quicksortHelper(lyst, pivolocation + 1, right)
def partition(lyst, left, right):
    # 找到基准点与最后一项交换
    middle = (left + right) // 2
    pivot = lyst[middle]
    lyst[middle], lyst[right] = lyst[right], lyst[middle]
    # 在第一项前建立一个边界
    boundary = left
    # 将小于基准点的第一项与边界后的第一项交换
    for index in range(left, right):
        if lyst[index] < pivot:
            lyst[index], lyst[boundary] = lyst[boundary], lyst[index]
            boundary += 1
    # 全部扫描完后 将基准点与边界后的那一项交换
    lyst[right], lyst[boundary] = lyst[boundary], lyst[right]
    return boundary
def main(size=20, sort=quicksort):
    lyst = []
    for count in range(size):
        lyst.append(random.randint(1, size + 1))
    print(lyst)
    sort(lyst)
    print(lyst)
if __name__ == '__main__':
    main()

合并排序(归并排序)

非正式的概述;

  1. 计算一个列表的中间位置,并且递归地排序其左边和右边的子列表(分而治之).
  2. 将两个排好序的子列表重新合并为单个的排好序的列表
  3. 当子列表不再能够划分的时候,停止这个过程
from TestClass import Array


def mergeSort(lyst):
    '''
    合并的过程中使用了和列表相同大小的一个数组(这个数组是在根目录下定义的)。这个数组名为copyBuffer。为了避免每次调用merge的时候为copyBuffer分配和释放内存的开销。只在mergeSort中分配一次该缓冲区,并且在后续将其作为一个参数传递给mergeSortHelper和merge。每次调用mergeSortHelper的时候,都需要知道他所操作的子列表的边界。这些边界通过另外的两个参数low,high来提供。
    '''
    copyBuffer = Array(len(lyst))
    mergeSortHelper(lyst, copyBuffer, 0, len(lyst) - 1)

def mergeSortHelper(lyst, copyBuffer, low, high):
    if low < high:
        middle = (low + high) // 2
        mergeSortHelper(lyst, copyBuffer, low, middle)
        mergeSortHelper(lyst, copyBuffer, middle + 1, high)
        merage(lyst, copyBuffer, low, middle, high)

def merage(lyst, copyBuffer, low, middle, high):
    '''
    :param low: 第一个子列表的第一索引
    :param middle: 第一个子列表的最后一索引
           middle+1:第二个子列表的第一索引
    :param high: 第二个子列表的最后一索引
    这个函数将两个排好序的子列表合并到一个大的拍好序的子列表中。

    '''
    i1 = low  # 将索引指针设置为每个子列表的第一索引
    i2 = middle + 1

    for i in range(low, high + 1):  # 从每个子列表的第一项开始重复地比较各项。将较小的项从其子列表中复制到复制缓存即copyBuffer中
        if i1 > middle:  # 并继续处理子列表中的下一项。重复这个过程,直到两个子列表中的所有的项都已经复制过。如果先到达其中一个子列表的末尾,通过从另一个子列表复制剩余的项,从而结束这个步骤。
            copyBuffer[i] = lyst[i2] 
            i2 += 1
        elif i2 > high:
            copyBuffer[i] = lyst[i1]
            i1 += 1
        elif lyst[i1] < lyst[i2]:
            copyBuffer[i] = lyst[i1]
            i1 += 1
        else:
            copyBuffer[i] = lyst[i2]
            i2 += 1
    for i in range(low, high + 1):  # 将copyBuffer中low 和high之间的部分,复制回lyst中对应的位置。
        lyst[i] = copyBuffer[i]

排序法平均时间最差情形稳定度额外空间备注
冒泡O( n2 n 2 )O( n2 n 2 )稳定O(1)n小时较好
选择O( n2 n 2 )O( n2 n 2 )不稳定O(1)n小时较好
插入O( n2 n 2 )O( n2 n 2 )稳定O(1)大部分已排序时较好
基数O( logRB l o g R B )O( logRB l o g R B )稳定O(n)B是真数(0-9),
R是基数(个十百)
ShellO( nlogn n l o g n )O( ns n s ) (1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值