冒泡排序与两数交换的实现与优化

冒泡排序与两数交换的实现与优化

By: 大志若愚

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

过程图:

 

(一)标准版本:未优化版本

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace 冒泡排序
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] array = {9,1,2,3,4,5,6,7,8,0};
            array = BubbleSort01(array);
            foreach (int item in array)
            {
                Console.Write(item + " ");
            }
            Console.ReadKey();
        }
        /// <summary>
        /// 从大到小排序
        /// </summary>
        /// <param name="array">要进行冒泡排序的数组</param>
        /// <returns>排序后的数组</returns>
        private static int[] BubbleSort01(int[] array)
        {
            for (int i = 0; i < array.Length - 1; i++)
            {
                for (int j = 0; j < array.Length -1 - i; j++)
                {
                    if (array[j] < array[j + 1])
                    {
                        Swap01(ref array[j], ref array[j + 1]);
                    }
                }
            }
            return array;
        }
     // 实现二数交换,下面有其他方式的解释       
     
private static void Swap01(ref int num1,ref int num2) { int temp; temp = num1; num1 = num2; num2 = temp; } } }

  (2)设置单标志:优化版本一

  标志:用于标志每趟循环是否有数进行了交换,若无,则数组已实现排序 

private static int[] BubbleSort02(int[] array)
{
    bool flag = true;
    int n = array.Length - 1;
    while (flag)
    {
        flag = false;
        for (int i = 0; i < n - 1; i++)
        {
            if (array[i] < array[i + 1])
            {
                //Swap01(ref array[i], ref array[i + 1]);
                //Swap02(ref array[i], ref array[i + 1]);
                Swap03(ref array[i], ref array[i + 1]);
                flag = true;
            }
        }
        n--;
    }
    return array;
}

  flag用于标志每趟循环是否有数进行交换[即是否仍乱序],若为true,则标志数组还未有序。

(3)设置双标志:优化版本二

  标志一:用于标志每趟循环是否有数进行了交换,若flag=0,则数组已实现排序

  标志二:若循环有数进行了交换,标志最后一次交换的位置,标志之后的则已经有序

private static int[] BubbleSort03(int[] array)
{
    int k;
    int flag = array.Length - 1;
    while (flag > 0)
    {
        k = flag;
        flag = 0;
        for (int i = 0; i < k; i++)
        {
            if (array[i] < array[i + 1])
            { 
                Swap03(ref array[i], ref array[i + 1]);
                flag = i;
            }
        }            
    }           
    return array;
}

  flag用于标志每趟循环是否有数进行交换[即是否仍乱序],若为大于0,则标志数组还未有序。

  增加了k来标志在这之前仍需要排序位置,在k之后的则已经有序。

实现两数交互:

  我想在这里说下,实现两数交互的实现

(1)一般做法,利用第三方变量,同时因为int,enum,struct典型的值类型,需要进行引用传值

private static void Swap(ref int num1,ref int num2)
{       int temp = num1;
    num1 = num2;
    num2 = temp;
}

不使用第三方变量实现:解方程,异或

 (2)  解方程式:[名字可以随意,自己可以取个好记忆的]

 private static void Swap(ref int num1, ref int num2)
{
    num1 = num1 - num2;
    num2 = num1 + num2;
    num1 = num2 - num1;
}

但是这种方式引入了一个陷阱,如果num1是一个很大的正数而num2是一个很大的负数,那么num1-num2就会溢出。虽然在num2=num1+num2时可能会通过再一次溢出从而获得真实的num1的值,不推荐这种利用未定义行为的解法

如何理解这种解法?其实第一行是num1=num1-num2还是num1=num1+num2再或者是num1=num1*num2都可以,对应地在第二行把num2通过这个式子和num2本身的运算求出num1即可,再在第三行利用num1、num2的组合值以及原先的num1求解num2。明显地,使用*比+或-更容易溢出。理解后,完全不必死记硬背这三个式子,看成是解方程就不难了。

 (3)XOR[异或]

private static void Swap03(ref int num1, ref int num2)
{
    num1 = num1 ^ num2;
    num2 = num2 ^ num1;
    num1 = num2 ^ num1;
}

异或的性质:

  (1) X^0 = X 且 X^X = 0

  (2)交换律: X^Y  = Y^X 

  (3)结合律: (X^Y)^Z = X^(Y^Z)

  (4)自反性: X^Y^Y = X

  所以,这个也会出现一个问题:int x = 10; Swap(ref x, int ref x); 就会出现为x = 0的情况,对此我认为是因为由于对x的引用导致的,在交换过程中: num1 = num1 ^ num2;使得num1 = 0;而num2与num1都是对x的引用,Swap过程中,就没有保存临时计算产生的数据以及原数据,所以导致num1、num2都成为了0。

  交换两个相等的数据是没问题的,比如 int x = 8; int y = 8; Swap03(ref x ,ref y);完全是没有问题的。

  一般交换数据不会自己和自己交换数据,但是有时会出现例外,建议交换数据前进行判断是否相等,相等则比进行交换。

private static void Swap04(ref int num1, ref int num2)
{
    if(num1 != num2)
    {
        num1 = num1 ^ num2;
        num2 = num2 ^ num1;
        num1 = num2 ^ num1;
    }
}
原文地址:https://www.cnblogs.com/liushen/p/3375323.html