查找数组实现包中的第 k 个最大元素

发布于 2024-08-04 01:48:43 字数 1213 浏览 8 评论 0原文

我们有一个放在袋子里的Comparable集合,并且必须找到第k最大的元素。我将集合复制到 HashSet 以删除重复项,然后将 HashSet 转换为要排序的数组,从而访问第 k 个元素。代码可以编译,但测试失败,我不知道出了什么问题。有什么想法吗?

public E kth(int k) {
    uniqueSet();
    Object[] uniqueArr = hashSet.toArray();
    startQuick(uniqueArr);
    return (E) uniqueArr[k - 1];
}

private void startQuick(Object[] uniqueArr) {
  int i = 0, j = uniqueArr.length;
  quickSort(uniqueArr, 0, j);
}

private void quickSort(Object[] uniqueArr, int i, int j) {
    int index = partition(uniqueArr, i, j);
    if (i < index - 1) {
        quickSort(rankBagArr, index - 1, j);
    }
    if (index < j) {
        quickSort(rankBagArr, i, index - 1);
    }
}

private int partition(Object[] uniqueArr, int i, int j) {
    E tmp;
    E pivot = (E) rankBagArr[(i + j) / 2];

    while (i <= j) {
        while (rankBagArr[i].compareTo(pivot) < 0) {
            i++;
        }
        while (rankBagArr[j].compareTo(pivot) > 0) {
            j--;
        }

        if (i <= j) {
            tmp = (E) rankBagArr[i];
            rankBagArr[i] = rankBagArr[j];
            rankBagArr[j] = tmp;
            i++;
            j--;
        }
    }
    return i;
}

We have a collection of Comparables held in a bag and have to find the kth largest element. I copied the collection to a HashSet to remove duplicates, then converted the HashSet to an array to be sorted and consequently the kth element accessed. The code compiles, but fails the testing, and I can't figure out what's wrong. Any ideas?

public E kth(int k) {
    uniqueSet();
    Object[] uniqueArr = hashSet.toArray();
    startQuick(uniqueArr);
    return (E) uniqueArr[k - 1];
}

private void startQuick(Object[] uniqueArr) {
  int i = 0, j = uniqueArr.length;
  quickSort(uniqueArr, 0, j);
}

private void quickSort(Object[] uniqueArr, int i, int j) {
    int index = partition(uniqueArr, i, j);
    if (i < index - 1) {
        quickSort(rankBagArr, index - 1, j);
    }
    if (index < j) {
        quickSort(rankBagArr, i, index - 1);
    }
}

private int partition(Object[] uniqueArr, int i, int j) {
    E tmp;
    E pivot = (E) rankBagArr[(i + j) / 2];

    while (i <= j) {
        while (rankBagArr[i].compareTo(pivot) < 0) {
            i++;
        }
        while (rankBagArr[j].compareTo(pivot) > 0) {
            j--;
        }

        if (i <= j) {
            tmp = (E) rankBagArr[i];
            rankBagArr[i] = rankBagArr[j];
            rankBagArr[j] = tmp;
            i++;
            j--;
        }
    }
    return i;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

来世叙缘 2024-08-11 01:48:43

首先,这部分非常可疑:

  if (i < index - 1)
        quickSort(rankBagArr, index-1 ,j);
  if (index < j)
        quickSort(rankBagArr, i, index-1);

您不是说:

  if (i < index - 1)
        quickSort(rankBagArr, i, index-1);
  if (index + 1 < j)
        quickSort(rankBagArr, index + 1, j);

我不熟悉你的分区方法,所以我不知道这是否正确。我认为我理解它,并且检查起来看起来还不错,但是很容易出现一点点错误,如果不仔细研究,很难发现这些错误。

这是我最近用 C# 编写的一个分区方法 - 如果您愿意,您应该能够很容易地将其转换为 Java。

private static int Partition<T>(T[] array, int left, int right,
  IComparer<T> comparer) {
  // Pivot on the rightmost element to avoid an extra swap
  T pivotValue = array[right];
  int storeIndex = left;
  for (int i = left; i < right; i++) {
    if (comparer.Compare(array[i], pivotValue) < 0) {
      Swap(array, i, storeIndex);
      storeIndex++;
    }
  }
  Swap(array, right, storeIndex);
  return storeIndex;
}

static void Swap<T>(T[] array, int x, int y) {
  T tmp = array[x];
  array[x] = array[y];
  array[y] = tmp;
}

不只是使用 Arrays.sort

For a start this part is highly suspect:

  if (i < index - 1)
        quickSort(rankBagArr, index-1 ,j);
  if (index < j)
        quickSort(rankBagArr, i, index-1);

Don't you mean:

  if (i < index - 1)
        quickSort(rankBagArr, i, index-1);
  if (index + 1 < j)
        quickSort(rankBagArr, index + 1, j);

?

I'm not familiar with your approach to partitioning, so I don't know whether that's correct or not. I think I understand it, and it looks okay on inspection, but it's very easy to get off-by-one errors which are hard to see without careful study.

Here's a partition method I wrote in C# recently - you should be able to translate it into Java quite easily if you want to.

private static int Partition<T>(T[] array, int left, int right,
  IComparer<T> comparer) {
  // Pivot on the rightmost element to avoid an extra swap
  T pivotValue = array[right];
  int storeIndex = left;
  for (int i = left; i < right; i++) {
    if (comparer.Compare(array[i], pivotValue) < 0) {
      Swap(array, i, storeIndex);
      storeIndex++;
    }
  }
  Swap(array, right, storeIndex);
  return storeIndex;
}

static void Swap<T>(T[] array, int x, int y) {
  T tmp = array[x];
  array[x] = array[y];
  array[y] = tmp;
}

Any reason for not just using Arrays.sort though?

失眠症患者 2024-08-11 01:48:43

如果你想通过排序来解决问题,那么

  1. 使用 API 中的排序方法(Arrays.sort 或 Collections.sort)。重新发明轮子是没有意义的。
  2. 对集合的内容进行一次排序,而不是每次查找第 k 个元素时。

快速排序分区适用于查找第 k 个元素,而无需对整个集合进行排序 - 您进行分区,如果最低范围大于 k,则您经常将分区划分到较低范围,如果它小于 k,则转到较高范围并查找(k - 较低范围的大小)第一个元素。它比对整个集合进行排序具有更好的复杂性。您可以在此处阅读更多相关信息。

无论如何,您的方法具有名为 uniqueArr 的参数,但是您可以执行某些操作在rankBagArr 上执行。是拼写错误吗?您的代码中没有rankBagArr 的定义。

If you want to solve the problem by sorting, then

  1. Use sorting methods from API (Arrays.sort or Collections.sort). Reinventing the wheel is pointless.
  2. Sort contents of your collection once, not every time you look for k-th element.

The quicksort partitioning is good for finding k-th element without sorting entire collection - you partition, if lowest range is larger then k, you recurrently go with partition to lower range, if it's smaller then k, you go to higher range and look for (k - size of lower range)-th element. It has better complexity than sorting whole collection. You can read more about it here

Anyway, your methods have parameter named uniqueArr, but some operations you perform on rankBagArr. Is it a typo? There is no definition of rankBagArr in your code.

迟月 2024-08-11 01:48:43

希望您可以少一些操作(并提高性能),并更正您所看到的默认值...

从列表(ArrayList)开始,您可以要求对其进行排序(使用比较器和 Collections.List )。排序(列表))。然后你可以向下循环:

  • 如果发现新元素不等于,则记住最后一个元素,
  • 增加计数器,当前元素是你的目标
  • 当计数器达到 k 值时

May you could have a bit less of manipulations (and improve performance), and correct the default you are seeing...

Starting with a List (ArrayList), you could ask to sort it (using the comparator, and Collections.sort(list)). Then you could loop down and:

  • memorizing the last element
  • if you find the new element is not equals, increment a counter
  • when your counter reaches the k value, the current element is your target
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文