Бинарный поиск в Java - обучение по-своему

1

Поэтому я пытаюсь научить себя, как реализовать бинарный поиск на Java, поскольку тема может быть отброшена, но у меня проблемы.

Понимаете, я склонен быть немного упрямым, и я бы предпочел не просто скопировать какую-то реализацию из Интернета.

Чтобы научить меня этому, я создал очень (ОЧЕНЬ) грубый маленький класс, который выглядит следующим образом:

public class bSearch{

/**
 * @param args
 */

public static void main(String[] args) {

    int one = 1;
    int two = 2;
    int three = 3;
    int four = 4;
    int five = 5;
    int six = 6;

    ArrayList tab = new ArrayList();

    tab.add(one);
    tab.add(two);
    tab.add(three);
    tab.add(four);
    tab.add(five);
    tab.add(six);

    System.out.println(bSearch(tab, 53));

}

@SuppressWarnings({ "rawtypes", "unchecked" })
public static int bSearch(ArrayList tab, int key) {

    if (tab.size() == 0)
        return 0;

    if ((int) tab.get(tab.size() / 2) == key)
        return key;

    ArrayList smallerThanKey = new ArrayList();
    ArrayList largerThanKey = new ArrayList();

    for (int i = 0; i < (tab.size() + 1) / 2; i++) {
        smallerThanKey.add(tab.get(i));
    }

    System.out.println("Smaller array = " + smallerThanKey);

    for (int i = (tab.size() + 1) / 2; i < tab.size(); i++) {
        largerThanKey.add(tab.get(i));
    }

    System.out.println("Larger array = " + largerThanKey);

    if (key < (int) tab.get(tab.size() / 2)) {
        bSearch(smallerThanKey, key);
    } else {
        bSearch(largerThanKey, key);
    }

    return key;
    }
}

Как вы можете видеть, это довольно далеко от красивой, но это ясно для того, чтобы noobie, как и я, чтобы понять, так или иначе.

Теперь, вот проблема; когда я подаю ему номер, который находится в ArrayList, он возвращает мне номер (ура!), но когда я подаю ему номер, который не находится в ArrayList, он по-прежнему возвращает мне мой номер (boo!),

У меня такое чувство, что моя ошибка очень незначительна, но я просто не вижу ее.

Или я ошибаюсь, и есть какая-то большая фундаментальная ошибка?

Ваша помощь глубоко оценена!

ОБНОВИТЬ

Спасибо за все конструктивные комментарии и ответы! Несколько полезных указателей в правильном направлении несколькими из вас. +1 для всех, кто ударил меня по правильному пути.

Следуя советам, которые вы дали, в основном касаясь моих рекурсий, не заканчивающихся должным образом, я добавил несколько return утверждений следующим образом;

if (key < (int) tab.get(tab.size() / 2)) {
     return bSearch(smallerThanKey, key);
} else {
     return bSearch(largerThanKey, key);
}

Теперь, что это делает, это на один шаг ближе к тому, чего я хочу достичь.

Теперь я получаю 0 если число нигде не найдено, и само число, если оно должно быть найдено. Таким образом, прогресс идет!

Однако это не сработает, если у меня есть поиск отрицательного числа или нуля (не то, что я знаю, почему я должен, но просто выбрасываю это там).

Есть ли исправление для этого, или я лаяю неправильное дерево в вопросе?

  • 0
    Не совсем связано, но вы действительно должны использовать дженерики ... Кроме того, вы пытались использовать отладчик?
  • 0
    Если вы новичок, как вы говорите, первым уроком: никогда не подавляйте предупреждения новичком, у вас могут возникнуть проблемы с этим
Показать ещё 4 комментария
Теги:
binary-search

3 ответа

1
Лучший ответ

РЕДАКТИРОВАТЬ

Точно так же, как быстрое решение заданного вами вопроса: вам нужно изменить последние несколько строк на следующие

  if (key < (int) tab.get(tab.size() / 2)) {
      return bSearch(smallerThanKey, key);
  } else {
      return bSearch(largerThanKey, key);
  }
}

Сказав это, позвольте мне отметить еще несколько проблем, которые я вижу здесь:

(а) вы можете использовать дженерики. Это использование ArrayList<Integer> а не просто ArrayList это избавит вас от всех этих приведений.

(b) Вместо того, чтобы вернуть найденное значение, вам лучше вернуть индекс в ArrayList, где находится значение, или -1, если он не был найден. Вот почему: возврат key предоставляет вызывающей стороне очень мало новой информации. Я имею в виду - вызывающий уже знал, какой ключ. Если вы вернете указатель к ключу, вы позволяете вызывающему абоненту узнать, был ли найден ключ или нет, и если он был найден там, где он находится в списке.

(c) Вы по существу копируете весь список каждый раз, когда вы bSearch() в bSearch(): вы копируете примерно половину списка в smallerThanKey и (примерно) наполовину в greaterThanKey. Это означает, что сложность этой реализации не O(log n) а вместо O(n).

(EDIT # 2)

Суммируя пункты (a), (b), (c) здесь, как можно написать этот метод:

public static int bSearch(ArrayList<Integer> tab, int key) {
  return bSearch(tab, 0, tab.size(), key);
}

public static int bSearch(ArrayList<Integer> tab, int begin, int end, int key) {
    int size = end - begin;
    if (size <= 0)
        return -1;

    int midPoint = (begin + end) / 2;
    int midValue = tab.get(midPoint);
    if (midValue == key)
        return midPoint;

    if (key < midValue) {
        return bSearch(tab, begin, midPoint, key);
    } else {
        return bSearch(tab, midPoint + 1, end, key);
    }
}

Как вы можете видеть, я добавил второй метод, который принимает begin и end параметры. Эти параметры позволяют методу, в котором часть списка должна выглядеть. Это намного дешевле, чем создание нового списка и копирование элементов. Вместо этого рекурсивная функция просто использует объект списка и просто вызывает себя с новыми значениями begin, end.

Возвращаемое значение теперь является индексом key внутри списка (или -1, если не найден).

  • 0
    почему проголосовали? он просто пытается помочь парню найти ошибку. Разве это не вопрос?
  • 0
    @RamanSingh Потому что это не решает проблему. Это просто означает, что метод будет возвращать 0 практически во всех случаях.
Показать ещё 4 комментария
1

Ваша рекурсия закончилась неправильно. В конце метода вы рекурсивно вызываете метод bSearch для левой или правой части массива. В этот момент вам нужно вернуть результат поиска рекурсивных вызовов.

Идея двоичного поиска: если ваш текущий узел не является key, посмотрите влево, если значение текущего узла больше, чем key или посмотрите вправо, если оно меньше. Поэтому, посмотрев туда, вам нужно вернуть результат поиска.

if (key < (int) tab.get(tab.size() / 2)) {
    return bSearch(smallerThanKey, key);
} else {
    return bSearch(largerThanKey, key);
}

Как побочное замечание, посмотрите на System.arraycopy и всегда полезно не подавлять предупреждения.

1

Я думаю, что проблема здесь:

if (key < (int) tab.get(tab.size() / 2)) {
    bSearch(smallerThanKey, key);
} else {
    bSearch(largerThanKey, key);
}

return key;

Вы просто отбрасываете результат своего рекурсивного вызова на bSearch и возвращаете key. Таким образом, на самом деле не удивительно, что вы возвращаете все количество, которое вы подаете в этот метод.

Помните, как должен работать бинарный поиск - если значение не находится в середине, верните результат поиска в левой/правой половине массива. Поэтому вам нужно что-то сделать с этими рекурсивными вызовами....

И с бинарным поиском вы действительно должны больше заботиться о том, чтобы найти место, которое вы ищете, а не его ценность - вы уже знаете это! Так что, по вашему мнению, бинарный поиск работал правильно, было немного ошибочно - поиск 1 должен был вернуть 0 - индекс/местоположение 1.

Кроме того, вам не нужно иметь дело с копированием массивов и т.д. - что операция, которая не нужна для поиска. Просто используйте параметры, чтобы указать, где начать/завершить поиск.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню