数据结构学习 第三章 搜索和排序算法

搜索算法

1. 最小值搜索

def indexOfMin(lyst: list) -> int:
    minIndex = 0
    currentIndex = 1
    while currentIndex < len(lyst):
        if lyst[currentIndex] < lyst[minIndex]:
            minIndex = currentIndex
        currentIndex += 1
    return minIndex

​ 该函数与python中自带的min()方法是一个效果,都是找出列表中的最小值,其运行逻辑是假定最小值是第一个并将其索引赋值出来,用第一个与后面每一个数据进行比对,若后面有比该索引更小的数据,将最小索引赋值为更小数据的索引,直到没有一个比该索引对应的值小的,返回最小索引。

​ 该函数的复杂度为O(n)。

2. 顺序搜索

def sequentialSearch(target, lyst: list) -> int:
    position = 0
    while position < len(lyst):
        if target == lyst[position]:
            return position
        position += 1
    return -1

​ 该函数是顺序搜索列表的函数,定义一个变量来存储搜索索引,初始值为0,并逐步递增;当找到目标值时,输出此时的索引;若列表中未找到,则输出-1.

​ 在最好情况下,该函数的复杂度为O(1);最坏情况下为O(n);平均情况为O(n)。

计算平均情况的复杂度

​ 若要找到一个函数平均情况的复杂度,只需要将该函数不同情况下需要代码需要执行的次数相加在除以n即可。

3. 二分搜索

def binarySearch(target, sortedLyst: list) -> int:
    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

​ 该函数是基于有序列表的二分搜索,即待搜索的列表是有序的,若列表无序,则需要先对列表进行排序。该函数先定义左右指针分别指向列表左右端点,在做指针不超过右指针的情况下,找到中点并将中点对应的值与目标值进行对比,若相等,则返回中点索引;若目标值小于中点值,则将右指针设置在中点的前一个位置并重复上述比较;若目标值大于中点值,则将左指针设置在中点的后一个位置并重复上述比较;到最后没找到目标值,则返回-1。

​ 计算中点时使用"//“而不是”/“是因为中点是索引值,需要int类型,而”/"计算出来是float类型。

​ 该函数在最好情况下复杂度为O(1),最坏情况下为O( l o g 2 n log_2 n log2n),平均情况下也是O( l o g 2 n log_2n log2n)。

排序算法

交换函数

def swap(lyst, i, j):
    temp = lyst[i]
    lyst[i] = lyst[j]
    lyst[j] = temp

​ 用于交换列表中i,j位置的元素。

1. 基本的排序算法

1.1. 选择排序

def selectionSort(lyst: list):
    i = 0
    while i < len(lyst) - 1:
        minIndex = i
        j = i + 1
        while j < len(lyst):
            if lyst[j] < lyst[minIndex]:
                minIndex = j
            j += 1
        if minIndex != i:
            swap(lyst, minIndex, i)
        i += 1

​ 该算法会定义两个指针i,j,i在最初的时候,i指针会指向第一个元素,而j指针会指向i指针的后一个元素,将最小值索引设置为i指针并开始移动j指针,将j指针指向的值与最小值索引所代表的值进行对比,若j指针指向的值更小,则将最小值索引更改为j指针指向的索引并继续比较,直到最后一个元素,此时就最小值索引代表的就是当前对最小的元素,将最小值索引与i指针指向的索引相比较,如果不同(因为在循环开始最小值索引设置的值是i指向的索引),则将i指针指向的值与最小值索引代表的值进行位置交换,这样就能成功排序。

​ 该算法在所有情况下复杂度都是O( n 2 n^2 n2)

1.2. 冒泡排序

def bubbleSort(lyst: list):
    n = len(lyst)
    while n > 1:
        i = 1
        while i < n:
            if lyst[i] < lyst[i - 1]:
                swap(lyst, i, i - 1)
            i += 1
        n -= 1

​ 该算法定义两个指针n,i,n指针指向列表末尾并逐步前移,i指针指向第二个元素并逐步后移且i指针永远要小于n指针,从第二个元素开始,每个元素都只与它的前一个元素进行比较,如果该元素小于前一个元素,则将这两个元素交换位置并将i指针向后移一位,直到i指针与n指针执行同一个元素为止,视为一轮,这样一轮想气泡一样将最大的一位数字移到列表末尾,然后令列表末尾的n指针向前移一位(因为第一轮已经找到了最大值并将其放置在了最后了)并开始下一轮,如冒泡般将每一轮的最大值移动到当前轮次的n指针处。

​ 该算法与选择排序一样,在所有情况下复杂度都是O( n 2 n^2 n2)。

1.3. 插入排序

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

​ 该算法定义两个指针i,j,在初始条件下,i指针指向第二个元素,并将待插入的元素设置为i指针指向的元素,j指针指向i指针的前一个元素,当待插入的元素小于j指针指向的元素时,将j指针指向的元素向后移动一位用来空出待插入的位置,直到j指针无法前移为止,然后将待插入元素插入进空位即可,然后将i指针后移一位并重复上述步骤。

​ 该算法在最好情况下复杂度是O(n),而最坏和平均情况下复杂度都是O( n 2 n^2 n2)。

2. 更快的排序算法

​ 更快的排序算法都是基于自身递归的算法,其复杂度均为O( n l o g 2 n nlog_2n nlog2n)。

2.1 快速排序

def quickSort(lyst: list):
    quickSortHelper(lyst, 0, len(lyst) - 1)

def quickSortHelper(lyst: list, left: int, right: int):
    if left < right:
        boundary = partition(lyst, left, right)
        quickSortHelper(lyst, left, boundary - 1)
        quickSortHelper(lyst, boundary + 1, right)

def partition(lyst: list, left: int, right: int):
    middle = (left + right) // 2

    swap(lyst, middle, right)

    boundary = left

    for index in range(left, right):
        if lyst[index] < lyst[right]:
            swap(lyst, index, boundary)
            boundary += 1
    swap(lyst, right, boundary)

    return boundary

​ 该算法使用了递归来进行编码,通过递归和分治策略来突破O( n 2 n^2 n2)的时间复杂度,快速排序指的是先在列表中找一个基准值(通常是最中间的那一个值),然后将基准值移动到最后面,在列表开头添加一个边界。将列表中的每个元素都与基准值进行比较,若小于基准值,则将其放在边界的左边,而大于基准值的元素放在边界的右边,最后将基准值放在边界处,这样就基于基准值形成了两个子列表,分别是大于基准值的和小于基准值的,将这两个子列表分别重复上述步骤,直到分出的子列表中少于两个元素才停止,这样就可以将所有元素排序完成了。

​ 该算法的复杂度为O( n l o g 2 n nlog_2n nlog2n)。

2.2 归并排序

def mergeSort(lyst: list):
    copyBuffer = Array(len(lyst))
    mergeSortHelper(lyst, copyBuffer, 0, len(lyst) - 1)

def mergeSortHelper(lyst: list, copyBuffer: Array, low: int, high: int):
    if low < high:
        middle = (low + high) // 2
        mergeSortHelper(lyst, copyBuffer, low, middle)
        mergeSortHelper(lyst, copyBuffer, middle + 1, high)
        partition(lyst, copyBuffer, low, middle, high)

def partition(lyst: list, copyBuffer: Array, low: int, middle: int, high: int):
    firstListStart = low
    secondListStart = middle + 1

    for i in range(low, high + 1):
        if firstListStart > middle:
            copyBuffer[i] = lyst[secondListStart]
            secondListStart += 1
        elif secondListStart > high:
            copyBuffer[i] = lyst[firstListStart]
            firstListStart += 1
        elif lyst[firstListStart] < lyst[secondListStart]:
            copyBuffer[i] = lyst[firstListStart]
            firstListStart += 1
        else:
            copyBuffer[i] = lyst[secondListStart]
            secondListStart += 1

    for i in range(low, high + 1):
        lyst[i] = copyBuffer[i]

​ 该算法也使用了递归编码,通过递归和分治策略来突破O( n 2 n^2 n2)的时间复杂度,归并算法是将一个列表从中间分开,分为两个子列表,并将两个子列表分别从中间分开,直到子列表无法在分为止,然后在将排序好的子列表合并,最终形成一个完整的列表。代码中使用的copyBuffer是使用数组类来创建一个缓冲区,用来放置列表合并过程中的子列表,最后将合并完成后的列表重新赋值给lyst(原列表)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值