数据结构 - 交换排序(冒泡 and 快速排序)- Python 实现

数据结构 - 交换排序(冒泡 and 快速排序)- Python 实现

  • 交换排序

    交换排序的核心思想是通过交换位置来对序列进行排序(其实很多排序都会用到交换操作,但它们的思想,或者说思考角度并不是交换,而只是将交换作为实现的一种手段。)

    冒泡排序基本上是大学学习编程写的第一个“程序”,姑且看作算法的启蒙,而快速排序是工业界最受欢迎的排序算法之一,可以看做龙头老大。这“一头一尾”可谓将算法设计中思想的重要性体现的淋漓尽致。

    那么同样是基于交换思想的算法,为什么冒泡排序的平均时间复杂度是 O ( n 2 ) O(n^2) O(n2),而快速排序却是 O ( n log ⁡ n ) O(n\log n) O(nlogn) 呢?接下来让我们一探究竟。

    • 冒泡排序

      · 最优时间复杂度: O ( n 2 ) O(n^2) O(n2)
      · 最坏时间复杂度: O ( n 2 ) O(n^2) O(n2)
      · 平均时间复杂度: O ( n 2 ) O(n^2) O(n2)
      · 空间复杂度: O ( 1 ) O(1) O(1)
      · 稳定性:稳定排序

      基本思想是:遍历数组,对 相邻 的两个数进行比较,较大的数项表尾方向移动,而较小的数向表头方向移动。

      如果将顺序表的表头看作温泉的池底,表位看作温泉的页面,那么就相当于每次小的元素往水底移动,大的元素向页面冒出。正所谓“冒泡”…有点牵强…“Talking is cheap,show me the code!”,直接看 Python 代码吧。

      def bubble(array):
      
      	n = len(array)
      		# 从前向后两两比较,第 i 轮会将第 i 大的元素放到倒数第 i 个位置
      	    for i in range(n):
      	    	# 因为第 i 轮会确定第 i 大的元素,那么倒数 i 个元素已经有序
      	    	# 所以后面的比较可以截止到倒数第 i 个元素为止
      	    	# 相当于前 n-i 个元素没有经过排序,而后 i 个元素经过排序
          		for j in range(n-i-1):
          				# 比较相邻的两个元素
              			if array[j] > array[j+1]:
              				# 如果第二个数小,则交换位置
                  			array[j], array[j+1] = array[j+1], array[j]
      	return array
      

      接下来对冒泡排序耗时进行计算

      from random import randint
      import time
      
      input_data = [randint(0, 20000) for _ in range(10000)]
      
      start = time.clock()
      bubble(input_data)
      end = time.clock()
      
      # 此次排序耗费时间为: 8.10140057378438
      # 通过多次运行后耗时基本在 8(+/-0.2)s
      print(end - start)
      

      还有一个冒泡排序的改进版,在前 n-i 元素有序的情况下不做多余的比较:

      def bubble_improve(array):
      
      	n = len(array)
      	for i in range(n):
          	# flag = 0 代表没有发生过交换
          	flag = 0
          	for j in range(n-i-1):
              		if array[j] > array[j+1]:
                  		array[j], array[j+1] = array[j+1], array[j]
                  		# 如果发生交换那么 flag 就不为 0 
                  		flag += 1
      		# 内层循环遍历的是未排序列,如果遍历未排序列没有发生交换,
      		# 则证明没有排序的序列已经有序,那么可以直接退出
      		# 这样的改进可以减少对已经有序的比较次数
          	if not flag:
              		return
      
       	return array
      

      对此改进版本的耗时进行计算:

      from random import randint
      import time
      
      input_data = [randint(0, 20000) for _ in range(10000)]
      
      start = time.clock()
      bubble_improve(input_data)
      end = time.clock()
      
      # 此次排序耗费时间为: 8.810628180255106
      # 通过多次运行后耗时基本在 8.8(+/-0.4)s
      print(end - start)
      

      说是改进,实际上增加了很多次判断,而判断操作也是有一定耗时的,所以“改进版”也不一定好~

    • 快速排序

      · 最优时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
      · 最坏时间复杂度: O ( n 2 ) O(n^2) O(n2)
      · 平均时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
      · 空间复杂度: O ( l o g n ) O(log n) O(logn)
      · 稳定性:不稳定排序

      冒泡排序只能与相邻的元素进行比较,那么可不可以与非相邻元素比较呢?答案就是快速排序。

      基本思想:找元素在顺序表中的正确位置,对一个顺序表使用两个指针,分别指向表头与表尾,选取表头的元素作为被比较元素,移动两个指针进行比较,两个指针重合位置即为正确位置。一趟排序后确定被比较元素的正确位置,即此元素的左面所有元素都比他小,右面所有元素都比他大。而后将两边的元素作为两个顺序表再使用此方法递归实现。

      看 Python 代码:

      def sort(array, start, end):
      
      	# 判断此数组是否只有一个元素
      	if start >= end:
          		return
      	# start,end 记录表头表尾位置,low,high 进行移动
      	low, high = start, end
      	# 选出被比较元素
      	obj = array[end]
      	# 判断两指针是否重合(实际上是 high 在 low 的左面一个位置)
      	while low < high:
      		# 移动 low 指针,如果小于被比较元素则继续移动
              while low < high and array[low] <= obj:
                  low += 1
              # 如果大于被比较元素则将这个大的元素赋值给此时 high 所指元素
              #(high 此时指示的位置的元素已经是重复元素,之前已经被换位)
              else:
                  array[high] = array[low]
      		# 移动 high指针,如果大于被比较元素则继续移动
              while low < high and array[high] > obj:
                  high -= 1
              # 如果小于被比较元素则将这个小的元素赋值给此时 low 所指元素
              else:
                  array[low] = array[high]
      		# 将被比较元素赋值到正确位置
          array[low] = obj
      	# 根据表头与表尾位置以及此时被比较元素的正确位置拆分顺序表,
      	# 递归进行以上相同的操作
      	sort(array, start, low-1)
      	sort(array, low+1, end)
      

      接下来对快速排序耗时进行计算:

      from random import randint
      import time
      
      input_data = [randint(0, 20000) for _ in range(10000)]
      print(input_data)
      
      start = time.clock()
      sort(input_data, 0, len(input_data)-1)
      print(input_data)
      end = time.clock()
      
      # 此次排序耗费时间为: 0.02883855227136317
      # 通过多次运行后耗时基本在 0.02(+/-0.01)s
      print(end - start)
      

      可以看到快速排序的排序速度比冒泡排序快非常多,在 n=10000 时近乎 300 倍!这就是算法、思想的力量!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值