This question already has answers here:
Why does flowing off the end of a non-void function without returning a value not produce a compiler error?
(11 answers)
Closed 4 years ago.
On an assignment I had to make a recursive binary search algorithm output the index instead of True/False without modifying the parameters. I had a really tough time but after resorting to semi-trial-and-error I stumbled upon this mess:
#include <iostream>
#include <math.h>
#include <climits>
using namespace std;
int BinarySearch(int arr[], int len, int target) {
int temp = 0;
int mid = len/2;
if (len <= 0) return INT_MIN; // not found
if (target == arr[mid]){
return mid; // found
}
if (target < arr[mid]){
temp = BinarySearch(arr, mid, target);
}
else {
temp = mid+1 + BinarySearch(arr+mid+1, len-mid-1, target);
}
}
I have literally no idea why it works, even after running it through a visualizer. It's very sensitive to the code being changed and I can't get it to output -1 when it fails to find the target so I made it at least always output a negative number instead.
I don't really need it fixed, I just want to know how it even works since seemingly none of the recursive call's outputs are even used. Thanks.
It is undefined behaviour (see e.g. Why does flowing off the end of a non-void function without returning a value not produce a compiler error?).
The compiler appears to return temp by chance, likely because it is the first local variable declared inside the function. Returning temp would fix it.
As far as I understand you want to return -1, if the target is not found and the index of the target otherwise. In
if (len <= 0) return INT_MIN; // not found
you are returning INT_MIN, if the target is not found. You need to change it to
if (len <= 0) return -1; // not found
Since your function returns an int value, it has to return something on each patch. You can fix it by adding the return at the end of the function:
if (target < arr[mid]){
temp = BinarySearch(arr, mid, target);
}
else {
temp = mid+1 + BinarySearch(arr+mid+1, len-mid-1, target);
}
return temp;
}
BinarySearch returns the index of target in the current arr. Since the current arr often doesn't begin with index 0, you're adding and subtracting mid+1. You're also doing it, if the target was not found and BinarySearch returns -1. You have to fix the else part:
else {
int index(BinarySearch(arr+mid+1, len-mid-1, target));
temp = index == -1 ? -1 : mid + 1 + index;
}
Related
I was trying to solve this Leetcode problem - https://leetcode.com/problems/partition-equal-subset-sum/. I came up with a memoized solution that never got accepted because apparently it took too much time. Below is that solution.
class Solution{
public:
bool canPartition(vector<int>& nums){
int totalSum = 0;
for(auto value : nums)
totalSum += value;
if(totalSum%2 == 1)
return false;
return helper(nums, totalSum/2, 0);
}
private:
bool helper(vector<int> nums, int totalSum, int index){
if(totalSum == 0)
return true;
if(totalSum < 0)
return false;
if(index == nums.size())
return false;
// Check in the cache
pair<int, int> key = make_pair(totalSum, index);
if(cache.count(key)){
//cout << "Cache hit!\n";
return cache[key];
}
// Include this
bool include = helper(nums, totalSum-nums[index], index+1);
// Exclude this
bool exclude = helper(nums, totalSum, index+1);
cache[key] = include || exclude;
return cache[key];
}
map<pair<int, int>, bool> cache;
};
After trying for a while, I made a small change where instead of using "include" and "exclude" booleans, I just did the below and the time complexity improved so significantly that it dropped from ~1000ms to ~0ms. I am confused why did this happen? Why is using two booleans and then storing their result in a map so much slower than when not using them?
cache[key] = helper(nums, totalSum-nums[index], index+1) || helper(nums, totalSum, index+1);
Can anyone please enlighten me here? Pretty confused about it.
The first version calls helper twice - once for include and once for exclude. The second version, since it uses the logical-or operator, will not call the 2nd helper if the first helper is true. In other words, if include is true, the exclude call is not made because it will not change the result of the expression.
Another performance hit is the nums parameter to helper. You don't make any changes to it within the function, so you can pass it as const vector<int> &nums to avoid making an unnecessary copy of the entire content of the array. canPartition can also take its parameter as a const reference since you do not modify it.
I need to find the Index of the minimum number in an array using a recursive function in c++ the function can only get 2 parameters: the pointer to the array and the size of it.
int smallest(int arr[], int num);
I managed to do this but with a helper variable that is static and declared outside the function here is what I got:
static int flag = 0;
int smallest(int* arr, int num) {
if (flag == num - 1)
return flag;
if (arr[num - 1] > arr[flag]) {
return smallest(arr, num - 1);
} else {
flag++;
return smallest(arr, num);
}
}
Now my question is can I do this without the static variable or any other variable other than the num? here is what I got so far:
int smallest(int arr[], int num) {
if (arr != &arr[num - 1])
if (*arr < arr[num - 1])
smallest(arr, num - 1);
else
smallest(arr + 1, num);
return num - 1;
}
It doesn't return the index of the minimum value but it does get to its adress, the function ends when the array pointer address is the same as the address of the minimum value. how can I get it to return the index?
I'm a student and I'm pretty new to C++ I appreciate the help! thanks!
===
edit:
this is originally from a homework assignment but the constraint to not use external help variables or functions is mine! and I'm curious to know if its even possible.
Because this is obviously homework, I'm not going to reveal the ACTUAL answer in entirety, but I'll give some (hopefully) good advice.
With recursion, think first of what your end condition is. That should be an array of 1 element. You return the index 0 in that case because of the array you have, it's the only element, so return the index of it.
if(num == 1)
{
return 0;
}
So then how is that useful to you? Compare it to exactly one other element. That's how you break this down. Your array turns into "one element and MANY" or "It's just one element." If it's just one, return the only value. If it's many, call yourself recursively from the second element (index 1) onward:
int restOfArraySmallest = smallest(arr+1, num-1) + 1;
You need the +1 because you've offset where it starts from. But you know the value in restOfArraySmallest is the index into YOUR arr of the smallest value of everything except the first element. Because your recursive calls don't include the first element, just the rest.
Hopefully that's enough to get you the rest of the way.
Edit: Because the OP has responded and said it wasn't essential to their homework assignment, here's the entire function:
// Recursively finds the index of the smallest element of the passed-in array
int smallest(int* arr, int num)
{
if(num <= 1)
{
return 0; // If we're in the last element, the smallest is the only element
}
// More than one element, so find the least element in all the elements
// past the first element of the array
// Note: the index returned is offset from our current view of the array,
// so add +1 to any returned result so we can index directly from it
int restOfArraySmallest = smallest(arr+1, num-1) + 1;
// Compare the first element in the array to the smallest of the entire rest
// of the array:
if(arr[0] < arr[restOfArraySmallest])
{
// The first element is smaller, it's the smallest, so return that index
return 0;
}
else
{
// The element already found is smaller, so return that index. It's already
// correctly offset
return restOfArraySmallest;
}
}
And that's it. If there's duplicate entries for smallest, it will favor the LAST one.
The trick with recursion is to NOT keep external variables. What you pass as the arguments and RETURN BACK are all the information you have. For some algorithms, it's enough.
Note, if you use recursive functions with datasets big enough, you will eventually get a Stack Overflow. Not the website, the crash type. This algorithm is pretty light in that only one extra variable and the two arguments themselves get allocated each pass, but it adds up eventually.
So I'm pretty new to C++ and I'm trying to generate Fibonacci numbers using recursion right? Except when I try to index to the next value I need to increase the index, and the function won't let me increase it.
How I know, is that I've run it in debug mode and stepped over the function - it continuously loops inside the fibGen function with i staying at a constant value of 1 (When I call fibGen in my main function the parameters are (startingSeq, 1, 13), where startingSeq is another vector with values {1,1}
The code builds and compiles fine, but when I run it I get a memory alloc error, which is obv caused by the infinite loop it seems to have.
What have I done wrong? Why doesn't the 'i' increase?
I've tried increasing i by having i++ inside the recursive fibGen call (in the else statement), I've tried having i++ outside the function call, and I've tried what's in there right now, where I have i+=1 and then pass i through.
Also I didn't have the 'return fibSeq' down the bottom originally because it doesn't make sense to have it there, but I put it in because vscode wouldn't compile without it in there, saying that it could never reach the end of fibGen (which makes sense now, and when this problem's fixed I think it can be removed, but at the moment it's just there so that the program will compile)
std::vector<int> fibGen(std::vector<int> fibSeq, int i, int max){
if(fibSeq[i] >= max){
return fibSeq;
}else{
fibSeq.push_back(fibSeq[i] + fibSeq[i-1]);
i+=1;
fibGen(fibSeq, i, max);
}
return fibSeq;
}
The output should be a vector containing the Fibonacci sequence, and I'm getting a mem alloc error (described above)
Actually, your code kind of works. You are just handling the vector of results sub-optimally. With your declaration:
std::vector<int> fibGen(std::vector<int> fibSeq, int i, int max)
you will always pass copies of the vector around (and you are never using the returned value). Instead, what you probably want to do is to work on the same data. To do this, use a reference to a vector (denoted by &). Then, you do not need to return anything:
void fibGen(std::vector<int>& fibSeq, int i, int max) {
if (fibSeq[i] >= max) {
return;
}
else {
fibSeq.push_back(fibSeq[i] + fibSeq[i - 1]);
i += 1;
fibGen(fibSeq, i, max);
}
}
void main(void) {
std::vector<int> fib = { 1, 1 };
fibGen(fib, 1, 34);
for (auto i : fib)
std::cout << i << std::endl;
}
So, I could not reproduce the error, until I realized that in your actual code, you were doing this instead of the code you posted:
std::vector<int> fibGen(std::vector<int> fibSeq, int i, int max){
if(fibSeq[i] >= max){
return fibSeq;
}else{
fibSeq.push_back(fibSeq[i] + fibSeq[i-1]);
i+=1;
fibGen(fibSeq, i, max);
}
}
In Visual Studio 2010, at least, it compiled fine, but threw an error at runtime, which I believe is what you described. So I'm going to assume I reproduced it.
The reason this is throwing an error is because you are invoking C++'s infamous undefined behavior. Undefined behavior in C++ permits anything to happen, like not failing to compile, but throwing a runtime error.
The fix is simple, really. You just need to return a value in all execution paths. Or, simply put, just do this:
std::vector<int> fibGen(std::vector<int> fibSeq, int i, int max){
if(fibSeq[i] >= max){
return fibSeq;
}else{
fibSeq.push_back(fibSeq[i] + fibSeq[i-1]);
i+=1;
return fibGen(fibSeq, i, max);
// Notice we are returning the value of the recursive call to fibGen().
// We can do this because we have already modified the vector the way we need to,
// so just simply returning the value is fine
}
}
Of course, you can take Nico Schertler's suggestion instead too:
void fibGen(std::vector<int>& fibSeq, int i, int max) {
if (fibSeq[i] >= max) {
return;
}
else {
fibSeq.push_back(fibSeq[i] + fibSeq[i - 1]);
i += 1;
fibGen(fibSeq, i, max);
}
}
It should be noted that not returning a value from a void function is not undefined behavior (that I'm aware), but actually how void's intended to work, so this function is fine not returning a value.
There are 2 possible ways that I am familiar with while returning a boolean/integer value from a recursive function that defines is the operation carried out was a success or not.
Using static variables inside the recursive function. Changing values in the recursive calls and then returning the final value once everything is done.
Passing the result variable by reference to the recursive function and then manipulating its values in the function and then checking if the value corresponds to the result or not.
void Graph::findPath(string from, string to)
{
int result = 0;
if (from == to) cout<<"There is a path!"<<endl;
else
{
findPathHelper(from, to, result);
if (result) cout<<"There is a path!"<<endl;
else cout<<"There is not a path!"<<endl;
}
}
void Graph::findPathHelper(string from, string toFind, int &found)
{
for (vector<string>::iterator i = adjList[from].begin(); i != adjList[from].end(); ++i)
{
if (!(toFind).compare(*i))
{
found = 1;
break;
}
else
findPathHelper(*i, toFind, found);
}
}
Is there a better way to achieve this?
Thank You
I have changed your implementation to use a return value
bool Graph::findPathHelper(const string& from, const string& toFind)
{
for (vector<string>::iterator i = adjList[from].begin(); i != adjList[from].end(); ++i)
{
// I have assumed you comparison was incorrect - i.e. toFind == *i is that you want
// toFind == *i - The two strings are equal - Thus found
// or
// Recurse on *i - Have we found it from recursion
if (toFind == *i || findPathHelper(*i, toFind)) {
return true;
}
}
// We have searched everywhere in the recursion and exhausted the list
// and still have not found it - so return false
return false;
}
You can return a value in the recursive function and use that returned value for checking if it was success or not in subsequent calls.
Using static variable for this purpose may work but it's generally not a good IDEA and many consider it as bad practice.
Look into the below link which explains why we must avoid static or global variables and what kind of problems it could lead to during recursion.
http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/recursion2.html
Note: I do not have enough reputation still to make a comment; and therefore i have posted this as answer.
Im trying to create a recursive function that contains a vector of numbers and has a key, which is the number we are looking for in the vector.
Each time the key is found the function should display a count for how many times the key appears in the vector.
For some reason my recursive function is only returning the number 1 (disregard the 10 I was just testing something)
Here's my code:
int recursive_count(const vector<int>& vec, int key, size_t start){
if (start == vec.size())
return true;
return (vec[start] == key? 23 : key)
&& recursive_count(vec, key, (start+1));
}
int main() {
vector <int> coco;
for (int i = 0; i<10; i++) {
coco.push_back(i);
}
cout << coco.size() << endl;
int j = 6;
cout << recursive_count(coco, j, 0) << endl;
}
Not sure what you are trying to do, but as is - your function will return false (0) if and only if the input key is 0 and it is in the vector. Otherwise it will return 1.
This is because you are basically doing boolean AND operation. The operands are true for all values that are not 0, and the only way to get a 0 - is if it is in the vector - and the key is 0.
So, unless you get a false (0) along the way, the answer to the boolean formula is true, which provides the 1.
EDIT:
If you are trying to do count how many times the key is in vec - do the same thing you did in iterative approach:
Start from 0 (make stop condition return 0; instead of return true;)
Increase by 1 whenever the key is found instead of using operator&&, use the operator+.
(I did not give a direct full answer because it seems like HW, try to follow these hints, and ask if you have more questions).
To me it seems that a recursive function for that is nonsense, but anyway...
Think about the recursion concepts.
What is the break condition? That the current character being checked is not in the string anymore. You got that right.
But the recursion case is wrong. You return some kind of bool (what's with the 23 by the way?
The one recursion round needs to return 1 if the current element equals key, and 0 otherwise.
Then we only need to add up the recursion results, and we're there!
Here's the code
int recursive_count(const vector<int>& vec, int key, size_t start) {
if (start >= vec.size()) {
return 0;
} else {
return
((vec[start] == key) ? 1 : 0) +
recursive_count(vec, key, start+1);
}
}
Since this is even tail-recursion, good compilers will remove the recursion for you by the way, and turn it into its iterative counterpart...
Your recursive_count function always evaluates to a bool
You are either explicitly returning true
if (start == vec.size())
return true;
or returning a boolean compare
return (vec[start] == key? 23 : key) // this term gets evaluated
&& // the term above and below get 'anded', which returns true or false.
recursive_count(vec, key, (start+1)) // this term gets evaluated
It then gets cast to your return type ( int ), meaning you will only ever get 0 or 1 returned.
As per integral promotion rules on cppreference.com
The type bool can be converted to int with the value false becoming
0 and true becoming 1.
With,
if (start == vec.size())
return true;
your function with return type int returns 1