我目前正在用 Java编写快速排序算法来对随机的整数数组进行排序,然后使用System.nanoTime()对它们进行计时.这些数组的大小是10的幂,从10 ^ 3开始到10 ^ 7结束.此外,随机列表具有不同的属性.我正在整理纯粹的随机列表,列表中包含一些相同的值(很少),反向排序列表,排序列表和几乎排序的列表.

排序有效.它在数组上递归执行快速排序,直到它需要排序30个元素或更少的数组,在这种情况下,它执行插入排序.

一切都很好10 ^ 3和10 ^ 4但是一旦我达到10 ^ 5的值,它只会对随机,几个独特和随机列表进行排序,但在排序几乎排序和排序的列表时会产生堆栈溢出错误.

我不相信问题在于生成列表的方式,因为堆栈溢出发生在排序算法中(编译器引用的行是findPivot()方法中的第一行).此外,它通常会在崩溃之前对1到6个列表进行排序.因此,必须以某种方式使算法本身与几乎排序和排序的列表进行交互.此外,反向列表的生成涉及调用用于创建排序列表的代码(然后将其反转).

此外,我发现该问题不太可能只是由于某种原因,算法必须通过在几乎排序和排序的列表中递归而不是其他列表类型来调用数组的部分分区,如它可以对10 ^ 7值的随机列表进行排序,这将需要比具有10 ^ 5值的近似排序列表更多的分区.

我意识到它必须与这些列表类型如何与我的快速排序的递归相互作用有关,但如果有人可以阐明它会很棒.我把代码放到了完整的快速排序算法和下面的随机列表生成器中.

快速排序算法

/**
 * Performs a quick sort given the indexes of the bounds of an integer array
 * @param arr The array to be sorted
 * @param highE The index of the upper element
 * @param lowE The index of the lower element
 */
public static void quickSort(int[] arr,int highE,int lowE)
{       
    //Only perform an action if arr.length > 30,otherwise insertion sort [recursive base case])
    if (lowE + 29 < highE)
    {
        //Get the element and then value of the pivot
        int piVote = findPivot(arr,highE,lowE);
        int pivotVal = arr[piVote],storeE = lowE;

        //Swap the pivot and the last value.
        swapElements(arr,piVote,highE);

        //For each element in the selection that is not the pivot,check to see if it is lower than the pivot and if so,move it to the leftmost untouched element.
        for (int i = lowE; i < highE; i++)
        {
            if (arr[i] < pivotVal)
            {
                swapElements(arr,storeE,i);

                //Increment storeE so that the element that is being switched moves to the right location
                storeE++;
            }
        }

        //Finally swap the pivot into its proper position and recrusively call quickSort on the lesser and greater portions of the array
        swapElements(arr,highE);                   
        //Lesser
        quickSort(arr,storeE - 1,lowE);
        //Greater
        quickSort(arr,storeE + 1);
    }
    else
    {
        insertSort(arr,lowE);
    }
}




/**
 * Finds the pivot element
 * @param arr The array to be sorted
 * @param highE The index of the top element
 * @param lowE The index of the bottom element
 * @return The index of the pivot.
 */
public static int findPivot(int[] arr,int lowE)
{
    //Finds the middle element
    int mid = (int) Math.floor(lowE + (highE - lowE) / 2);

    //Returns the value of the median of the first,middle and last elements in the array.
    if ((arr[lowE] >= arr[mid]) && (arr[lowE] >= arr[highE])) 
    {
        if (arr[mid] > arr[highE]) {return mid;}
        else {return highE;}
    }
    else if ((arr[mid] >= arr[lowE]) && (arr[mid] >= arr[highE])) 
    {
        if (arr[lowE] > arr[highE]) {return lowE;}
        else {return highE;}
    }
    else 
    {
        if (arr[lowE] > arr[mid]) {return lowE;}
    }

    return mid;
}




/**
 *Performs an insertion sort on part of an array
 * @param arr The array to be sorted.
 * @param highE The index of the top element.
 * @param lowE The index of the low element.
 */
public static void insertSort(int[] arr,int lowE)
{
    //Sorts elements lowE to i in array,with i being gradually incremented.
    for (int i = lowE + 1; i <= highE; i++)
    {
        for (int j = i; j > lowE; j--)
        {
            if (arr[j] < arr[j - 1])
            {
                swapElements(arr,j,j-1);
            }
            else {break;}
        }
    }
}

随机列表发电机

/**
 * Creates a random list
 * @param arr The array to place the list inside of
 */
public static void randomList(int[] arr)
{
    //Places a random number at each element of the array

    for (int i = 0; i < arr.length; i++)
    {
        arr[i] = (int) Math.floor(Math.random() * RAND_MAX);
    }
}




/**
 * Creates a nearly sorted list of random numbers
 * @param arr the array to place the list inside of
 */
public static void nearSortList(int[] arr)
{
    //Creates a sorted list in arr
    sortList(arr);



    int swaps = (int) (Math.ceil(Math.pow((Math.log(arr.length)),2.0)));

    //The two values to be switched each time
    int a,b;

    //Performs a number of swaps equal to swaps [log(N) ^ 2] rounded up,with numbers switched no more than ln(N) places away
    for (int i = 0; i < swaps; i++)
    {
        a = (int) Math.floor(Math.random() * arr.length);

        b = (int) (a + Math.random() * 2 * Math.log(arr.length) - Math.log(arr.length));

        //Accounts for cases in which b is either greater or smaller than the array bounds
        if (b < 0)
        {
            b = -b;
        }
        else if (b >= arr.length)
        {
            b = -1 * (arr.length - b);
        }

        swapElements(arr,a,b);
    }
}




/**
 * Creates a random list with many unique values in
 * @param arr the array to place the list inside of
 */
public static void fewUniqueList(int[] arr)
{
    int[] smallArr = new int[(int) Math.floor(Math.pow(arr.length,9.0 / 10.0))];


    //Creates a smaller array of random values
    randomList(smallArr);



    //Fills the larger list up with values from the smaller list,ensuring aproximately N / N ^ (9/10) instances of each number in the array and ensuring,at most,there are N ^ (9/10) (rounded down) unique values in the large array
    for (int i = 0; i < arr.length; i++)
    {
        arr[i] = smallArr[(int) Math.floor(Math.random() * smallArr.length)];
    }
}




/**
 * Creates a reversed list of random numbers
 * @param arr the array to place the list inside of
 */
public static void reversedList(int[] arr)
{
    //Creates a sorted list in arr
    sortList(arr);




    //Switches each ith elements with its mirror on the other end of the array until the value of i reaches the middle of the array
    for (int i = 0; i < (int) (arr.length / 2.0); i++)
    {
        swapElements(arr,i,arr.length - 1 - i);
    }
}




/**
 * Creates a sorted list of random numbers using a merge sort
 * @param arr the array to place the list inside of
 */
public static void sortList(int[] arr)
{
    //Creates a random list in arr
    randomList(arr);

    Arrays.sort(arr);
}

编辑:[已解散]

编辑2:

我已经使用以下代码替换了基本的递归调用,以便仅在EJP建议时调用两个分区中的最小分区,但仍然没有解决问题.

if (storeE - 1 - lowE < highE - storeE + 1)
{
    //Lesser
    quickSort(arr,lowE);
    //Greater
    quickSort(arr,storeE + 1);
}
else
{
    //Greater
    quickSort(arr,storeE + 1);
    //Lesser
    quickSort(arr,lowE);
}

编辑3:

好吧,现在显而易见的是,递归深度远远大于我对于近乎排序和排序的列表所给予的信任.但现在我需要弄清楚为什么会出现这种情况,以及为什么随机列表的深度仅为10 ^ 7的值,但几乎排序和排序的列表的深度超过3000

解决方法

对于随机数组,您可以对大量数据进行分区.
但是对于一个(几乎)排序的数组,你一次主要是分割1个元素.

因此,对于排序数组,您的堆栈大小最终会与数组的大小相同,而对于随机数组,它更可能是该大小的对数.

因此,即使随机数组比近似排序的数组大得多,因此较小的一个引发异常也就不足为奇了,但较大的一个则不然.

修改你的代码

就修复方面而言,如EJP pointed out,您应首先执行较小的分区以限制堆栈增长.但这本身不会解决问题,因为Java doesn’t support tail-call optimization(嗯,它是可选的实现,因为我理解这个问题).

这里一个相当简单的解决方法是将函数抛入while循环,本质上是对尾调用优化进行硬编码.

为了更好地了解我的意思:

public static void quickSort(int[] arr,int lowE)
{
    while (true)
    {
        if (lowE + 29 < highE)
        {
            ...
            quickSort(arr,lowE);

            // not doing this any more
            //quickSort(arr,storeE + 1);

            // instead,simply set the parameters to their new values
            // highE = highE;
            lowE = storeE + 1;
        }
        else
        {
            insertSort(arr,lowE);
            return;
        }
    }
}

好吧,既然你已经有了基本的想法,那么看起来会更好(功能上与上面相同,只是更简洁):

public static void quickSort(int[] arr,int lowE)
{
    while (lowE + 29 < highE)
    {
        ...
        quickSort(arr,lowE);
        lowE = storeE + 1;
    }
    insertSort(arr,lowE);
}

这当然实际上并不首先做较小的一个,但我会留给你弄清楚(似乎你已经对如何做到这一点有了一个很好的想法).

这是如何工作的

对于一些弥补价值……

您当前的代码执行此操作:(缩进表示该函数调用内发生的事情 – 因此增加缩进意味着递归)

quickSort(arr,100,0)
   quickSort(arr,49,0)
      quickSort(arr,24,0)
         insertion sort
      quickSort(arr,26)
         insertion sort
   quickSort(arr,51)
      quickSort(arr,76,74)
         insertion sort

修改后的代码执行此操作:

quickSort(arr,0)
         break out of the while loop
         insertion sort
   lowE = 26
   break out of the while loop
      insertion sort
lowE = 51
run another iteration of the while-loop
    quickSort(arr,0)
      break out of the while loop
      insertion sort
lowE = 74
break out of the while loop
   insertion sort

增加堆栈大小

不确定你是否考虑过这个问题,或者它是否适用于你的参数,但你总是可以考虑increasing the stack size with the -Xss command-line parameter.

java – 为什么这种快速排序会导致几乎排序的列表和排序列表上的堆栈溢出?的更多相关文章

  1. html5使用canvas实现弹幕功能示例

    这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

  2. 前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)

    这篇文章主要介绍了前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. H5 canvas实现贪吃蛇小游戏

    本篇文章主要介绍了H5 canvas实现贪吃蛇小游戏,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. 使用canvas实现黑客帝国数字雨效果

    这篇文章主要介绍了使用canvas实现黑客帝国数字雨效果,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧

  5. ios – parse.com用于键,预期字符串的无效类型,但是得到了数组

    我尝试将我的数据保存到parse.com.我已经预先在parse.com上创建了一个名为’SomeClass’的类.它有一个名为’mySpecialColumn’的列,其数据类型为String.这是我尝试使用以下代码保存数据的代码:如果我运行这个我得到:错误:密钥mySpecialColumn的无效类型,预期字符串,但得到数组这就是我在parse.com上的核心外观:有谁知道我为什么会收到这个错误?

  6. ios – 上下文类型’NSFastEnumeration’不能与数组文字一起使用

    斯威夫特3,你会这样做吗?解决方法正如您所发现的,您不能使用as-casting将数组文字的类型指定为NSFastEnumeration.您需要找到一个符合NSFastEnumeration的正确类,在您的情况下它是NSArray.通常写这样的东西:

  7. ios – 获取资产目录文件夹中所有图像的数组

    在iOS中,是否可以获取资产目录文件夹中的图像数组?我不确定为什么会对此进行投票.我真的不知道从哪里开始.我的另一种方法是创建文件夹中所有文件的plist,但它似乎是多余的.我无法添加任何代码,因为我会添加什么?

  8. ios – 来自调试器的消息:由于内存问题而终止

    我的应用程序使用Geojson文件.我使用MapBoxSDK将MGLpolyline添加到地图中.但问题是我的文件太大,以至于应用程序崩溃并收到错误:来自调试器的消息:由于内存问题而终止.我在第一次循环时面对66234个对象.我试图将数组块化为新数组,但没有成功.请帮我解决问题.这是我在地图上绘制的代码,这里是我的testprojectongithubuseXcode8.1如果有任何不同的第三方可

  9. ios – Swift – 使用字典数组从字典访问数据时出错

    我有一个非常简单的例子,说明我想做什么基本上,我有一个字典,其值包含[String:String]字典数组.我把数据填入其中,但当我去访问数据时,我收到此错误:Cannotsubscriptavalueoftype‘[([String:String])]?’withanindexoftype‘Int’请让我知道我做错了什么.解决方法您的常量数组是可选的.订阅字典总是返回一个可选项.你必须打开它.更

  10. ios – 在Swift中使用“Map”创建两个数组的超集

    假设我有两个数组:我想组合两个数组,以便我得到一个输出我该怎么做呢?

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部