【C#】快速排序堆栈溢出

快速排序堆栈溢出

在使用VS写快排的时候,发现了以下错误:
而且堆栈溢出这种错误不能被捕捉,真是令人头大。  ̄へ ̄
在这里插入图片描述

挖坑法

看一下代码,使用的是挖坑法。

  public void QuickSort_v1(ref int[] data,int Low, int High)
  {
       if (Low >= High)
       {
           return;
       }
       int pivot = data[Low];  //使用data[Low]作为枢纽元素
       int i = Low;
       int j = High;
       while (i < j)
       {
           while (data[j] >= pivot && i < j)
           { 
               j--;
           }

           data[i] = data[j];              //比枢纽小的向前移动 
           while (data[i] <= pivot && i < j)
           {
               i++;
           }

           data[j] = data[i];              //比枢纽大的向后移动

       }
       data[i] = pivot; 
       QuickSort_v1(ref data,Low, i - 1); //左区间快排
       QuickSort_v1(ref data,i + 1, High); //右区间快排 
   }

这个方法拿来普通排序没有问题,但是数量级稍微上升就有溢出的风险,当然这个得看脸,不幸的我一次中了。


不难看出,以上代码十分头铁,枢纽总是选择第一个数。如果测试的数据分布不均匀,左右区间分布不均匀,一个区间递归次数过多就会堆栈溢出,例如我把十万降序数,十万重复数和十万升序数分别带入,毫无例外全部暴毙。(ಥ_ಥ)

当然解决办法还是有的:

  1. 改进枢纽选择:进行三数选中,即比较首位、末位和中间位,选取一个中间值作为枢纽。(无法解决重复数)
  2. 在三数选中的同时,对重复数进行聚集:(解决溢出问题)
       public void QuickSort_v3(ref int[] data,int Low, int High)
        {
            if (Low >= High)
            {
                return;
            }
            //使用较为中间的数作为枢纽
            DealArray(Low, High);//作用:比较首位末位和中间位,选出中间大小的值与首位交换
            //到此可认为较为接近中间的值在第一位
            int i, j;
            int pivot;                    //使用data[Low]作为枢纽元素
            int pLow, pHigh;              //pLow与pHigh是记录枢纽两边 与枢纽值相同 的数量
            pivot = data[Low];
            i = Low;
            pLow = 0;
            j = High;
            pHigh = 0;
            while (i < j)
            {
                while (data[j] >= pivot && i < j)
                {
                    if (data[j] == pivot)
                    {
                    	//将与枢纽相同的值移动到一边
                        Swap(ref data,j, High - pHigh);//作用:交换两边的值
                        pHigh++;
                    }
                    j--;
                }

                data[i] = data[j];              //比枢纽小的向前移动 

                while (data[i] <= pivot && i < j)
                {
                    if (data[i] == pivot)
                    {
                        Swap(ref data,i, Low + pLow);
                        pLow++;
                    }
                    i++;
                }
                data[j] = data[i];              //比枢纽大的向后移动
            }
            data[i] = pivot;
            //把与枢轴相同的元素聚集起来  
            //将相同的值移动到中间
            for (int m = 0; m < pHigh; m++)
            {
                Swap( ref data, High - m, i + 1 + m);
            } 
            for (int n = 0; n < pLow; n++)
            {
                Swap( ref data , Low + n, i - 1 - n);
            }

            QuickSort_v3(ref data ,Low, i - 1 - pLow); //左区间快排
            QuickSort_v3( ref data ,i + 1 + pHigh, High); //右区间快排
        }

我的比较次数可以突破天际!
这种方法在挖坑的思想上,解决了堆栈溢出问题,但是由于能力不足,我写出来总是比较麻烦和不爽。

左右指针法

其实利用左右指针法的思想,能够比较好的解决这个问题

   private void QuickSort_v4(ref int[] data,int low, int high)
   {
       int i = low, j = high;
       int pivot = data[low + (high - low) / 2];

       while (i <= j)
       {
           while (data[i] < pivot)
           {
               i++;
           }
           while (data[j] > pivot)
           {
               j--;
           }
           if (i <= j)
           {
               Swap(ref data ,i, j);
               i++;
               j--;
           }//同时加减,能够使大多数情况使枢纽考中,减少递归次数
       }
       if (low < j)
           QuickSort_v4(ref data,low, j);
       if (i < high)
           QuickSort_v4(ref data,i, high);
   }

这种方法能够很好的平衡左右区间,就不会那么容易溢出。

其他

快排也可以和其他排序算法相结合去解决这类问题。

这里再标记一些其他大佬的文章:

https://blog.csdn.net/qq_36528114/article/details/78667034 ( 快速排序(三种算法实现和非递归实现))
https://blog.csdn.net/insistgogo/article/details/7785038 (三种快速排序以及快速排序的优化)


水平有限,请多包涵✧(≖ ◡ ≖✿

猜你喜欢

转载自blog.csdn.net/zigzagbomb/article/details/100852419
今日推荐