初级算法——排序(C语言)

这里只说关于冒泡排序,选择排序,直接插入排序,希尔排序,归并排序,快速排序的相关内容。

(1)冒泡排序

对于我的理解,冒泡排序像气泡一样逐渐依次向上浮动,方法就是逐一比较相邻的两个数,将小的调换在前面,一遍比较完后再进行相邻的数字比较,直到最后从小到大排好序。 

              图示如下:

                                

  最常见的冒泡排序是:

 #include<stdio.h>

void BubbleSort(int a[], int n)

{

     int i, j, temp;

     for(i = 0; i < n - 1; i++)

     {

             for(j = i + 1; j < n; j++)

             {

                    if(a[i] > a[j])

                    {

                          temp = a[j];

                          a[j] = a[i];

                           a[i] = temp;

                     }

              }

        }

   }

   int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        BubbleSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

   }

       

   优化后的:

     #include<stdio.h>

      void BubbleSort(int a[], int n)

      {

           int i, j, temp, flag;

           flag = 1;

           for(i = 0; i < n - 1; i++)

           { 

                   for(j = n - 1; j > i; j --)

                   {

                           flag = 0;

                           if(a[j - 1] > a[j])

                            {

                                temp = a[j - 1];

                                a[j - 1] = a[j];

                                a[j] = temp;

                                flag = 1;

                             }

                     }

             }

       } 

       

 int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        BubbleSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

   }

    

(2)选择排序:

先找到数组中最小的数字,再将该值与起始值进行交换,在进行第二轮遍历找到第二个最小值,再将该值与第二个值进行交换,直至所有数组从小到大排列好。

图示如下:

                   

#include<stdio.h>

void SelectSort(int a[], int n)

      int i, j, temp;

      for(i = 0; i < n - 1; i++)

      {

           min = i;

           for(j = i + 1; j < n; j++)

           {

                 if(a[j] < a[min])

                 {

                       min = j;

                  }

            }

            if(min != i)

            {

                 temp = a[min];

                 a[min] = a[i];

                 a[i] = temp;

             }

        }

}

int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

   }

(3)直接插入排序

直接插入排序算法的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。

图示如下:

                        

#include<stdio.h>

void InsertSort(int a[], int n)

{

      int i, j, temp;

      for(i = 0; i < n; i++)

      {

             if(a[i] < a[i - 1])

             {

                   temp = a[i];

                    for(j = i - 1; k[i] > temp; j --)

                    {

                             a[j + 1] = a[j];

                     }

                     a[j + 1] = temp;

              }

        }

  }

  

   int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

    }

(4) 希尔排序

之前的排序都是在相邻的元素之间进行操作,就算是插入也是将待插入的数字在数列中依次进行对比从而插入到正确的位置。而对于希尔排序来说,其将遍历的长度进行了扩张,比如第一轮遍历的长度为5,第二轮遍历长度为3,第三轮遍历长度为1,此时则遍历结束。

图示如下:

                  

#include<stdio.h>

void InsertSort(int a[], int n)

{

    int i, j, temp;

    int gap = n;

    do

    { 

           gap = gap /  3 + 1;

           for(i = gap; i < n; i++)

            {

                    if(a[i] < a[i - gap])

                     {

                            temp = a[i];

                             for(j = i - gap; k[j] > temp; i -= gap)

                              {

                                     k[j + gap] = k[j];

                               }

                                k[j + gap] = temp;

                       }

           }

      }while(gap > 1);

      

    int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

    }

         

(5)归并排序

归并排序就是利用归并的思想实现的排序方法。它的原理是假设初始序列有n个记录,则可以看成n个有序的子序列,每一个子序列长度为1,然后两两归并,得到n/2个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。

 图示如下:

                                                                                                                              

普遍版:

#include<stdio.h>

#define MAXSIZE 10

void merging(int *list1, int list1_size, int *list2, int list2_size)

{

      int temp[MAXSIZE];

      int i, j, k, m;

      while( i < list1_size && j < list2_size)

      {

             if(list1[i] < list2[j])

             {

                      temp[k++] = list1[i++];

              }

               else

               {

                        temp[k+=] = list2[j++];

                }

        }

        while(i < list1_size)

        {

               temp[k++] = list1[i++];

         }

         while(j < list2_size)

          {

                 temp[k++] = list2[i++];

           } 

           for(m = 0; m < (list1_size + list2_size); m++)  

           {

                  list1[m] = temp[m];

            }

}

void MergeSort(int a[], int n)

{

      if(n > 1)

      {

            int *list1 = k;

            int list1_size = n / 2;

            int *list2 = k + n / 2;

            int list2_size = n - list1_size;

            MergeSort(list1, list1_size);

            MergeSort(list2, list2_size);

            merging(list1, list1_size, list2, list2_size);

  }

  

   int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

    }

    优化版:

    #include<stdio.h>

    #include<stdlib.h>

    void MergeSort(int a[], int n)  //刚开始(初始化)left_max与right_min是相同的

    {

           int i, left_min, left_max, right_min, right_max; 

            int *temp = (int *)malloc(n * sizeof(int));

            for(i = 1; i < n; i *= 2)  /* i是步长,两两组合,从合并两个到合并四个,一直扩张合并范围直至所有的整合完毕*/

            { 

                   for(left_min = 0; left_min < n - i; left_min = right_max)

                    {

                           right_min = left_max = left_min + i;

                            right_max = left_max + i;

                            if(right_max > n)

                           {

                                   right_max = n;

                            }

                            next = 0;

                            while(left_min < left_max && right_min < right_max)

                             {

                                         if(a[left_min] < a[right_min])

                                         {

                                                 temp[next++] = a[left_min];

                                          }

                                          else

                                           {

                                                   temp[next++] = a[right_min];

                                            }

                             }

                             while(left_min < left_max)

                             {        

                                      a[--right_min] = a[--left_min];

                              }   

                              while(next > 0)

                               {

                                      a[--right_min] = temp[--next];

                                }

                                

                     }

             }

      } 

   int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

    }    

(6)快速排序

快速排序与冒泡排序的本质是一样的,都属于交换排序,但快速排序明显更高级,效率更高。直接上代码。

  普遍版:

   #include<stdio.h>

   void swap(int k[], int low, int high)

   {

         int temp;

         temp = k[low];

         k[low] = k[high];

         k[high] = temp;

    }

   int Partition(int k[], int low, int high)   //基准点的查找

,{   //小于基准点的再左边,大于基准点的在右边  

        int point;

         point = k[low];  //先将基准点定为较小的一个

          while(low < high)

          {

               while(low < high && k[high] >= point)

                {

                      high--;  // 当减至low = high 以及k[high] < point时,将high与low相交换

                 }

                swap(k, low, high);

                 while(low < high && k[low] <= point)

                {

                       low++;  // 当加至low = high 以及k[low] > point时,将low与high相交换

                 }

                  swap(k, low, high);

            }

         return low;  //在这一层层筛选后,选择当中位于中间的那个作为基准点

    }

   void qSort(int k[], int low, int high)

   {

            int point;

            if(low < high)

            {

                   point = Partition(k, low, high);  //基准点时快速排序的核心,这中间的操作很关键

                   qSort(k, low, point - 1);  //递归操作

                    qSort(k, point + 1, high);

             }

    }

   void QuickSort(int a[], int n)

    {

            qSort(k, 0, n - 1);  //直接调用前者即可

     }

    int main()

   {

        int i, a[5] = {5, 3, 6, 1, 7};

        SelectSort(a, 5);

        printf("排序后的结果:");

        for(i = 0; i < 5; i++)

        { 

                printf("%d", a[i]);

        }

        printf("\n\n");

        return 0;

    }

优化版:

One.优化基准点:

int Partition(int k[], int low, int high)  //之前的一个个查找难免会发生不必要的交换,浪费时间

   {

         int point;

         int m = low + (high- low) / 2;  // 定义一个m作为high与low的大致中间点

          if(k[low] > k[high])

          {

                 swap(k, low, high);

           }

         //通过判断m与high以及low的具体位置关系,减少不必要的操作来提高效率

           if(k[m] > k[high])

           {

                  swap(k, m, high);  

            }

            if(k[m]  > k[low])

            {

                  swap(k, m, low);

             }

          point = k[low];   //将基准点进行暂定,从而再进行以下操作时可以更快找出基准点

          while(low < high)

          {

               while(low < high && k[high] >= point)

                {

                      high--;

                 }

                swap(k, low, high);

                 while(low < high && k[low] <= point)

                {

                       low++;

                 }

                  swap(k, low, high);

            }

        return low; 

    }

Two .优化不必要的交换:       

int Partition(int k[], int low, int high)

   {

         int point;

         int m = low + (high- low) / 2;

          if(k[low] > k[high])

          {

                 swap(k, low, high);

           }

           if(k[m] > k[high])

           {

                  swap(k, m, high);

            }

            if(k[m]  > k[low])

            {

                  swap(k, m, low);

             }

          point = k[low];

          while(low < high) //之前用到的交换太过麻烦,可以直接用赋值来达到目的省去交换

          {

               while(low < high && k[high] >= point)

                {

                      high--;

                 }

                k[low] = k[high];

                 while(low < high && k[low] <= point)

                {

                       low++;

                 }

                  k[high] = k[low];

            }

       k[low] = point;

       return low;  

    }

            

Three.优化小数组时的方案:

 #define MAX_LENGTH_INSERT_SORT 7

void ISort(int a[], int n)  //调用直接插入排序的函数

{

      int i, j, temp;

      for(i = 0; i < n; i++)

      {

             if(a[i] < a[i - 1])

             {

                   temp = a[i];

                    for(j = i - 1; k[i] > temp; j --)

                    {

                             a[j + 1] = a[j];

                     }

                     a[j + 1] = temp;

              }

        }

  }

  void InsertSort(int a[], int low, int high)

  {

           ISort(k + low, high - low + 1)

  }        

  

 void qSort(int k[], int low, int high)  /*通过限定一定的范围进行选择性的排序,由于快速排序适合包含较多值的数组,因此当数组长度小于一定范围是用快速插入排序是最好的*/

 {

         int point;

         if(high - low > MAX_LENGTH_INSERT_SORT)

         {

                 point = Partition(k, low, high);

                 qSort(k, low, point - 1);

                 qSort(k, point + 1, high);

         }

          else

          {

                InsertSort(k, low, high);

           }

  }

 Four:优化递归操作(将多个递归变为一个递归)

 void qSort(int k[], int low, int high)

 {

         int point;

         if(high - low > MAX_LENGTH_INSERT_SORT)

         {

               while(low < high)  //将用两个递归变成用一个递归即可,其他的用尾递归则可。

               {

                         point = Partition(k, low, high);

                         if(point - low < high - point)

                         {

                               qSort(k, low, point - 1);

                                low = point + 1;

                          }

                           else

                           {

                                    qSort(k, point + 1, high);

                                    high = point - 1;

                           }

                }

         }

          else

          {

                InsertSort(k, low, high);

           }

  }

最后就是关于排序算法的应用了,以洛谷上的求第K小的数为例:

题目已经说明要用分治思想来做,可我不想这么干,于是在DevC++上写完后运行发现结果是正确的,可惜在洛谷上编译不通过,代码如下:

#include<stdio.h>
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;
            }
        }
    }
}
int main()
{
    int n, m;
    int a[10000];
    scanf("%d %d", &n, &m);
    int i; 
    for(i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    BubbleSort(a, n);
    for(i = 0; i < n; i++)
    {
        if(i == m)
        {
            printf("%d", a[i]);
            break;
        }
    }
    return 0;
}

哎,不听题目要求的解题最终的结果是惨痛的,只能老老实实用分治了,这里的排序换成了快速排序,代码如下:

#include<stdio.h>
long Partition(long first, long last);
void QuickSort(long first, long last , long k);
 
long arr[5000005] = { 0 };
 
int main()
{
    //输入n和k
    long n = 0;
    long k = 0;
    scanf("%ld%ld", &n,&k);
    //输入各个数
    for (long i = 0; i < n; i++)
        scanf("%ld",&arr[i]);
    //到k的快速排序
    QuickSort(0, n - 1, k);
    //输出第k大的值
    printf("%ld", arr[k]);
    return 0;
}
 
long Partition(long first, long last)
{
    long i = first, j = last, temp = 0;
    while (i < j)
    {
        //右侧扫描
        while (i < j && arr[i] <= arr[j])
            j--;
        if (i < j) {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;
        }
        //左侧扫描
        while (i < j && arr[i] <= arr[j])
            i++;
        if (i < j) {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            j--;
        }
    }
    return i;
}
 
void QuickSort(long first, long last, long k)
{
    if (first >= last)
        return;
    else 
    {
        long pivot = Partition(first, last);
        if(pivot > k)
            QuickSort(first, pivot - 1, k);
        else if(pivot < k)
            QuickSort(pivot + 1, last, k);
        else
        {
            printf("%ld", arr[pivot]);
            exit(0);
        }
    }
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值