通俗易懂经典排序算法(一)---Java和Python实现冒泡排序及算法优化详解

Python和Java冒泡排序

冒泡排序

1)基本概念

冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

通俗的讲,冒泡就是将元素两两之间进行比较,谁大就往后移动,直到将最大的元素排到最后面,接着再循环一趟,从头开始进行两两比较,而上一趟已经排好的那个元素就不用进行比较了

2) 实际运作过程

冒泡排序算法的运作如下:

  • 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

通俗的理解:

依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。

3) 实现思路

用二重循环实现,外循环变量设为i,内循环变量设为j。假如有n个数需要进行排序,则外循环重复n-1次,内循环依次重复n-1,n-2,...,1次。每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识,i的值依次为1,2,...,n-1,对于每一个i,j的值依次为0,1,2,...n-i 。

设数组长度为N:
1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
3.N=N-1,如果N不为0就重复前面二步,否则排序完成。

ps:实在理解不了的文章末尾后面的举例包含每一趟排序中每一次排序的变化。】

4) python实现

def bubble_sort(list):
    for j in range(len(list)-1,0,-1):
        # j表示每次遍历需要比较的次数,是逐渐减小的
        for i in range(j):
            if list[i] > list[i+1]:
                list[i], list[i+1] = list[i+1], list[i]
    return list

if __name__=="__main__":
    list=[6,5,4,3,2,1]
    print(u'冒泡排序')
    bubble_sort(list)
    print(list)

5) Java实现

public class BubbleSort {

	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int end = arr.length - 1; end > 0; end--) {
			for (int i = 0; i < end; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int imp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}


	// for test
	public static void main(String[] args) {
		
		int arr[] = {6,5,4,3,2,1}
		bubbleSort(arr);
		printArray(arr);
	}

}

6)性能(算法时间、空间复杂度、稳定性)分析

时间复杂度

若记录序列的初始状态为"正序",则冒泡排序过程只需进行一趟排序,在排序过程中只需进行n-1次比较,移动次数为0;反之,若记录序列的初始状态为"逆序",则需进行n(n-1)/2次比较,移动次数为3n(n-1)/2次。因此冒泡排序总的时间复杂度为O(n*n)。

  • 最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。)
  • 最坏时间复杂度:O(n^2)
  • 当原始序列杂乱无序时,冒泡排序的平均时间复杂度:O(n^2)。

空间复杂度

冒泡排序排序过程中进行两两交换时需要一个临时变量,所需额外空间为1,因此空间复杂度为O(1)。稳定

稳定性

冒泡排序在排序过程中,元素两两交换时,相同元素的前后顺序保持不变,所以冒泡排序是一种稳定排序算法

7) 冒泡排序算法总结

N个元素需要排序N-1轮;

第i轮需要比较N-i次;

N个元素排序,需要比较n(n-1)/2次

8)算法优化

冒泡排序法存在的不足及改进方法:
第一,在每趟排序的每次循环过程中,程序无法判断是否完成整体排序,比如数组或列表:[1,2,3,4,7,5,6],第一次循环将最大的数排到最后,此时列表已经都排好序了,就是不用再进行第二次、第三次...

为了解决这一不足,可设置一个标志位flag为False,如果元素之间交换了位置,将变量重新赋值为True,最后再判断,在一次循环结束后,变量如果还是为False,则break退出循环,结束排序。

# python优化
def bubble_sort(list):
    for j in range(len(list) - 1,0,-1):
        # j表示每次遍历需要比较的次数,是逐渐减小的
        flag = False
        for i in range(j):
            if list[i] > list[i + 1]:
                list[i], list[i + 1] = list[i + 1], list[i]
                flag = True
        if not flag:
            break
    return list

package 冒泡排序Java优化;
import java.util.Arrays;
/**
 * 冒泡排序改进版
 * 
 *
 */
public class BubbleSort {
    public static void BubbleSort(int[] arr) {
        
       
            int temp;//定义一个临时变量
            for(int end = arr.length - 1; end > 0; end--){//冒泡趟数,n-1趟
                boolean flag = false;
                for(int i = 0; i < end; i++){
                    if(arr[i] > arr[i + 1]){
                        temp = arr[i];
                        arr[i] = arr[i+1];
                        arr[i+1] = temp;
                        flag = true;
                    }
                }
                if(!flag){
                    break;//若果没有发生交换,则退出循环
                }
            }
        }
    }

// for test
    public static void main(String[] args) {
        int arr[] = new int[]{1,6,2,2,5};
        BubbleSort.BubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}



***举例说明:要排序数组:int[] arr={6,5,4,3,2,1};   

第一趟排序:

    第一次排序:6和5比较,6大于5,交换位置:  5,6,4,3,2,1

    第二次排序:6和4比较,6大于8,交换位置:5,4,6,3,2,1

    第三次排序:6和3比较,6大于3,交换位置:  5,4,3,6,2,1

    第四次排序:6和2比较,6大于2,交换位置:5,4,3,2,6,1

    第五次排序:6和1比较:6大于1,交换位置: 5,4,3,2,1,6

    第一趟总共进行了5次比较, 排序结果:      5,4,3,2,1,6

---------------------------------------------------------------------

第二趟排序:

    第一次排序:5和4比较,5大于4,交换位置:4,5,3,2,1,6

    第二次排序:5和3比较,5大于3,交换位置:  4,3,5,2,1,6

    第三次排序:5和2比较,5大于2,交换位置:4,3,2,5,1,6

    第四次排序:5和1比较,5大于1,交换位置: 4,3,2,1,5,6

    第二趟总共进行了4次比较, 排序结果:      4,3,2,1,5,6

---------------------------------------------------------------------

第三趟排序:

    第一次排序:4和3比较,4大于3,交换位置:  3,4,2,1,5,6

    第二次排序:4和2比较,4大于2,交换位置:3,2,4,1,5,6

    第三次排序:4和1比较,4大于1,交换位置:  3,2,1,4,5,6

    第二趟总共进行了3次比较, 排序结果:         3,2,1,4,5,6

---------------------------------------------------------------------

第四趟排序:

    第一次排序:3和2比较,3大于2,交换位置:2,3,1,4,5,6

    第二次排序:3和1比较,3大于1,交换位置:  2,1,3,4,5,6

    第二趟总共进行了2次比较, 排序结果:        2,1,3,4,5,6

---------------------------------------------------------------------

第五趟排序:

    第一次排序:2和1比较,2大于1,交换位置:  1,2,3,4,5,6

    第二趟总共进行了1次比较, 排序结果:  1,2,3,4,5,6

---------------------------------------------------------------------

最终结果:1,2,3,4,5,6

---------------------------------------------------------------------

每趟排序对应的比较次数如下图所示:

由此可见:N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数,即

for (int end = arr.length - 1; end > 0; end--) {
			for (int i = 0; i < end; i++) {
                //交换位置
                        }

}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值