python选择排序算法图解_排序算法详解与python实现

Note:

写后感:

理解算法思想很重要!

理解算法思想很重要!

理解算法思想很重要!

之后尝试自己独立码代码对算法的理解更深刻!

本文章所有算法默认从小到大排序。

1. 冒泡排序(Bubble Sort)

自然语言描述

按照列表中待排序的先后顺序,依次比较相邻的两个数,若两者是升序则不做任何操作,否则交换两者位置。

核心算法举例

以第一趟为例

1 5 7 3 9 2 6 8 1 与 5比较,不变

1 5 7 3 9 2 6 8 5 与 7比较,不变

1 5 3 7 9 2 6 8 7 与 3比较,交换位置

1 5 3 7 9 2 6 8 7 与 9比较,不变

1 5 3 7 2 9 6 8 9 与 2比较, 交换位置

1 5 3 7 2 6 9 8 9 与 6比较,交换位置

1 5 3 7 2 6 8 9 9 与 8比较,交换位置

所以第一趟结束后,排序结果为

1 5 3 7 2 6 8 9 9为最大数,后续不需要比较

算法优劣分析

一共比较((N-1)+(N-2)+...+2+1)次

最好情况下交换0次

最坏情况下交换((N-1)+(N-2)+...+2+1)次

平均时间复杂度O(n²)

算法实现

#-*-coding=UTF-8-*-

class BubbleSort:

def __init__(self,list_=[]):

self.list_ = list_

def get_current_list(self):

return self.list_

def ascent_sort(self):

for i in range(0,len(self.list_)-1):

for j in range(0, len(self.list_)-i-1):

if self.list_[j]>self.list_[j+1]:

temp = self.list_[j]

self.list_[j] = self.list_[j+1]

self.list_[j+1] = temp

#实例化

list1 = BubbleSort([1,8,4,9,2])

print list1.get_current_list()

list1.ascent_sort()

print list1.get_current_list()

2. 选择排序算法(Selection Sort)

自然语言描述

遍历列表所有元素,最小(大)的元素放在最左(右)边。

确定第一个元素位置后,遍历剩下的所有元素,最小(大)的元素放在最左(右)边。

以此类推,直到倒数第二个元素。

核心算法举例

第二趟比较为例(以找最小数为例)

此时min_index = 1

1 7 5 9 4

7 与 5 作比较,min_index更新为2

5 与 9 作比较,min_index不变

5 与 4 作比较,min_index更新为4

将index = 4与index=1的元素交换位置

1 4 5 9 7

算法优劣分析

一共比较((N-1)+(N-2)+...+2+1)次,交换N次。

平均时间复杂度O(n²)

算法实现

#-*-coding = utf-8-*-

class SelectionSort:

def __init__(self,list_=[]):

self.list_ = list_

def get_current_list(self):

return self.list_

def ascent_sort(self):

for i in range(0,len(self.list_)-1):

min_index, swap_temp = i, i

for j in range(i, len(self.list_)-1):

if self.list_[min_index] > self.list_[j+1]:

min_index = j + 1

swap_temp = self.list_[min_index]

self.list_[min_index] = self.list_[i]

self.list_[i] = swap_temp

return self.list_

#实例化

list1 = SelectionSort([1,7,4,9,2])

print list1.get_current_list()

list1.ascent_sort()

print list1.get_current_list()

3. 直接插入算法(Insertion Sort)

自然语言描述

在前面已经排好序的列表中插入新元素。步骤:

将第二元素与第一个元素比较,如果小于第一个元素则交换位置,反之不变。

将第三个元素分别与前两个元素比较(这里的算法用的是与之前一个的位置比较),插入合适位置。

以此类推,直到最后一个元素。

核心算法举例

第二趟比较为例

此时 current_value = 1

5 7 1

current_value = 1 与7作比较

5 7 7

current_value = 1 与5作比较

5 5 7

将 current_value = 1 插入

1 5 7

算法优劣分析

最坏的情况下,一共比较并交换(1+2+...+(N-1))次

最好的情况下,只需比较(N-1)次,无需交换

平均时间复杂度O(n²)

稳定

插入排序适用于数量较小,部分或者全部排序过的列表。尽管插入排序的时间复杂度也是O(n²),但一般情况下,插入排序会比冒泡排序快一倍,要比选择排序还要快一点。并且,直接插入排序在对几乎已经排好序的数据操作时,效率高,可以达到线性排序的效率。

算法实现

#-*-coding = UTF-8-*-

class InsertionSort:

def __init__(self,list_=[]):

self.list_ = list_

def get_current_list(self):

return self.list_

def ascent_sort(self):

for i in range(1,len(self.list_)):

position = i

current_value = self.list_[i]

while position>0 and self.list_[position-1]>current_value:

self.list_[position] = self.list_[position-1]

position = position - 1

self.list_[position] = current_value

return self.list_

#实例化

list1 = InsertionSort([1,9,3,7])

print list1.get_current_list()

list1.ascent_sort()

print list1.get_current_list()

4. 希尔排序(Shell Sort)

自然语言描述

直接插入算法一趟只能为数据移动一位,比较低效。希尔排序作为直接插入算法的优化,又称缩小增量排序、递减增量排序。

以步长分区,对跳过步长的数进行直接插入算法排序,直至步长为1。

步长是希尔排序的精髓,已知的最好步长序列是由Sedgewick提出的 1,5,19,41,109,...

核心算法举例

原列表:[4,6,8,2,9,3,7,5,1]

步长增量:9/2=4, 4/2=2, 2/2=1 (Note: 取列表长度的一半作为最初步长,这里的步长使用的是Donald Shell的建议)

第一趟(步长为4):

原列表 4 6 8 2 9 3 7 5 1,分组后为:

4 6 8 2

9 3 7 5

1

排序后为:

1 3 7 2

4 6 8 5

9

即排序后列表顺序为:

1 3 7 2 4 6 8 5 9

第二趟(步长为2)

待排序列表1 3 7 2 4 6 8 5 9,分组后为:

1 3

7 2

4 6

8 5

9

排序后为:

1 2

4 3

7 5

8 6

9

即排序后列表顺序为:

1 2 4 3 7 5 8 6 9

第三趟(步长为1), 即直接插入排序

待排序列表1 2 4 3 7 5 8 6 9

排序后列表顺序为:1 2 3 4 5 6 7 8 9

算法优劣分析

希尔排序比插入排序要快,甚至在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。

不稳定

平均时间复杂度O(nlog2n)「n倍的log以2为底n」

算法实现

巧记:将直接插入算法的步长从1改为gap,加入限制条件gap > 0即可

#-*-coding = utf-8 -*-

class ShellSort():

def __init__(self, list_=[]):

self.list_ = list_

def get_current_list(self):

return self.list_

def ascent_sort(self):

gap = len(self.list_)/2

while gap > 0:

for i in range(gap, len(self.list_)):

current_value = self.list_[i]

position = i

while position >0 and self.list_[position - gap] > current_value:

self.list_[position] = self.list_[position - gap]

position = position - gap

self.list_[position] = current_value

gap = gap/2

return self.list_

list1 = ShellSort([1,9,3,2,5,8])

print list1.get_current_list()

list1.ascent_sort()

print list1.get_current_list()

5. 归并排序(Merge Sort)

自然语言描述

将两个有序表合并成一个有序表。归并排序算法依赖于归并操作。

eadffef6c81c

from Wikipedia

两个有序表,left = [0,3,5,7], right = [1,4],

合成的有序列表result = []

left[0] < right[0] ----> result = [0]

left[1] > right[0] ----> result = [0,1]

left[1] < right[1] ----> result = [0,1,3]

left[2] > right[1] ----> result = [0,1,3,4]

此时right[]为空,将left[]剩余的元素依次放入result列表中:result = [0,1,3,4,5,7]

两种核心算法

递归法

设计递归,将复杂的问题分解为最小规模子问题。

将列表分解为 两个更小的列表。

递归分解,将更小的列表继续分解,直到达到最小规模,也就是只有一个元素的时候。

对已经排序好的列表 进行合并。单个元素的列表,认为是已经排序好的。

eadffef6c81c

from Wikipedia

递归python实现

#-*-coding=utf-8-*-

#递归算法

def mergeSort(seq=[]):

if len(seq) == 1:

return seq

else:

mid = len(seq)/2

left = []

right = []

left = mergeSort(seq[:mid])

right = mergeSort(seq[mid:])

return merge(left,right)

#将排好序的

def merge(left=[],right=[]):

#i, j are index for left and right seperately

i, j = 0,0

result = []

while i < len(left) and j < len(right):

if left[i] <= right[j]:

result.append(left[i])

i = i + 1

else:

result.append(right[j])

j = j + 1

#将剩余的部分依次加入result

result = result + left[i:]

result = result + right[j:]

return result

#实例化

list1 = [2,6,8,1,3,9,4]

print list1

print mergeSort(list1)

#time consume

import random,time

start_time = time.time()

seq = random.sample(range(10000), 10000) #random.sample(取值范围, 获取的个数)

result = mergeSort(seq)

print 'Time consume:{}'.format(time.time()-start_time)

非递归法(迭代法)

从最小的子问题开始解决,直到复杂的问题。要搞清每次排序归并的对象。

eadffef6c81c

非递归算法(迭代法)

第一次:我们将数组分为 8个子数组 每个数组 1 个元素,对相邻的两个数组进行排序合并。

第二次:我们将数组分为 4个子数组 每个数组 2 个元素,对相邻的两个数组进行排序合并。

第三次:我们将数组分为 2个子数组 每个数组 4 个元素,对相邻的两个数组进行排序合并。

至此:排序完毕。

分析

第一步:每一次子数组的元素个数

k = 1 #子数组的个数

while k

k = k*2

第二步:确定要合并的两个相邻数组的区间[low:mid)[mid:height)

low = low(之前的low) +2k

mid = low(现在的low)+k

height = low(现在的low) + 2k

并且,

height不能越界(不能超过数组长度);

mid不能大于height(mid大于height说明此时子数组个数不足k,那么这个时候该子数组不给予拆分直接pass,下图给予说明)

eadffef6c81c

来自http://www.jianshu.com/p/3f27384387c1

非递归python实现

#-:-coding=utf-8-*-

class MergeSort:

def __init__(self,seq=[]):

self.seq = seq

def get_current_seq(self):

return self.seq

def ascent_sort(self):

k = 1 #子数组元素的个数

while k < len(self.seq):

low = 0

while low < len(self.seq):

height = min(low + 2*k, len(self.seq))

mid = low + k

if mid < height:

'''mergeSort'''

left = self.seq[low:mid]

right = self.seq[mid:height]

result =[]

i,j = 0,0

while i < len(left) and j < len(right):

if left[i] < right[j]:

result.append(left[i])

i += 1

else:

result.append(right[j])

j += 1

result = result + left[i:]

result = result + right[j:]

'''将原始数组的[low,height)替代为已经排好序的数组'''

self.seq[low:height] = result

low = low + 2*i

k *= 2

return self.seq

list1 = MergeSort([3,5,2,1])

print list1.get_current_seq()

list1.ascent_sort()

print list1.get_current_seq()

6. 堆排序(HeapSort)

堆排序是对简单选择排序的一种优化。

堆的定义及性质见这里。

堆的主要性质:

若根的index为1,则最后一个非叶子节点的index为len(seq)/2。此时对于index为i的节点,其左节点的index为2i;右节点的index为2i+1。

若根的index为0,最后一个非叶子节点的index为len(seq)/2-1。此时对于index为i的节点;其左节点的index为2i+1,右节点的index为2i+2。

堆排序需要解决两个主要的问题:

P1:如何将无序的列表构建成最小堆

strip

P1:如何从无序数组构建最小堆

P2:将最小堆的顶部取出后如何重建最小堆

strip

P2:如何重构最小堆

堆排序过程演示:

eadffef6c81c

from Wikipedia

堆排序的三个步骤:

构建堆

调整堆

堆排序

堆排序python实现

#-*-coding=utf-8-*-

def build_max_heap(seq):

"""建立一个堆"""

#根据完全二叉树的性质,根的index为0,非叶子节点的index为1至len(seq)/2-1,

#

for i in range(len(seq)/2-1, -1, -1):

max_heap(seq, len(seq), i)

def max_heap(seq, heap_size, index):

"""调整列表中的元素以保证以index为根的堆是一个最大堆--->从而最终得到从小到大的排列顺序"""

# 将当前结点与其左右子节点比较,将较大的结点与当前结点交换,然后递归地调整子树

left_child = 2 * index + 1

right_child = left_child + 1

if left_child < heap_size and seq[left_child] > seq[index]:

largest = left_child

else:

largest = index

if right_child < heap_size and seq[right_child] > seq[largest]:

largest = right_child

if largest != index:

seq[index], seq[largest] = seq[largest], seq[index] #python特有,不需temp

max_heap(seq, heap_size, largest)

def heap_sort(to_sort_list):

"""堆排序"""

# 先将列表调整为堆

build_max_heap(to_sort_list)

heap_size = len(to_sort_list)

# 调整后列表(此时为堆)的第一个元素就是这个列表中最大的元素,将其与最后一个元素交换,然后将剩余的列表再调整为最大堆

for i in range(len(to_sort_list) - 1, 0, -1):

to_sort_list[i], to_sort_list[0] = to_sort_list[0], to_sort_list[i]

heap_size -= 1

max_heap(to_sort_list, heap_size, 0)

to_sort_list = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]

heap_sort(to_sort_list)

print to_sort_list

算法优劣分析

平均时间复杂度O(nlogn)

空间复杂度O(1)

7. 快速排序(QuikSort)

自然语言描述

对冒泡排序的有效改进。快速排序是一种不稳定的排序算法,即多个相同的值的相对位置也许会在算法结束时产生变动。

假设要排序的列表a[0],a[1]...a[n-1],首先任意选取一个数据(通常选用a[0])作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。整个排序过程可以递推进行,从而使整个列表变得有序。

更具体一些:

设置两个变量i、j,排序开始的时候:i=0,j=n-1;

以第一个数组元素作为关键数据,赋值给key,即key = a[0];

从j开始向前搜索 (顺序不能调转,因为你的比较对象是a[0]),即由后开始向前搜索,找到第一个小于key的值list[j],将list[j]赋值给list[i], 并令i++;

从i开始向后搜索,即由前开始向后搜索,找到第一个大于key的list[i],将list[i]赋值给list[j],并令j--;

重复第3、4步,直到i = j(下面有图解说明这一结束条件)。

Note:

3,4步中,没找到符合条件时,改变j、i的值,使得j = j-1,i = i+1,直至找到为止。

找到符合条件的值,进行交换的时候i, j指针位置不变,只交换所指的值。

i == j这一过程一定正好是i++或j--完成的时候,此时令循环结束。

不多说了,上图!

一趟比较的图解:

eadffef6c81c

字太丑凑活看

快速排序python实现

#-*-coding=UTF-8-*-

def quick_sort(a,left,right):

if len(a) <= 1 or right < 0 or left >= len(a) or right <= left:

return a

key = a[left]

i, j = left, right

while i < j:

while a[j] > key and i < j:

j -= 1

if a[j] < key and i < j:

a[i] = a[j]

i += 1

while a[i] < key and i < j:

i += 1

if a[i] > key and i < j:

a[j] = a[i]

j -= 1

a[i] = key

quick_sort(a, left, i-1)

quick_sort(a, i+1, right)

return a

list1 = [67,23,89,35,28,90,10,24]

left = 0

right = len(list1)-1

print(quick_sort(list1, left, right))

总结比较

eadffef6c81c

七大算法的比较

补充:

基于桶排序的思想

计数排序

class CountingSort:

def countingSort(self, A, n):

# Assume that there are only 1000 numbers

buckets = [0] * 1001

for num in A:

buckets[num] += 1

sorted_arr = []

for i in range(0, 1001):

if buckets[i] != 0:

sorted_arr += [i] * buckets[i]

return sorted_arr

基数排序

class RadixSort:

def radixSort(self, A, n):

# write code here

bucket = [[] for l in range(10)]

for i in range(4):

for number in A:

value = (number//10**i)%10

bucket[value].append(number)

temp = []

for j in range(len(bucket)):

while bucket[j]:

temp.append(bucket[j].pop(0))

A = temp

return A

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值