排序算法之冒泡排序及鸡尾酒排序

目录

一、排序算法的分类

二、冒泡排序

1.原始的冒泡排序

2.改进一步的冒泡排序

3.更进一步的冒泡排序

三、鸡尾酒排序

四、完整测试代码


一、排序算法的分类

在介绍排序算法之前,我们先根据时间复杂度,罗列主流的排序算法,作为一个了解。大致分为以下三类:

1.时间复杂度为O(n^2)的排序算法

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 希尔排序(希尔排序比较特殊,性能略胜于O(n^2),但比不上O(nlogn))

2.时间复杂度为O(nlogn)的排序算法

  • 快速排序
  • 归并排序
  • 堆排序

3.时间复杂度为线性的排序算法

  • 计数排序
  • 桶排序
  • 基数排序

排序算法根据稳定性,又可分为稳定排序和不稳定排序。稳定排序是值相同的元素在排序后仍然保持着排序前的顺序,不稳定排序则是相反。

二、冒泡排序

思想:相邻元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置(右侧为最大元素位置);小于等于右侧元素时,位置不变(由此可见冒泡排序是稳定排序)。

冒泡排序每一轮都要比较剩余的所有元素,总共遍历n-1轮(最后还剩一个元素时不用比较),时间复杂度为O(n^2)。

1.原始的冒泡排序

最原始的冒泡排序通过上述的描述,我们可以用双层循环来实现,第一层循环控制遍历的次数,第二层循环进行两两比较。代码实现非常简单,如下所示。

    /*
    原始冒泡排序
    前后两个元素进行比较,大的元素往后移
    冒泡排序是稳定的排序
     */
    public static void sort(int[] array){
        int temp;
        for(int j = 0; j < array.length - 1; j++)//控制进行几轮比较
            for(int i = 0; i < array.length-1-j; i++){//控制每一轮的比较
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                }
            }
    }

2.改进一步的冒泡排序

若我们在某一轮比较之后,序列已经是有序的,那么就不用继续进行排序。基于此思想,我们设置一个布尔值(isSorted),在每一轮遍历之前将其设为true,若某轮比较发生了元素的交换,则将该值设置为false。最后在未发生交换元素的那轮,该值为true,我们可以跳出循环,得到的便是排好序的序列。

    /*
    冒泡排序第二版改进(一轮有序后不必再排序)
    当有一轮排序后若已经有序,则剩下的几轮排序就不必执行了
     */
    public static void sort1(int[] array){
        int temp;
        boolean isSorted;
        for(int j = 0; j < array.length - 1; j++)//控制进行几轮比较
        {
            isSorted = true;//用isSorted假设每次序列都是有序的
            for(int i = 0; i < array.length-1-j; i++){//控制每一轮的比较
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                    isSorted = false;//若有元素交换,则是无序的还需进行下一趟遍历
                }
            }
            if(isSorted)
                break;
        }
    }

3.更进一步的冒泡排序

适用于序列中部分序列已经为有序的情况(如:3,4,2,1,5,6,7,8,前半部分无序,后半部分有序),我们无需每轮都比较它们,因此可以设置一个无序的边界值,记录交换的元素的位置。(个人觉得该方法不是非常实用(但书本上是这样介绍的),效果其实和在一次交换元素后就跳出内层循环一样)

    /*
    冒泡排序第三版改进(记录最后一次元素交换的位置,该位置为无序边界区)
    该方法相当于交换一次之后就退出当前循环,可以减少交换次数
     */
    public static void sort2(int[] array){
        for(int j = 0; j < array.length-1; j++)//控制进行几轮比较
        {
            boolean isSorted = true;//用isSorted假设每次序列都是有序的
            int sortBorder = array.length - 1;
            for(int i = 0; i < sortBorder; i++){//控制每一轮的比较
                int temp = 0;
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                    isSorted = false;//若有元素交换,则是无序的还需进行下一趟遍历
                    sortBorder = i;//把无序数列的边界更新为最后一次交换元素的位置
                }
            }
            if(isSorted)
                break;
        }
    }

三、鸡尾酒排序

首先声明,鸡尾酒排序从本质上来说也是冒泡排序。

鸡尾酒排序和原始的冒泡排序的区别是,该排序的比较和交换元素的顺序是双向的(即第一轮从左到右两两比较比较,第二轮从右到左比较,第三轮又从左到右比较......)。

如,我们给出要排序的序列为{2,3,4,5,6,7,8,1},若按照正常的冒泡排序,则每次排序都需要从左到右比较,将最后一个1放到最前面需要进行7轮比较,但若我们采用上面鸡尾酒排序的思想,则只需进行3轮排序:第一轮从左到右排序为{2,3,4,5,6,7,1,8},第二轮从右到左排序为{1,2,3,4,5,6,7,8},在两轮之后其实已经有序,但我们还要再进行一轮判断。因此总共只需3次即可,大大减少了比较次数。

鸡尾酒排序中,我们用外循环中套两个小循环(分别为奇数次和偶数次)来实现。

    //鸡尾酒排序,从左到右比较和从右到左比较轮流进行,适用于部分有序的情况如2,3,4,5,6,7,8,1
    public static void sort3(int[] array){
        int temp = 0;
        for(int i = 0; i < array.length/2; i++){
            boolean isSorted = true;
            //奇数轮,从左到右比较和交换
            for(int j = i; j < array.length - i - 1; j++){
                if(array[j] > array[j+1]){
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    isSorted = false;
                }
            }
            if(isSorted)
                break;
            isSorted = true;
            //偶数轮,从右到左比较和交换
            for(int j = array.length-i-1; j > i; j--){
                if(array[j] < array[j-1]){
                    temp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = temp;
                    isSorted = false;
                }
            }
            if(isSorted)
                break;
        }
    }

四、完整测试代码

import java.util.Arrays;

public class MySort {
    /*
    原始冒泡排序
    前后两个元素进行比较,大的元素往后移
    冒泡排序是稳定的排序
     */
    public static void sort(int[] array){
        int temp;
        for(int j = 0; j < array.length - 1; j++)//控制进行几轮比较
            for(int i = 0; i < array.length-1-j; i++){//控制每一轮的比较
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                }
            }
    }

    /*
    冒泡排序第二版改进(一轮有序后不必再排序)
    当有一轮排序后若已经有序,则剩下的几轮排序就不必执行了
     */
    public static void sort1(int[] array){
        int temp;
        boolean isSorted;
        for(int j = 0; j < array.length - 1; j++)//控制进行几轮比较
        {
            isSorted = true;//用isSorted假设每次序列都是有序的
            for(int i = 0; i < array.length-1-j; i++){//控制每一轮的比较
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                    isSorted = false;//若有元素交换,则是无序的还需进行下一趟遍历
                }
            }
            if(isSorted)
                break;
        }
    }

    /*
    冒泡排序第三版改进(记录最后一次元素交换的位置,该位置为无序边界区)
    该方法相当于交换一次之后就退出当前循环,可以减少交换次数
     */
    public static void sort2(int[] array){
        for(int j = 0; j < array.length-1; j++)//控制进行几轮比较
        {
            boolean isSorted = true;//用isSorted假设每次序列都是有序的
            int sortBorder = array.length - 1;
            for(int i = 0; i < sortBorder; i++){//控制每一轮的比较
                int temp = 0;
                if(array[i] > array[i+1]){
                    temp = array[i+1];
                    array[i+1] = array[i];
                    array[i] = temp;
                    isSorted = false;//若有元素交换,则是无序的还需进行下一趟遍历
                    sortBorder = i;//把无序数列的边界更新为最后一次交换元素的位置
                }
            }
            if(isSorted)
                break;
        }
    }

    //鸡尾酒排序,从左到右比较和从右到左比较轮流进行,适用于部分有序的情况如2,3,4,5,6,7,8,1
    public static void sort3(int[] array){
        int temp = 0;
        for(int i = 0; i < array.length/2; i++){
            boolean isSorted = true;
            //奇数轮,从左到右比较和交换
            for(int j = i; j < array.length - i - 1; j++){
                if(array[j] > array[j+1]){
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    isSorted = false;
                }
            }
            if(isSorted)
                break;
            isSorted = true;
            //偶数轮,从右到左比较和交换
            for(int j = array.length-i-1; j > i; j--){
                if(array[j] < array[j-1]){
                    temp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = temp;
                    isSorted = false;
                }
            }
            if(isSorted)
                break;
        }
    }


    public static void main(String[] args) {
        int[] array = new int[]{5,8,6,3,9,2,1,7};
        sort1(array);
        System.out.println(Arrays.toString(array));

        int[] array1 = new int[]{3,4,2,1,5,6,7,8};
        sort2(array1);
        System.out.println(Arrays.toString(array1));

        int[] array2 = new int[]{2,3,4,5,6,7,8,1};
        sort3(array2);
        System.out.println(Arrays.toString(array2));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值