草庐IT

python实现各种算法详解,以及时间复杂度

WELCOME TO ivanlee717!!!! 2023-04-11 原文

python实现各种排序

1. 快速排序

1:首先取序列第一个元素为基准元素pivot=R[low]。i=low,j=high。

2:从后向前扫描,找小于等于pivot的数,如果找到,R[i]与R[j]交换,i++。

3:从前往后扫描,找大于pivot的数,如果找到,R[i]与R[j]交换,j--。

4:重复2~3,直到i=j,返回该位置mid=i,该位置正好为pivot元素。 完成一趟排序后,以mid为界,将序列分为两部分,左序列都比pivot小,有序列都比pivot大,然后再分别对这两个子序列进行快速排序。

以序列(30,24,5,58,16,36,12,42,39)为例,进行演示:

1、初始化,i=low,j=high,pivot=R[low]=30:

i j
30 24 5 58 16 26 12 41 39

2、从后往前找小于等于pivot的数,找到R[j]=12

i j
30 24 5 58 16 26 12 41 39
  • R[i]与R[j]交换,i++
i j
12 24 5 58 16 26 30 41 39

3、从前往后找大于pivot的数,找到R[i]=58

i j
12 24 5 58 16 26 30 41 39
  • R[i]与R[j]交换,j--
i j
12 24 5 30 16 26 58 41 39

4、从后往前找小于等于pivot的数,找到R[j]=16

i j
12 24 5 30 16 26 58 41 39
  • R[i]与R[j]交换,i++
i,j
12 24 5 16 30 26 58 41 39

5、从前往后找大于pivot的数,这时i=j,第一轮排序结束,返回i的位置,mid=i

Low mid-1 mid mid+1 High
12 24 5 16 30 26 58 41 3

此时已mid为界,将原序列一分为二,左子序列为(12,24,5,18)元素都比pivot小,右子序列为(36,58,42,39)元素都比pivot大。然后在分别对两个子序列进行快速排序,最后即可得到排序后的结果。

def quicksort(low,high,li):
    if low >= high:
        return li
    i = low
    j= high
    pivot = li[low]
    while i<j:
        while i<j and li[j]>pivot:
            j -= 1
        li[i] = li[j]
        while i<j and li[i]<pivot:
            i += 1
        li[j] = li[i]

    li[j] = pivot
    quicksort(low,i-1,li)
    quicksort(i+1,high,li)
    return li



lists=[30,24,5,58,18,36,12,42,39]
print("排序前的序列为:")
for i in lists:
    print(i,end =" ")
print("\n排序后的序列为:")
for i in quicksort(0,len(lists)-1,lists):
    print(i,end=" ")

2. 冒泡排序

冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

冒泡排序算法的运作如下:

  • 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

def bubblesort(li):
    for i in range(len(li)-1,0,-1):
        for j in range(1,i):
            if li[j-1]>li[j]:
                li[j-1],li[j] = li[j],li[j-1]

    return li

li = [54,26,93,17,77,31,44,55,20]
print(bubblesort(li))

3. 插入排序

插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。

这个算法和冒泡排序有点类似,都是两层for循环,一个是从正面挨个比,一个是倒着比

def insertsort(li):
    for i in range(1,len(li)):
        for j in range(i,0,-1):
            if li[j-1]>li[j]:
                li[j-1],li[j] = li[j],li[j-1]

    return li

li = [54,26,93,17,77,31,44,55,20]
print(insertsort(li))

4. 希尔排序

希尔排序是先取一个小于待排序列表长度的正整数d1,把所有距离为d1的数据看成一组,在组内进行插入排序。然后取一个小于d1的正整数d2,继续用d2分组和进行组内插入排序。每次取的分组距离越来越小,组内的数据越来越多,直到di=1,所有数据被分成一组,再进行插入排序,则列表排序完成。

希尔排序是基于插入排序进行优化的排序算法。在插入排序中,如果数据几乎已经排好序时,效率是很高的(想一下抓牌和插牌),时间复杂度为 O(n) 。但数据的初始状态并不一定是“几乎已经排好序”的,用插入排序每步只能将待插入数据往前移动一位,而希尔排序将数据分组后,每次可以往前移动di位,在di>1时进行的分组和组内排序可以将数据变成“几乎排好序”的状态,此时再进行最后一次插入排序,提高效率。

希尔排序的原理如下:

  1. 选择小于待排序列表长度 n 的正整数序列 d1,d2,...,di,其中 n>d1, d(i-1)>di, di=1 ,作为数据的间隔距离对列表进行分组。这里对 di 的取值和个数没有要求,只要是整数,d1<n,依次变小即可。

  2. 依次用 d1, ..., di 作为数据的距离对列表进行分组和组内插入排序,一共需要进行 i 轮排序。

  3. 在最后一轮排序前,列表中的数据达到了“几乎排好序”的状态,此时进行最后一轮插入排序。

以列表 [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21] 进行升序排列为例。

要进行升序排列,则分组后所有组内插入排序都进行升序排列。本例中以列表长度的length/3作为初始的分组距离 d1 ,d1=4。

  1. 从列表的开头开始,对所有数据按 d1 作为距离进行分组,分组只保证数据的间隔距离相等,不保证每组的数据个数一样,只是本例中刚好每组数据一样多。本例的数据可以分为4组,也就是第1,5,9作为排序对象,然后2,6,10;3,7,11;4,8,12.一共要排序4次。排完序为

  2. 第二次排序的话,这个距离为4//3 = 1,也就是最后一轮排序。这也和直接插入排序完全一样了,先将第一个数据作为已排序序列,后面的数据作为未排序序列,然后依次将未排序序列中的数据插入到已排序序列中。

def shellsort(li):
    n = len(li)
    gap = n//3
    while gap > 0:
        for i in range(gap,n):
            j = i
            while j>=gap and li[j-gap]>li[j]:
                li[j-gap],li[j] = li[j],li[j-gap]
                j -= gap
        gap = gap//3

alist = [54,26,93,17,77,31,44,55,20]
shellsort(alist)
print(alist)

5. 归并排序

将原问题划分成 n 个规模较小而结构与原问题相似的子问题;递归地解决这些问题,然后再合并其结果,就得到原问题的解。从上图看分解后的数列很像一个二叉树。

归并排序采用分而治之的原理:

  1. 将一个序列从中间位置分成两个序列;
  2. 在将这两个子序列按照第一步继续二分下去;
  3. 直到所有子序列的长度都为1,也就是不可以再二分截止。这时候再两两合并成一个有序序列即可。

def merge(left,right):
    r,l = 0,0
    res = []
    while r < len(right) and l < len(left):
        if left[l] < right[r]:  # 排序,小于等于的放左边
            res.append(left[l])
            l += 1
        else:
            res.append(right[r])  # 排序,大的放右边
            r += 1
    res +=(left[l:])
    res +=(right[r:])
  
    return res

def mergesort(li):
    if(len(li)) <2:
        return li

    mid = len(li)//2
    left = mergesort(li[:mid])
    right = mergesort(li[mid:])
    return merge(left,right)



li = [8,0,4,3,6,2]
print(mergesort(li))

6. 选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

def selection_sort(alist):
    n = len(alist)
    # 需要进行n-1次选择操作
    for i in range(n-1):
        # 记录最小位置
        min_index = i
        # 从i+1位置到末尾选择出最小数据
        for j in range(i+1, n):
            if alist[j] < alist[min_index]:
                min_index = j
        # 如果选择出的数据不在正确位置,进行交换
        if min_index != i:
            alist[i], alist[min_index] = alist[min_index], alist[i]

alist = [54,226,93,17,77,31,44,55,20]
selection_sort(alist)
print(alist)

7. 堆排序

堆排序(Heap Sort)是利用堆这种数据结构而设计的一种排序算法,是一种选择排序。

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

堆排序思路为: 将一个无序序列调整为一个堆,就能找出序列中的最大值(或最小值),然后将找出的这个元素与末尾元素交换,这样有序序列元素就增加一个,无序序列元素就减少一个,对新的无序序列重复操作,从而实现排序。

算法实现步骤

  1. 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆);
  2. 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素;
  3. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素;
  4. 如此反复进行交换、重建、交换,直到整个序列有序。
def heap_sort(array):
    first = len(array) // 2 - 1
    for start in range(first, -1, -1):
        # 从下到上,从右到左对每个非叶节点进行调整,循环构建成大顶堆
        big_heap(array, start, len(array) - 1)
    for end in range(len(array) - 1, 0, -1):
        # 交换堆顶和堆尾的数据
        array[0], array[end] = array[end], array[0]
        # 重新调整完全二叉树,构造成大顶堆
        big_heap(array, 0, end - 1)
    return array


def big_heap(array, start, end):
    root = start
    # 左孩子的索引
    child = root * 2 + 1
    while child <= end:
        # 节点有右子节点,并且右子节点的值大于左子节点,则将child变为右子节点的索引
        if child + 1 <= end and array[child] < array[child + 1]:
            child += 1
        if array[root] < array[child]:
            # 交换节点与子节点中较大者的值
            array[root], array[child] = array[child], array[root]
            # 交换值后,如果存在孙节点,则将root设置为子节点,继续与孙节点进行比较
            root = child
            child = root * 2 + 1
        else:
            break


if __name__ == '__main__':
    array = [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21]
    print(heap_sort(array))

8. 计数排序

计数排序(Counting Sort)是一种不比较数据大小的排序算法,是一种牺牲空间换取时间的排序算法。

计数排序适合数据量大且数据范围小的数据排序,如对人的年龄进行排序,对考试成绩进行排序等。

计数排序先找到待排序列表中的最大值 k,开辟一个长度为 k+1 的计数列表,计数列表中的所有初始值都为 0。走访待排序列表,如果走访到的元素值为 i,则计数列表中索引 i 的值加1,走访完整个待排序列表,就可以统计出待排序列表中每个值的数量。然后创建一个新列表,根据计数列表中统计的数量,依次在新列表中添加对应数量的 i ,得到排好序的列表。

def counting_sort(array):
    if len(array) < 2:
        return array
    max_num = max(array)
    count = [0] * (max_num + 1)
    for num in array:
        count[num] += 1
    new_array = list()
    for i in range(len(count)):
        for j in range(count[i]):
            new_array.append(i)
    return new_array
 
 
if __name__ == '__main__':
    array = [5, 7, 3, 7, 2, 3, 2, 5, 9, 5, 7, 6]
    print(counting_sort(array))

9. 桶排序

桶排序(Bucket sort)是一种通过分桶和合并实现的排序算法,又被称为箱排序。

桶排序先将数据分到有限数量的桶里,然后对每一个桶内的数据进行排序(桶内排序可以使用任何一种排序算法,如快速排序),最后将所有排好序的桶合并成一个有序序列,列表排序完成。

桶排序需要占用很多额外的空间,对桶内数据进行排序,选择哪种排序算法对于性能的影响至关重要。桶排序适用的场景并不多,用得多一点的是基于桶排序思想的计数排序和基数排序。

桶排序的原理如下:

  1. 求出待排序列表中的最大值和最小值,得到数据的范围。

  2. 根据数据的范围,选择一个适合的值构建有限数量的桶,确定每个桶的数据范围。如数据范围是[0,100),将数据分成10个桶,第一个桶为[0,10),第二个桶为[10,20),以此类推。

  3. 将待排序列表中的数据分配到对应的桶中。

  4. 对每一个桶内的数据进行排序,这里可以采用任意一种排序算法,建议采用时间复杂度小的排序算法。

  5. 将所有桶中的数据依次取出,添加到一个新的有序序列中,列表排序完成。

# coding=utf-8
def bucket_sort(array):
    min_num, max_num = min(array), max(array)
    bucket_num = (max_num-min_num)//3 + 1
    buckets = [[] for _ in range(int(bucket_num))]
    for num in array:
        buckets[int((num-min_num)//3)].append(num)
    new_array = list()
    for i in buckets:
        for j in sorted(i):
            new_array.append(j)
    return new_array
 
 
if __name__ == '__main__':
    array = [5, 7, 3, 7, 2, 3, 2, 5, 9, 5, 7, 8]
    print(bucket_sort(array))

有关python实现各种算法详解,以及时间复杂度的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  5. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  6. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  7. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  8. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  9. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  10. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

随机推荐