Во-первых, если вы собираетесь использовать произвольный доступ к списку, вы должны выбрать реализацию списка, которая эффективно это поддерживает. Из Javadoc на LinkedList:
Все операции выполняются, как и следовало ожидать для двусвязного
список. Операции, которые индексируют в списке, будут проходить по списку из
начало или конец, в зависимости от того, что ближе к указанному индексу.
ArrayList более экономичен и намного быстрее для произвольного доступа. На самом деле, поскольку вы заранее знаете длину, вы можете даже использовать простой массив.
Алгоритмам: давайте начнем с простого: как бы вы сгенерировали все подмножества размера 1? Вероятно, так:
for (int i = 0; i < set.length; i++) {
int[] subset = {i};
process(subset);
}
Где процесс - это метод, который делает что-то с набором, например, проверяет, является ли он "лучше", чем все подмножества, обработанные до сих пор.
Теперь, как бы вы расширили это для работы с подмножествами размера 2? Какова взаимосвязь между подмножествами размера 2 и подмножествами размера 1? Ну, любое подмножество размера 2 можно превратить в подмножество размера 1, удалив его самый большой элемент. Иными словами, каждое подмножество размера 2 можно сгенерировать, взяв подмножество размера 1 и добавив новый элемент, больший, чем все другие элементы в наборе. В коде:
processSubset(int[] set) {
int subset = new int[2];
for (int i = 0; i < set.length; i++) {
subset[0] = set[i];
processLargerSets(set, subset, i);
}
}
void processLargerSets(int[] set, int[] subset, int i) {
for (int j = i + 1; j < set.length; j++) {
subset[1] = set[j];
process(subset);
}
}
Для подмножеств произвольного размера k обратите внимание, что любое подмножество размера k можно превратить в подмножество размера k-1 путем измельчения самого большого элемента. То есть все подмножества размера k могут быть сгенерированы путем генерации всех подмножеств размера k-1, и для каждого из них и каждого значения, большего, чем наибольшее в подмножестве, добавьте это значение к набору. В коде:
static void processSubsets(int[] set, int k) {
int[] subset = new int[k];
processLargerSubsets(set, subset, 0, 0);
}
static void processLargerSubsets(int[] set, int[] subset, int subsetSize, int nextIndex) {
if (subsetSize == subset.length) {
process(subset);
} else {
for (int j = nextIndex; j < set.length; j++) {
subset[subsetSize] = set[j];
processLargerSubsets(set, subset, subsetSize + 1, j + 1);
}
}
}
Тестовый код:
static void process(int[] subset) {
System.out.println(Arrays.toString(subset));
}
public static void main(String[] args) throws Exception {
int[] set = {1,2,3,4,5};
processSubsets(set, 3);
}
Но прежде чем вызывать это на больших наборах, помните, что число подмножеств может расти довольно быстро.