pythonnameerror所有情况_常见算法的python实现 - NameError - 博客园

常见算法的python实现

提到排序算法,常见的有如下几种:冒泡排序、选择排序、插入排序、快速排序、堆排序、归并排序、希尔排序;查找算法最常见二分查找。这些算法的时间复杂度如下:

算法名称

时间复杂度(一般情况)

冒泡排序

O(n2)

选择排序

O(n2)

插入排序

O(n2)

快速排序

O(nlog2n)

堆排序

O(nlog2n)

归并排序

O(nlog2n)

希尔排序

O(1.3n)

二分查找

O(log2n)

二分查找前提要求序列必须是有序的,所以下面我先介绍各排序算法的实现。注:默认按照升序排列

1、冒泡排序

冒泡排序的原理是从序列的第一个元素开始,与相邻的元素比较大小,如果左边的元素比右边的大,则交换两个元素的位置,依次类推,则一个循环完成后,序列中最大的元素就会到达序列的最右侧,通过多次同样的循环操作将序列从小到大排列。代码实现较简单:

1 def bubble(array):

2 for i in range(len(array)-1): # 仅需循环序列长度-1次即可完成排序

3 for j in range(len(array)-i-1): # 每次循环,右侧排好的元素不在进行比较

4 if array[j] > array[j+1]:

5 array[j], array[j+1] = array[j+1], array[j]

6 return array

除此之外,冒泡排序算法还有一个优化的写法。假设一个很大的序列,应用冒泡排序完成几次循环后,进行下一次循环发现并没有任何两个元素交换了位置,那说明什么呢?说明该序列已经变成了有序的序列,所以我们不需要在继续比较下去了,此时结束循环以便节省时间,变成代码实现如下:

1 def bubble_better(array):

2 for i in range(len(array)-1):

3 exchange = False # 每次大循环开始,将此标志设置为False

4 for j in range(len(array)-i-1):

5 if array[j] > array[j+1]:

6 array[j], array[j+1] = array[j+1], array[j]

7 exchange = True

8 if not exchange: # 如果此标志为False,说明本次循环没有元素交换位置,则结束整个循环

9 break

10 return array

2、选择排序

选择排序的原理是从序列中选择出最小的元素,放在序列的最左边,然后从剩下的元素中继续寻找最小的元素,放在最左边元素的右边,依次类推,右侧无序的元素越来越少,左侧有序元素越来越多,直至完成排序。代码实现如下:

1 def choice(array):

2 for i in range(len(array)-1):

3 min_index = i

4 for j in range(i+1, len(array)):

5 if array[min_index] > array[j]:

6 min_index = j

7 array[i], array[min_index] = array[min_index], array[i]

8 return array

3、插入排序

插入排序的原理是从序列的第二个元素开始循环至序列最后,每次循环从当前元素开始不断的与左边相邻的元素进行比较,如果左边的元素较大,则交换两个元素位置,这样序列左边逐步有序。代码实现如下:

1 def insert_sort(array):

2 for i in range(1, len(array)):

3 while i > 0 and array[i] < array[i-1]:

4 array[i], array[i-1] = array[i-1], array[i]

5 i -= 1

6 return array

4、快速排序

快速排序的原理是取序列的第一个元素为基准元素,指定两个指针分别指向序列的开始和结尾,拿结尾指针指向的元素与基准元素比较,如果该元素比基准元素大,则指针减一继续比较,一旦发现比基准元素小的元素,则将该元素放至序列的最左侧,然后拿开始指针指向的元素与基准元素比较,如果该元素比基准元素小,则指针增一继续比较,一旦发现比基准元素大的元素,则将该元素放至结尾指针指向的位置,重复上述过程,则比基准元素大的元素全部到了基准元素右侧,比基准元素小的元素全部到了基准元素左侧,然后对左右两部分继续上述过程,不难发现可应用递归完成最终的排序。

单次排序代码实现如下:

1 # 快速排序中的一次调整,即将第一个元素放在某个位置,使该元素右边的元素都比该元素大,左边的元素都比该元素小

2 def query_mid(array, left, right):

3 tmp = array[left]

4 while left < right:

5 while left < right and tmp <= array[right]:

6 right -= 1

7 array[left] = array[right]

8 while left < right and tmp >= array[left]:

9 left += 1

10 array[right] = array[left]

11 array[left] = tmp

12 return left

应用递归完成全部排序,代码实现如下:

1 # 应用递归实现快速排序

2 def _quick_sort(array, left, right):

3 if left < right:

4 mid = query_mid(array, left, right)

5 _quick_sort(array, left, mid-1)

6 _quick_sort(array, mid+1, right)

7 return array

5、堆排序

堆排序应用了二叉树这种数据结构-完全二叉树,分为大顶堆(父节点比任一孩子节点都大)和小顶堆(父节点比任一孩子节点都小),该排序算法的原理是首先将序列构造成大顶堆或者小顶堆,然后取下堆顶元素,将最后一个孩子节点放至堆顶位置,对堆进行调整,变成大顶堆或者小顶堆,通过不断的取下堆顶元素形成一个有序的序列。

堆得一次构造过程代码实现如下:

1 # 构建堆的调整过程

2 def shift(array, low, high):

3 tmp = array[low]

4 i = low

5 j = 2 * i + 1

6 while j <= high:

7 if j + 1 <= high and array[j] < array[j + 1]:

8 j += 1

9 if tmp < array[j]:

10 array[i] = array[j]

11 i = j

12 j = 2 * i + 1

13 else:

14 break

15 array[i] = tmp

排序过程代码实现如下:

1 def heap_sort(array):

2 n = len(array)

3 # 将array构造成大顶堆

4 for i in range(n//2-1, -1, -1):

5 shift(array, i, n-1)

6 # 从堆顶开始取数

7 for i in range(n):

8 array[0], array[n-1-i] = array[n-1-i], array[0]

9 shift(array, 0, n-i-2)

10 return array

6、归并排序

归并排序分为分解与合并两个过程,分解过程是将序列细分成两两元素的比较,然后在合并这些两两有序的元素,不断扩大合并,这种合并过程就是序列分成两部分,每一部分都是有序的,然后将这两部分合并成一个大的有序的序列。

一次归并排序的代码实现如下:

1 # 一次归并排序

2 def merge(array, low, mid, high):

3 tmp_list = []

4 i = low

5 j = mid + 1

6 while i <= mid and j <= high:

7 if array[i] < array[j]:

8 tmp_list.append(array[i])

9 i += 1

10 elif array[i] > array[j]:

11 tmp_list.append(array[j])

12 j += 1

13 else:

14 tmp_list.append(array[i])

15 i += 1

16 if j <= high:

17 for num in array[j:high+1]:

18 tmp_list.append(num)

19 if i <= mid:

20 for num in array[i:mid+1]:

21 tmp_list.append(num)

22 array[low:high+1] = tmp_list

23 return array

应用递归实现分解与合并的过程代码实现如下:

1 def _merge_sort(array, low, high):

2 if low < high:

3 mid = (low+high) // 2

4 _merge_sort(array, low, mid)

5 _merge_sort(array, mid+1, high)

6 merge(array, low, mid, high)

7 return array

7、希尔排序

希尔排序的原理是,针对待排序的序列,选定一个步长(初始步长一般为序列长度一半),拿出索引相差该步长位置的元素进行插入排序,然后步长减半,再次拿出索引相差该步长位置的元素进行插入排序,直至步长变为1。该排序代码实现如下:

1 def shell_sort(array):

2 gap = len(array) // 2

3 while gap >= 1:

4 for i in range(gap, len(array)):

5 tmp = array[i]

6 j = i - gap

7 while j >= 0 and array[j] > tmp:

8 array[j+gap] = array[j]

9 j -= gap

10 array[j+gap] = tmp

11 gap = gap / 2

12 return array

基于以上算法对序列进行排序后,就可以应用二分查找来查找相应的元素,每次拿序列的中间位置元素与被查找元素比较,如果中间位置元素比被查找元素小,则在中间元素右侧的序列中继续该方式查找;如果中间位置元素比被查找元素大,则在中间元素左侧的序列中继续该方式查找,通过该方式每次将范围缩短一半,大大缩短了查找时间,代码实现较简单,如下:

1 def half_find(array, data):

2 begin = 0

3 end = len(array) - 1

4 while begin <= end:

5 mid = (begin+end) // 2

6 if array[mid] == data:

7 return '%s founded at %s' % (data, mid)

8 elif array[mid] > data:

9 end = mid - 1

10 else:

11 begin = mid + 1

12 return 'not found'

各算法运行时间比较

这里构造了一个长度为10000的列表,打乱元素顺序,分别应用以上算法对该列表进行排序,通过一个计算运行时间的装饰器计算出各个排序算法所耗时间,装饰器代码如下:

1 # 计算算法执行时间装饰器

2 def use_time(func):

3 def use_time_inner(*args, **kwargs):

4 begin_time = time.time()

5 f = func(*args, **kwargs)

6 end_time = time.time()

7 global t

8 t = end_time - begin_time

9 print('use time(%s):%ss' % (func.__name__, t))

10 return f

11 return use_time_inner

使用该装饰器对各个算法进行装饰,执行以下代码得出时间:

1 # 生成序列

2 a = list(range(10000))

3 random.shuffle(a)

4 l1 = copy.copy(a)

5 l2 = copy.copy(a)

6 l3 = copy.copy(a)

7 l4 = copy.copy(a)

8 l5 = copy.copy(a)

9 l6 = copy.copy(a)

10 l7 = copy.copy(a)

11 l8 = copy.copy(a)

12

13 # 各排序算法执行

14 r1 = bubble_sort(l1) # 冒泡排序

15 r2 = bubble_better(l2) # 冒泡排序优化

16 r3 = choice_sort(l3) # 选择排序

17 r4 = insert_sort(l4) # 插入排序

18 r5 = quick_sort(l5) # 快速排序

19 r6 = heap_sort(l6) # 堆排序

20 r7 = merge_sort(l7) # 归并排序

21 r8 = shell_sort(l8) # 希尔排序

22

23 # 二分查找执行

24 r9 = half_find(r6, random.randint(0, len(a)))

运行结果如下,可以看到冒泡、选择、插入排序与其他排序算法时间相差还是很悬殊的!

use time(bubble_sort):15.331599950790405s

use time(bubble_better):14.486400127410889s

use time(choice_sort):7.4547998905181885s

use time(insert_sort):13.032400131225586s

use time(quick_sort):0.062399864196777344s

use time(heap_sort):0.062400102615356445s

use time(merge_sort):0.062400102615356445s

use time(shell_sort):0.07799983024597168s

use time(half_find):0.0s

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值