经典排序算法(冒泡排序,选择排序,插入排序,快速排序,堆排序)python实现

最近在复习经典排序算法,自己用python也实现了一下,这里不会涉及到原理(因为网上方法已经很详细啦),就把函数贴上来,可以让大家自己试着运行下,再结合别处的原理也可以更好地理解它们的实现。
如果有错误请指出,或者优化的地方,谢谢啦。(´▽`)

1. 冒泡排序

冒泡排序是实现起来最简单的排序算法,时间复杂度是O(n^2),它的代码核心是两层嵌套的for循环,循环里一个判断数组相邻两个元素大小,如果不满足就交换。
冒泡排序有一个小的优化的点:如果在外层循环的一趟里没有交换任何元素,就说明排序完成了,就不需要再继续排了。所以也设置了一个flag判断。
代码如下:

"""
bubble sorting
"""
 def bubble_sorting(array):
    for i in range(len(array)-1):
        flag = False
        for j in range(len(array)-i-1):
            if array[j]>array[j+1]:
                flag = True
                tmp = array[j]
                array[j] = array[j+1]
                array[j+1] = tmp
        if not flag:
            break
    return array
array = [0,1,0,7,9,8,5,4,2,0]
print(bubble_sorting(array))

运行结果就是[0, 0, 0, 1, 2, 4, 5, 7, 8, 9]

2. 选择排序

选择排序也是一种时间复杂度是O(n^2)的稳定排序算法,它的核心思想就是每次遍历数组选出最小的放在左边 ,这样右边无序区越来越小,左边有序区越来越大。也是两层循环嵌套。
代码如下:

def select_sorting(array):
    for i in range(len(array)):
        min = array[i]
        min_index = i
        for j in range(i,len(array)):
            if array[j]<min:
                min = array[j]
                min_index = j
        array[min_index] = array[i]
        array[i] = min
    return array
print(select_sorting(array))

运行结果 也是[0, 0, 0, 1, 2, 4, 5, 7, 8, 9]

3. 插入排序

插入排序跟上面两种排序算法比起来就算有点点绕了,它虽然也是需要两层循环,但是内层不再是遍历。插入排序要注意的是外层循环从第二个值开始,把第一个值当作是有序区。这样每一个新的值都会跟前面的有序区进行比较,如果小于前一个数就插到前面,一直到合适的位置为止。

def insert_sorting(array):
    for i in range(1,len(array)):
        j = i-1
        while j >= 0 and array[j]>array[j+1]:
            tmp = array[j+1]
            array[j+1] = array[j]
            array[j] = tmp
            j -= 1
    return array
print(insert_sorting(array))

7-28修改:之前还是觉得内层循环并不是插入排序,而是冒泡排序了,实际上并不需要每一个结果都要和上一个交换,因为插入排序应该是新的数和前面有序区的每一个数比较,如果仍然大于就把前面的数挪到后面,最后再插入新数。所以算法优化如下。
这里面的最后j+1其实也卡了我很久,但是后来想到:循环退出时一定不满足条件了,要么是j小于零,要么是终于找到了合适的位置。这让我想到一个小的脑筋急转弯:如果一个新的数比第二个数小,那么应该插入到哪里?答案是第二个。所以最后要把1加回来。

def insert_sorting1(array):
    for i in range(1,len(array)):
        j = i-1
        tmp = array[i]
        while j >= 0 and array[j]>tmp:
            array[j+1] = array[j]
            j -= 1
        array[j+1] = tmp
    return array

print(insert_sorting1([3,5,4,2,9,1,1,7,8,4]))

4. 快速排序

快速排序……卡了我很久很久,因为我一直在尝试用循环递推做,我……失败了。快排本身是一种很常用(超重要,面试也经常考的排序算法),它是一种“分治”的思想,就是要用递归的意思,接受了这一点再来写,就不会自己难为自己了。它始终选一个基准,一般是左边第一个,然后注意从右往左推,找到比他更小的就停止,然后从左往右推,找到比他更大的停止,然后判断下左右两个指针仍未相遇的话,就交换左右的值。此时左边都比基准小,右边都比基准大。再对左右两个子列表分别用这个方法,直到整个列表都有序。
接下来献上我很丑的代码:

def quick_sorting(start, end, array):
   if start >= end:
       return
   min = start
   max = end
   pivot = array[min]
   while start < end:
       while start < end and array[end] > pivot:
           end -= 1
       while start < end and array[start] < pivot:
           start += 1
       if start < end:
           array[start], array[end] = array[end], array[start]

   quick_sorting(min, start-1, array)
   quick_sorting(start+1,max,array)

   return array

假如你并不需要保留重复的项,或者需要排序的列表没有重复项,那么可以用一个真正简单美丽的递归解决问题,其实这个就是刚才的算法的简化:

def quick_sort(array):
    if len(array)<2:
        return array
    pivot = array[0]
    left = [i for i in array if i < pivot]
    right = [i for i in array if i > pivot]
    return quick_sort(left)+[pivot]+quick_sort(right)
print(quick_sort(array))

7-25 修改:

刚刚想到一个方法可以使上面的快排保留重复的值,只要做一点小的变化:

def quick_sorting(array):
    if len(set(array))<2:
        return array
    middle = array[0]
    left = [i for i in array if i <middle]
    pivot = [i for i in array if i == middle]
    right = [i for i in array if i > middle]

    return quick_sorting(left)+quick_sorting(pivot)+quick_sorting(right)

print(quick_sorting([9,9,1,8,5,2,3,3,7,14,2,2,2,2]))

所以以后偷懒的快排就可以这么写啦。

5. 堆排序(堆的基本操作)

我是按照小灰这一篇堆排序的文章写的代码,可以参考:
漫画:什么是二叉堆?(修正版)
总结起来,堆排序首先要理解二叉堆,大顶堆,小顶堆的概念,然后堆的基本操作有:插入(向上调整),删除(向下调整),调整二叉堆等。
希望大家有空可以读一下小灰的那篇文章~写得很清楚了,我这里就贴自己写得代码和用例吧。

# coding:utf-8
import sys

class HeapSorting():

    def __init__(self,array):
        self.arr = array

    def insert(self,value):
        child_index = len(self.arr)
        self.arr.append(value) # add the new value at the end of array
        tmp = self.arr[child_index]
        while child_index >= 1 :
            parent_index = (child_index - 1) // 2
            if self.arr[parent_index] < tmp:
                break
            self.arr[child_index] = self.arr[parent_index]
            child_index = parent_index
        array[child_index] = tmp

        return self.arr


    def delete(self):
        # delete the first value, make the last one the top
        self.arr[0] = self.arr[-1]
        del self.arr[-1]
        parent_index = 0
        child_index = parent_index*2+1
        while child_index <= len(self.arr)-1:
            child_index = parent_index * 2 + 2 if parent_index * 2 + 2 <= len(self.arr) - 1 and self.arr[
                parent_index * 2 + 2] < self.arr[parent_index * 2 + 1] else parent_index * 2 + 1
            if self.arr[parent_index] < self.arr[child_index]:
                break
            tmp = self.arr[parent_index]
            self.arr[parent_index] = self.arr[child_index]
            self.arr[child_index] = tmp
            parent_index = child_index
            child_index = parent_index*2+1
        return self.arr

    def down_adjust(self,parent_index):

        child_index = parent_index*2+1
        while child_index <= len(self.arr)-1:
            child_index = parent_index * 2 + 2 if parent_index * 2 + 2 <= len(self.arr) - 1 and self.arr[
                parent_index * 2 + 2] < self.arr[parent_index * 2 + 1] else parent_index * 2 + 1
            if self.arr[parent_index] < self.arr[child_index]:
                break
            tmp = self.arr[parent_index]
            self.arr[parent_index] = self.arr[child_index]
            self.arr[child_index] = tmp
            parent_index = child_index
            child_index = parent_index*2+1
        return self.arr

    def adjust(self):
        # start from the last non-leaf node
        node = (len(self.arr)-2)//2

        for i in range(node,-1,-1):
            self.arr = self.down_adjust(i)
        return self.arr

array = [7, 2, 6, 3, 9, 8, 5, 10, 12]
heap = HeapSorting(array)
heap_insert = heap.insert(1)
heap_delete = heap.delete() # delete the value on top of the heap
heap_adjust = heap.adjust()

print(heap_adjust)

上面的操作对于理解堆来说非常重要。
过两天还会补上正式的堆排序~就先写到这里(鞠躬)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容