简体   繁体   中英

How to find the closest number to a given number

I have a vector:

std::vector<int> vec = { 10 , 115 ,15,  35 , 55 , 75 };

I need to find the two numbers closest to a given number. The first number would be less than the given number and hte second would be greater than the given number.

For example if the given number is 40 the two numbers would be 35 and 55 .

This is currently how I'm doing it:

For the smaller number:

  1. first I search all the numbers smaller than the given number.
  2. Then I search for the greatest of them all.

The same for the greater number.

I wanted to know if we can do it more efficently?

I wanted to know if we can do it more efficently ?

If the vector is not sorted, sorting it would be a waste wrt asymptotic complexity (for time), and you might just want to use two sequential for loops to find the less than and greater than values, respectively. Eg:

#include <iostream>
#include <utility>
#include <vector>

// Returns a pair of closest less than (first) and greater
// than (second) numbers in 'vec' to that of 'number'.
// If a less than or greater than number can't be found,
// the value of 'number' is returned for that slot.
//
// Time complexity:  O(N)
// Space complexity: O(1)
auto closestNumbers(const std::vector<int> &vec, int number) {
  std::pair closest_numbers{number, number};

  // Find closest below.
  for (auto e : vec) {
    if (e < number &&
        (e > closest_numbers.first || closest_numbers.first == number)) {
      closest_numbers.first = e;
    }
  }
  // Find closest above.
  for (auto e : vec) {
    if (e > number &&
        (e < closest_numbers.second || closest_numbers.second == number)) {
      closest_numbers.second = e;
    }
  }

  return closest_numbers;
}

int main() {
  const auto v = {10, 115, 15, 35, 55, 75};

  auto closest_numbers{closestNumbers(v, 40)};
  std::cout << closest_numbers.first << " " << closest_numbers.second
            << "\n"; // 35 55

  closest_numbers = {closestNumbers(v, 130)};
  std::cout << closest_numbers.first << " " << closest_numbers.second
            << "\n"; // 115 130

  closest_numbers = {closestNumbers(v, 1)};
  std::cout << closest_numbers.first << " " << closest_numbers.second; // 1 10
}

I would sort the array using something like std::sort . And that's it, the next is the closest higher the previous is the closest lower.

eg:

#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    int value = 1000;
    std::vector<int> vec = {10, 115, 15, 35, 55, 75};

    std::sort(vec.begin(), vec.end()); //sort

    //find the next greatest, in this case, *it would be 55
    auto it = find_if(vec.begin(), vec.end(), [value](int i) { return i > value; });

    //print the next larger if found, if not, print "Not found"
    it != vec.end() ? (std::cout << "Next larger: " << *it) : (std::cout << "Larger not found");
    std::cout << "\n";
    //print the next smaller if found, if not, print "Not found"
    *(it - 1) != value  && it != vec.begin() ? (std::cout << "Next smaller: " << *(it - 1)) : (std::cout << "Smaller not found");

}

What's left is to check if the value matches any element in the array, if so next smaller will be in *(it - 2) (if no repeated numbers are allowed), pending bounds check.

For number = 1 the program outputs:

Next larger: 10
Smaller not found

For number = 40 the program outputs:

Next larger: 55
Next smaller: 35

For number = 200 the program outputs:

Larger not found
Next smaller: 115

You could do your own algorithm by looking for larger/smaller values and saving the upper/lower limit of these values in temporaries along the way, ending up with a O(n) algorithm, wich, I gather, would be the best possible optimization apart from using some complex data structures.

I would argue that using standard algorithms would be preferable, they are safer and the gains in performance don't justify the hassle, but that's for you to decide.


This problem can be solved efficiently in O(log n) by using Binary Search Tree (BST) (the construction is in O(n log n).

In C++, we can use std::set which is implemented by Red–black_tree (kind of self-balancing binary search tree.).

void test_BST()
{
    std::vector<int> vec = { 10 , 115 ,15,  35 , 55 , 75 };
    int searched_key = 40;
    
    std::set<int> my_BST(vec.begin(),vec.end());

    auto lower_bound = my_BST.lower_bound(searched_key);
    auto upper_bound = my_BST.upper_bound(searched_key);

    cout<<" Value Before = " << *(lower_bound.operator--())<<endl;
    cout<<" Value After = " << *upper_bound<<endl;
}

The result is :

Value Before = 35
Value After = 55

You asked :

  1. first I search all the numbers smaller than the given number.
  2. Then I search for the greatest of them all.

I wanted to know if we can do it more efficently?

Answer:

Besides using a pre-sorted vector, then yes it can be done more efficiently, just iterate once over the vector and find both at once instead of iterating two times. ( here is how to do it with sorting )

(Note that this answer was posted prior to OP stating the input vector was not sorted)


The the vector is pre-sorted, eg by means of:

std::sort

then

std::lower_bound

can be used for the lookup.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM