You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

排序全相等整数数组时出现Stack overflow错误

快速排序处理全相等大数组时的栈溢出问题

我尝试对一个包含100000个全相等元素的数组进行排序,使用的快速排序代码如下:

public static void quickSortFirst(int[] arr, int left, int right) {
    if (left < right) {
      int pivotIndex = partition(arr, left, right);
      quickSortFirst(arr, left, pivotIndex - 1);
      quickSortFirst(arr, pivotIndex + 1, right);
    }
  }

  private static int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    int i = left + 1;
    int j = right;
    while (i <= j) {
      while (i <= j && arr[i] <= pivot) {
        i++;
      }
      while (i <= j && arr[j] > pivot) {
        j--;
      }
      if (i <= j) {
        swap(arr, i, j);
      }
    }
    swap(arr, left, j);
    return j;
  }

  private static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }

这段代码在排序1000和10000个元素时正常,但元素数量增至100000时会触发Stack overflow错误。我试过网上多种快速排序实现,只要处理100000个全相等元素都会出现这个问题。


问题根源

核心问题在于固定选第一个元素当基准 + 全相等元素的极端场景:
当所有元素都等于基准值时,partition方法会把所有元素都分到基准的同一侧——arr[i] <= pivot会让i一直走到right位置,最后j停在left处,交换后返回的pivotIndex就是left。这导致每次递归只会把右区间缩小1,左区间完全没变化,递归深度直接拉到O(n)级别。100000的递归深度远远超过JVM默认的栈容量(通常只有几千到几万),自然触发栈溢出。


解决方案

1. 优化基准值选择

别每次固定选第一个元素当基准,改用三数取中法(取左、中、右三个位置的元素,选中间值当基准)或者随机选基准,这样能让全相等元素场景下的partition结果尽量均分左右区间,把递归深度降到O(logn)。

示例(三数取中优化partition):

private static int partition(int[] arr, int left, int right) {
    // 三数取中选基准,避免固定选左边界的问题
    int mid = left + (right - left) / 2;
    // 调整左、中、右位置的元素,让中间值移到左边界
    if (arr[mid] < arr[left]) swap(arr, left, mid);
    if (arr[right] < arr[left]) swap(arr, left, right);
    if (arr[right] < arr[mid]) swap(arr, mid, right);
    swap(arr, left, mid);

    int pivot = arr[left];
    int i = left + 1;
    int j = right;
    while (i <= j) {
        while (i <= j && arr[i] <= pivot) {
            i++;
        }
        while (i <= j && arr[j] > pivot) {
            j--;
        }
        if (i <= j) {
            swap(arr, i, j);
        }
    }
    swap(arr, left, j);
    return j;
}

2. 尾递归优化

把最后一次递归调用改成循环,减少栈帧的累积。同时优先处理较小的区间,进一步压缩栈深度。

示例(修改quickSortFirst方法):

public static void quickSortFirst(int[] arr, int left, int right) {
    while (left < right) {
        int pivotIndex = partition(arr, left, right);
        // 优先处理较小的区间,减少栈深度
        if (pivotIndex - left < right - pivotIndex) {
            quickSortFirst(arr, left, pivotIndex - 1);
            left = pivotIndex + 1;
        } else {
            quickSortFirst(arr, pivotIndex + 1, right);
            right = pivotIndex - 1;
        }
    }
}

3. 针对全相等元素的特殊处理

在partition时先检查当前区间是否所有元素都等于基准,是的话直接返回中间位置,避免无效递归。适合明确有大量重复元素的场景。

示例:

private static int partition(int[] arr, int left, int right) {
    int pivot = arr[left];
    // 检查当前区间是否全是相等元素
    boolean allEqual = true;
    for (int k = left + 1; k <= right; k++) {
        if (arr[k] != pivot) {
            allEqual = false;
            break;
        }
    }
    if (allEqual) {
        return left + (right - left) / 2; // 返回中间位置,均分区间
    }

    int i = left + 1;
    int j = right;
    while (i <= j) {
        while (i <= j && arr[i] <= pivot) {
            i++;
        }
        while (i <= j && arr[j] > pivot) {
            j--;
        }
        if (i <= j) {
            swap(arr, i, j);
        }
    }
    swap(arr, left, j);
    return j;
}

内容的提问来源于stack exchange,提问作者Beren Şenbay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:43:23