冒泡排序

冒泡排序

算法简介

  冒泡排序(BubbleSort)算法:比较相邻元素的大小,如果第一个元素大于第二个元素,则交换它们的位置,然后第二个元素与第三个元素比较,直到所有的元素比较完,冒泡出最小的元素。假设我们有n各元素,那么我们就要进行 n-1 次冒泡,n-1 个最小的元素已经冒泡出来,因此,最后剩下的一个元素也就处于它应当处于的位置。

  本篇文章主要是对冒泡排序进行优化,使其避免不必要的比较,以及泛型的实现,共分四个版本。

先来看一张直观的图:

第一版

  

static int FirstVersionBubbleSort(int[] array)
{
    int count = array.Length - 1;
    int number = 0;
    for (int i = 0; i < count; i++)
    {
        for (int j = 0; j < count - i; j++)
        {
            if (array[j] > array[j + 1])
            {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
            number++;
        }
    }
    return number;
}

外层循环用来控制冒泡的次数,内层循环用来比较相邻元素的大小。number 用来记录总共进行了几次比较,为后面的优化版本做对比。

执行 Main():

int[] firstArray = { 2, 1, 3, 4 };

Console.WriteLine("总共比较的次数:{0}", FirstVersionBubbleSort(firstArray));
foreach (var item in firstArray)
{
    Console.Write(item.ToString().PadRight(2));
}
总共比较的次数:6
1 2 3 4

总共比较了6次,分别是:

第一次冒泡出 4:

2-1:1 2 3 4

2-3:1 2 3 4

3-4 : 1 2 3 4

第二次冒泡 3:

1-2 : 1 2 3 4

2-3 : 1 2 3 4

第三次冒泡 2:

1-2 : 1 2 3 4

看一下上面的过程,就很容想到,当整个数列已经排好序后我们就没有必要再做多余的比较。用一个标志量来表示上次是否内层循环是否有元素交换,如果有则进行下一次循环,否则退出,因为所有元素已经有序。

第二版

  

static int SecondVersionBubbleSort(int[] array)
{
    int count = array.Length;
    bool change;
    int number = 0;
    do
    {
        count--;
        change = false;
        for(int i = 0; i < count; i++)
        {
            if(array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                change = true;
            }
            number++;
        }
    }while(change);
    return number;
}

执行Main():

int[] secondArray= { 2, 1, 3, 4 };

Console.WriteLine("
总共比较的次数:{0}", SecondVersionBubbleSort(secondArray));
foreach (var item in secondArray)
{
    Console.Write(item.ToString().PadRight(2));
}
总共比较的次数:5
1 2 3 4

总共比较了5次,分别是:

第一次冒泡出 4:

2-1:1 2 3 4

2-3:1 2 3 4

3-4 : 1 2 3 4

此时 change 为 true

第二次冒泡 3:

1-2 : 1 2 3 4

2-3 : 1 2 3 4

发现已经有序,change 为 false, 结束冒泡。再继续思考,事实上,第一次冒泡后,2、3、4 已经有序,因此下次循环完全没有必要再继续对其比较;现在我们增加一个位置变量来记录每次冒泡的最后交换的位置,下次比较的此处就可以了,避免对后面已经有序的元素重复进行比较。

static int ThirdVersionBubbleSort(int[] array)
{
    int count = array.Length - 1, index = 0;
    bool change;
    int number = 0;

    do
    {
        change = false;
        for (int i = 0; i < count; i++)
        {
            if (array[i] > array[i + 1])
            {
                int temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                change = true;
                index = i;  // 记录最后一次交换的位置
            }
            number++;
        }
        count = index;
    } while (change);
    return number;
}

执行 Main():

int[] thirdArray = { 2, 1, 3, 4 };

Console.WriteLine("
总共比较的次数:{0}", ThirdVersionBubbleSort(thirdArray));
foreach (var item in thirdArray)
{
    Console.Write(item.ToString().PadRight(2));
}
总共比较的次数:3
1 2 3 4

有上面的结果,可以看到,这次只进行了 3 次比较,分别如下:

总共比较了3次,分别是:

第一次冒泡出 4:

2-1:1 2 3 4

2-3:1 2 3 4

3-4 : 1 2 3 4

此时 index 为 0, count 为 0,结束冒泡。可见,这种方式,可以大大降低比较的次数。

第四版

如果说继承是为我们提供了代码重用,那么泛型则为我们提供了算法的重用。下面我们将实现一个泛型版本的冒泡排序。

public static int FourthVersionBubbleSort<T>(List<T> list) where T:IComparable<T>
{
    int count = list.Count - 1, index = 0;
    bool change;
    int number = 0;

    do
    {
        change = false;
        for (int i = 0; i < count; i++)
        {
            if (list[i].CompareTo(list[i + 1]) > 0)
            {
                T temp = list[i];
                list[i] = list[i + 1];
                list[i + 1] = temp;
                change = true;
                index = i;  // 记录最后一次交换的位置
            }
            number++;
        }
        count = index;
    } while (change);
    return number;
}

执行 Main():

List<string> stringList = new List<string> { "Banana", "Apple", "Orange" };
Console.WriteLine("总共比较的次数:{0}", FourthVersionBubbleSort(stringList));
foreach (var item in stringList)
{
    Console.Write(item.ToString().PadRight(10));
}
总共比较的次数:2
Apple     Banana    Orange

 结束语

  冒泡排序最优时间复杂度为(即所有元素有序,只需遍历一次即可) O(n),最差时间复杂度为 O(n^2), 即当所有元素为逆序时。 

原文地址:https://www.cnblogs.com/kanlei/p/3442384.html