常用算法——排序(一)

排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题。例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困难。同样,存储在计算机中的数据的次序,对于处理这些数据的算法的速度和简便性而言,也具有非常深远的意义。

排序分为:外部排序 and 内部排序

这里我们讨论内部排序

image_thumb1

冒泡排序法

冒泡排序法的基本思想是:对待排序记录关键字从后往前(逆序)进行多遍扫描,当发现相邻两个关键字的次序与排序要求的规则不符时,就将这两个记录进行交换。这样,关键字较小的记录将逐渐从后面向前面移动,就象气泡在水中向上浮一样,所以该算法也称为气泡排序法。

image_thumb3                                  image_thumb5

编写一个随机生成数列的函数<CreateData>:

#include <stdlib.h>
int CreateData(int arr[],int n,int min,int max) //创建一个随机数组,a保存生成的数据,n为数组元素的数量 
{
    int i,j,flag;
    srand(time(NULL));
    if((max-min+1)<n) return 0; //最大数与最小数之差小于产生数组的数量,生成数据不成功 
    for(i=0;i<n;i++)
    {
        do
        {
            arr[i]=(max-min+1)*rand()/(RAND_MAX+1)+min;
            flag=0;
            for(j=0;j<i;j++)
            {
                if(arr[i]==arr[j])
                    flag=1;
            }
        }while(flag);        
    }
    return 1;
}

编写冒泡排序法程序:

void BubbleSort(int a[],int n)
{
    int i,j,t;
    for(i=0;i<n-1;i++)
    {
        for(j=n-1;j>i;j--)
        {
            if(a[j-1]>a[j])
            {
                t=a[j-1];
                a[j-1]=a[j];
                a[j]=t;
            }
        }
        printf("第%2d遍:",i+1); 
        for(j=0;j<n;j++)
            printf("%d ",a[j]);
        printf("
");
    }
}

测试:

int main()
{
    int i,a[ARRAYLEN];
    for(i=0;i<ARRAYLEN;i++)
        a[i]=0;
    if(!CreateData(a,ARRAYLEN,1,100))
    {
        printf("生成随机数不成功!
");
        getch();
        return 1;
    }
    printf("原数据:"); 
    for(i=0;i<ARRAYLEN;i++)
        printf("%d ",a[i]);
    printf("
");
    BubbleSort(a,ARRAYLEN);
    printf("排序后:"); 
    for(i=0;i<ARRAYLEN;i++)
        printf("%d ",a[i]);
    printf("
");
    getch();
    return 0;   
}

结果:

image_thumb8

我们发现,在第三遍时我们就已经排序完毕,可是还是进行了第四第五遍,所以为了提升冒泡排序法的效率,可对BubbleSort函数进行改进,当在某一遍扫描时,发现数据都已经按顺序排列了,就不再进行后继的扫描,而结束排序过程。

该进就是对其添加了一个标志位,当已经排序好之后,就加上标志位不再排序。

void BubbleSort1(int a[],int n)
{
    int i,j,t,flag=0;        //flag用来标记是否发生交换
    for(i=0;i<n-1;i++)
    {
        for(j=n-1;j>i;j--)
        {
            if(a[j-1]>a[j])//交换数据 
            {
                t=a[j-1];
                a[j-1]=a[j];
                a[j]=t;
                flag=1;
            }
        }
        printf("第%2d遍:",i+1); 
        for(j=0;j<n;j++)
            printf("%d ",a[j]);
        printf("
");
        if(flag==0)    //没发生交换,直接跳出循环
            break;
        else
            flag=0;
    }
}

快速排序法

快速排序使用分治策略来把待排序数据序列分为两个子序列,具体步骤为:
(1)从数列中挑出一个元素,称该元素为“基准”。
(2)扫描一遍数列,将所有比“基准”小的元素排在基准前面,所有比“基准”大的元素排在基准后面。
(3)通过递归,将各子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的子数列排序。

快速排序详细步骤:

以一个数组作为示例,取区间第一个数为基准数。
初始时,i = 0;  j = 9;   X = a[i] = 72image_thumb10
由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
从j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;
数组变为:
image_thumb11

i = 3;   j = 7;   X=72
再重复上面的步骤,先从后向前找,再从前向后找。
从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++
从i开始向后找,当i=5时,由于i==j退出。
此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。
数组变为:
image_thumb12
可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。

/// <summary>
        /// 对数组dataArray中索引从left到right之间的数做排序
        /// </summary>
        /// <param name="dataArray"></param>
        /// <param name="left">要排序数组的开始索引</param>
        /// <param name="right">要排序数组的结束索引</param>
        private static void QuickSort(int[] dataArray,int left,int right)
        {
            if (left < right)
            {
                int x = dataArray[left];//基准数 小的放左边 大的放右边
                int i = left;
                int j = right;//用来做循环的标志位

                while (true && i < j)
                {
                    //从右向左比较
                    while (true && i < j)
                    {
                        if (dataArray[j] <= x) //找到了一个比基准数 小于等于的数字 应该把它放在x的左边
                        {
                            dataArray[i] = dataArray[j];
                            break;
                        }
                        else
                        {
                            j--; //向左移动 到下一个数字 然后做比较
                        }
                    }

                    //从左向右比较
                    while (true && i < j)
                    {
                        if (dataArray[i] > x)
                        {
                            dataArray[j] = dataArray[i];
                            break;
                        }
                        else
                        {
                            i++;
                        }
                    }
                }

                //跳出循环 现在i=j i是中间位置
                dataArray[i] = x;//left-i-right

                QuickSort(dataArray, left, i - 1);
                QuickSort(dataArray, i + 1, right);
            }
        }

测试:

static void Main(string[] args)
        {
            int[] data = new int[] { 42, 20, 17, 27, 13, 8, 17, 48 };
            
            QuickSort(data,0,data.Length-1);

            foreach (var temp in data)
            {
                Console.Write(temp + " ");
            }

            Console.ReadKey();
        }

结果:

image_thumb14

因为可能数据量比较小 所以看不出前二种方法的区别 可以试着增加数据量比较一下

原文地址:https://www.cnblogs.com/moguwang/p/5304375.html