数据结构算法 - 交换排序(冒泡、冒泡改进 and 快速排序)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37352167/article/details/84982888

数据结构算法 - 交换排序(冒泡、快速排序)

  • 交换排序

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

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

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

  • 冒泡排序

    · 最优时间复杂度: O ( n 2 ) O(n^2)
    · 最坏时间复杂度: O ( n 2 ) O(n^2)
    · 平均时间复杂度: O ( n 2 ) O(n^2)
    · 空间复杂度: 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 ( n 2 ) O(n^2)
    · 平均时间复杂度: O ( n log n ) O(n\log n)
    · 空间复杂度: O ( l o g n ) O(log n)
    · 稳定性:不稳定排序

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

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

    看 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 倍!这就是算法、思想的力量!

猜你喜欢

转载自blog.csdn.net/weixin_37352167/article/details/84982888
今日推荐