一些排序算法

学数据结构和算法的时候接触到了不少排序算法,在这里整理一下。
涉及到的排序算法有冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序

一、暴力排序(我也不知道叫什么,感觉很暴力就这样叫了)

主要思想是:
将数组的第一个数和接下来的所有数比较,若该数小于第一个数,则他俩交换位置,第一轮遍历交换完就能找出最小的值放在第一位,然后找数组的第二个数和后面的所有数比较,也是交换出在后面的数中最小的(在整个数组中就是第二小的)
依次类推,一直排完整个数组

void ForceSort(vector<int> &arr)
 {
     int temp,len=arr.size();
     for (int i = 0; i < len - 1; ++i)
     {
         for (int j = i + 1; j < len; ++j)
         {
             if (arr[i] > arr[j])
             {
                 temp = arr[j];
                 arr[j] = arr[i];
                 arr[i] = temp;
             }
         }
     }
 }

二、冒泡排序

主要是比较两两相邻的数,如果是左边比右边大,则交换,否则就跳过,每这样从数组的尾到头两两交换一轮,就能选出一个最小值,然后下一轮就不用再管这个最小值了(++i)
下面还使用了一个tag作为标志位,当某轮冒泡从尾到头都已经没有出现过交换,就证明已经排序好了,不用再进行比较了,直接退出循环。就是因为他能用tag作为一种优化,所以在某些不完全乱序的排序任务中,要比上面的暴力排序要好

void BubbleSort(vector<int>& arr)
 {
     int temp, len = arr.size();
     bool tag=true;
     for (int i = 0; (i < len - 1) && tag; ++i)
     {
         tag = false;
         for (int j = len - 1; j >= i+1; --j)//从最后面开始,执行完一次此循环则将最小的数排到了最前面
         {
             if (arr[j] < arr[j - 1])//如果前一个数大于后一个数,就交换位置
             {
                 temp = arr[j - 1];
                 arr[j - 1] = arr[j];
                 arr[j] = temp;
                 tag = true;
             }
         }
     }
 }

三、选择排序

将第一个数和后面的每一个数作比较,找出最小的数的下标(所以每次都需要遍历完才知道谁最小)。找到最小的时候,就交换他俩的位置。这样就选出最小值放在第一位了,然后找重复操作找次小值(++i)。直到排序完成
选择排序的迭代次数其实和暴力排序是一样的,但是选择排序减少了交换次数,不用说每次一比他小就换位置,而是找到最小值才换
(即将交换语句放在了第二个循环的外面)

void SelectSort(vector<int>& arr)
 {
     int temp,min_index, len = arr.size();
     for (int i = 0; i < len - 1; ++i)
     {
         min_index = i;//min_index记录最小值的下标
         for (int j = i + 1; j < len; ++j)
         {
             if (arr[min_index] > arr[j])//进入里面就说明能找到更小的值
             {
                 min_index = j;
             }
         }
         if (min_index != i)//找到了比原本当前位置i更小的值就交换
         {
             temp = arr[min_index];
             arr[min_index] = arr[i];
             arr[i] = temp;
         }
     }
 }

四、插入排序

把每个数和上一个相比,如果这个数小于上一个数就考虑将这个数往前插,至于插到什么位置,就看这个数的大小,他要和前面的每一个数相比,若是小于就一直往前走,直到排在某个比他小的数的后面(然后他后面的数就往后挪位置给他)。(如果比所有数小就排第一了)
这种算法如果倒序比较多的话,其实效率并不比选择算法高,
但如果是完全乱序,这是一种比较快的算法。

void InsertSort(vector<int>& arr)
 {
     int temp,j, len = arr.size();
     for (int i = 1; i < len; ++i)
     {
         if (arr[i] < arr[i - 1])
         {
             temp = arr[i];
             for (j = i-1; arr[j]>temp; --j)
             {
                 arr[j+1] = arr[j];
                 if (j == 0)
                 {
                     --j;
                     break;
                 }
             }
             arr[j+1] = temp;
         }
     }
 }

五、希尔排序

插入排序的升级版,插入排序是每次和他上一个比较,
希尔排序是第一轮和他前gap个比较,就是把arr[i]和arr[i-gap]每两个数当成一组插入排序,进行排序。(一共有gap组)
第二轮是gap缩小一倍,把arr[i-gap],arr[i],arr[i+gap],arr[i+2*gap]每四个数当成一组插入排序。(一共有gap组)

依次类推
一般来讲只要理解了插入排序,然后再插入排序的基础上加个外循环,再把原本的间隔1,改成gap即可

void HillSort(vector<int>& arr)
 {
     int temp, j, len = arr.size(),gap;
     gap = len;
     do
     {
          gap = gap/2;
          for (int i = gap; i < len; i+=gap)
          {
               if (arr[i] < arr[i - gap])
               {
                    temp = arr[i];
                    for (j = i - gap; arr[j] > temp; j-=gap)
                    {
                         arr[j + gap] = arr[j];
                         if (j == 0)
                         {
                             j-=gap;
                             break; 
                          }
                    }
                    arr[j + gap] = temp;
                }
          }
     } while (gap > 1);
 }

六、归并排序

主要思想是先将n长的数组无限二分,直到每份都只有两个数,然后将这两个数排序,重新回退到上一层(每份只有四个数的时候)再进行排序,在原来两个两个已有顺序的基础上,这是比较好排序的,排完后又回退到上一层
依次类推
用递归的思想比较好实现,但递归有点影响效率了

void merging(vector<int>::iterator begin1, vector::iterator<int> end1, vector::iterator<int> begin2, vector::iterator<int> end2)
 {
     auto t1 = begin1, t2 = begin2;
     vector t3;
     while (end1 > t1&& end2 > t2)
     {
         if (*t1 < *t2)
         {
              t3.push_back(t1);
              ++t1;
         }
         else
         {
             t3.push_back(t2);
             ++t2;
         }
     }
     while (t1 < end1)
     {
         t3.push_back(t1);
         ++t1;
     }
     while (t2 < end1)
     {
         t3.push_back(t2);
         ++t2;
     }
     t1 = begin1;
     for (int i = 0; i < t3.size(); ++i)
     {
         *t1 = t3[i];
         ++t1;
     }
 }
void MeregeSort(vector<int>::iterator begin, vector<int>::iterator end)
 {
     if (end-begin > 1)
     {
         auto new_begin1 = begin;
         auto new_end1 = begin+(end- begin)/2;
         auto new_begin2 = begin + (end - begin) / 2 ;
         auto new_end2 = end;
         MeregeSort(new_begin1, new_end1);
         MeregeSort(new_begin2, new_end2);
         merging(new_begin1, new_end1, new_begin2, new_end2);
     }
 }

七、堆排序

是简单选择排序的升级版。
可以理解为数组是按完全二叉树层叠排列的,从最右下小二叉树起,将三个数选出最大的一个排到根的位置。继续从右到左从上到下依次执行,执行完后,根部的位置即当前最大值(即数组里第0位)。然后将这个最大值放到完全二叉树最末位,(数组里最后一位),然后再依次类推选出第二大,第三大。。。最终完成排序。

void SwapTheMax(vector<int>& arr, int i, int len)
 {
     int max_index = i;
     if (2 * i + 1 < len && arr[max_index] < arr[i * 2 + 1])
         max_index = 2 * i + 1;
     if (2 * i + 2 < len && arr[max_index] < arr[i * 2 + 2])
         max_index = 2 * i + 2;
     if (max_index != i)
     {
         int temp = arr[i];
         arr[i] = arr[max_index];
         arr[max_index] = temp;
         SwapTheMax(arr, max_index, len);
     }
 }
 void HeapSort(vector<int>& arr)
 {
     int len = arr.size();
     while (len > 1)
     {
         //初始化堆
         for (int i = len / 2 - 1; i >= 0; --i)
         {
             SwapTheMax(arr, i, len);
         }
         int temp = arr[0];
         arr[0] = arr[len - 1];
         arr[len - 1] = temp;
         //交换堆顶和当前最后一个的位置
         --len;
     }
 }

八、快速排序

是一种冒泡算法的升级版!
每次取一个数组中的(当前第一位)一个数作为基准点,然后把数组里大于他的数放在右边,小于它的数放在左边,把左边和右边又看成是一个数组,进行递归操作。当数组里递归到只剩一个数的时候就返回。最终就能实现一个排序效果。

int CalculatePoint(vector<int>::iterator begin, vector<int>::iterator end)
 {
     int point = begin;
     auto e = end-1, b = begin;
     while (e - b > 0)
     {
         while (b < e)
         {
             if (e < point)
             {
                 int temp = *e;
                 *e = point;
                 b = temp;
                 break;
             }
             --e;
         }
         while (b < e)
         {
             if (b > point)
             {
                 int temp = *b;
                 *b = point;
                 *e = temp;
                 break;
             }
             ++b;
         }
     }
     return (b - begin);
 }
void QuickSort(vector<int>::iterator begin, vector<int>::iterator end)
 {
     if (end - begin > 1)
     {
         int point = CalculatePoint(begin, end);
         QuickSort(begin, begin + point+1);
         QuickSort(begin + point+1, end);
     }
 }

开始测评

生成999个测试数据

各排序算法耗时

改进后的希尔排序怎么会比插入排序还慢呢[旺柴]
headsort是后来加的,这里没测评

换了组数据再测

emm、、反正大致就这样吧,各排序算法也是受数据影响,排序速度有所偏差的

结论:
一些简单的排序比如冒泡排序暴力排序速度是比较慢的,选择排序的速度也不快,插入排序简单但是速度还算快,改进后的希尔排序有一定提升。归并排序用了递归,实测起来反而速度慢了,快速排序就算用了递归速度也很快(快速排序牛逼!)

最后一句话总结:
有STL库的sort在,写个屁的排序,溜了溜了

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注