I have a sorted array which is rotated n times, n is unknown.Now I want to search an element using binary search in O(nlog n).I implemented the following code, it works fine.
But I think condition if((end-start)==1 ) can be skipped by making some modifications,
can any one suggest?
Eg of array 1 2 3 4 5
2 3 4 5 1 //rotated once
Code:
public static int srch(int a[],int start,int end,int key){
if(start>end)
return -1;
if((end-start)==1 ){
if(key==a[start])
return start;
else if(key==a[end])
return end;
else
return -1;
}
int mid = (start+end)/2;
if(a[mid]== key){
return mid;
}
else{
if(a[start] < a[mid] ){
//first half is sorted
if(key>a[mid]|| key <a[start]){
start= mid+1;
}else{
end =mid-1;
}
}else{
//second half is sorted
if(key>a[mid]){
start= mid+1;
}else{
end =mid-1;
}
}
return srch(a, start, end, key);
}
}
Any better/simple/more efficient solution?
Your solution fails for array {4,5,1,2,3} and key=4.
I think a modification in second part will solve the problem
else{
//second half is sorted
if(key>a[mid] && key<=a[end]){// modified condition
start= mid+1;
}else{
end =mid-1;
}
But I think condition
if((end-start)==1 ) can be skipped by
making some modifications, can any one
suggest?
I suppose this condition is not required at all.
Can u suggest a test case which fails for your modified code.
public static int srch(int a[],int start,int end,int key){
if(start>end)
return -1;
int mid = (start+end)/2;
if(a[mid]== key){
return mid;
}
else{
if(a[start] < a[mid] ){
//first half is sorted
if(key>a[mid]|| key <a[start]){
start= mid+1;
}else{
end =mid-1;
}
}else{
//second half is sorted
if(key>a[mid] && key<=a[high]){
start= mid+1;
}else{
end =mid-1;
}
}
return srch(a, start, end, key);
}
}
Your solution is running in O(log n), so there cannot be a more efficient solution. Maybe parts of your code could be optimized, but that will not effect the running time in terms of O. As you said, the code works and returns the correct values, therefore I'd say it's the correct answer for your interview question.
I haven't checked your code thoroughly for correctness, but your solution is O(log n). You cannot have a better solution algorithmically.
Related
I've been trying to learn algorithms and as part of this I have been trying to code binary search and the logic seems fine. The code doesn't terminate and the IDE stays idle forever. I don't understand what I'm doing wrong. Any help is appreciated. Thanks in advance!
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int no = 5;
System.out.print(binSearch(arr, no, 0, arr.length - 1));
}
private static boolean binSearch(int[] arr, int no, int start, int end) {
while(start <= end) {
int mid = (start + end) / 2;
if (arr[mid] == no) {
return true;
} else if (no > arr[mid]) {
binSearch(arr, no, mid + 1, end);
} else if(no < arr[mid]) {
binSearch(arr, no, start, mid - 1);
}
}
return false;
}
}
You are missing the return on the two recursive calls:
private static bool binSearch(int[] arr, int no, int start, int end) {
while(start <= end) {
int mid = (start + end) / 2;
if (arr[mid] == no) {
return true;
} else if (no > arr[mid]) {
return binSearch(arr, no, mid + 1, end);
} else if(no < arr[mid]) {
return binSearch(arr, no, start, mid - 1);
}
}
return false;
}
You could also consider writing it in a non-recursive loop.
okay so i think we review recursion a bit
binSearch(arr, num, start, end){
while (start<=end){
int mid = (start+end)/2;
if (arr[mid] == no) {
return true #when it finally matches return true
}
else if (arr[mid] > no) {
binSearch(arr, no, start, mid-1) #call binSearch for new value
}
}
}
Just to illustrate recursion, imagine we want some value B for an input A. Now imagine a node or some point as an origin that represents our input A. For every point or node that follows after A is some step we take towards finding the value B.
Once we find the value that we want, the structure of our approach can be illustrated as a single graph with one direction. A --> C --> --> D --> B
That is essentially how recursion works. Now first, lets take a look at your else if statement. When your parameters meet one of the else if conditions you make a call to your binSearch method.
What this does is basically create a new point of origin rather than working off the initial one. So lets say at iteration number 3 you finally meet your boolean condition and it returns true. But where does it return true to?
Only the last call or the most recent call that was made to binSearch. Lets call it iteration 2.
Now once the return value is made it simply moves on to the next block of code which brings us to your while loop. The only way your code can move on to the next block of code (which is returning the false value), is to break out of the while loop, ie. have your start value be greater than your end value.
But remember, we are on iteration 2. And iteration 2 was given the values for start and end that satisfied the while-loop so it loops again and whatever else-if statement iteration 2 landed on before the final iteration that returned true, it will keep repeating indefinitely.
The obvious solution as mentioned above is to put 'return' before the call is made as that will return all the way back to the original call to binSearch.
Also, the while loop is not necessary unless you are doing it without recursion.
I need to find if all paths of a binary tree that can end(which means all paths that starts from the root and end to a node that has only one child or none) have lengths that differ by no more than one.
My working solution work like this: the function longestPath finds the longest path, the function checkLengths traverse all nodes keeping track of the length of the paths and every time a node with only one child or none is found it checks if the difference between the length of the current path and the length of the longest path is more than 1.
This solution has complexity O(2n) because at worst every node has to be visited twice, once for the longestPath function and once for the lengthCheck function. I would like to improve the solution to O(n) but I'm having an hard time figuring out how to do so.
Edit: my solution is still O(n) but I would like to optimize it to find the solution by visiting each node only once and not twice.
int lengthCheckFlag=1;
int maxLength=-1;
void longestPath(Node n,int currentLength){
if(n==nullptr){
return;
}
if(n->left==nullptr && n->right==nullptr){
if(maxLength==-1){
maxLength=currentLength;
}
else{
if(currentLength>maxLength){
maxLength=currentLength;
}
}
}
longestPath(n->left,currentLength+1);
longestPath(n->right,currentLength+1);
}
void checkLengths(Node n,int currentLength){
if(n==nullptr){
return;
}
if(n->left==nullptr || n->right==nullptr){
if(abs(maxLength-currentLength)>1){
lengthCheckFlag=0;
}
}
checkLengths(n->left,currentLength+1);
checkLengths(n->right,currentLength+1);
}
bool lengthCheckWrapper(Node n){
if(n==nullptr){
return true;
}
longestPath(n,0);
checkLengths(n,0);
return lengthCheckFlag;
}
Code Update:
int maxP=-1;
int minP=-1;
void minmaxPaths(Node n,int currentLength){
if(n==nullptr){
return;
}
if(n->left==nullptr && n->right==nullptr){
if(maxP==-1){
maxP=currentLength;
minP=currentLength;
}
else{
if(currentLength>maxP){
maxP=currentLength;
}
if(currentLength<minP){
minP=currentLength;
}
}
}
minmaxPaths(n->left,currentLength+1);
minmaxPaths(n->right,currentLength+1);
}
bool lengthCheckWrapper(Node n){
if(n==nullptr){
return true;
}
minmaxPaths(n,0);
if(abs(minP-maxP)<=1){
return true;
}
return false;
}
Some remarks:
O(2n) is the same as O(n)
Your functions use different conditions for identifying the potential end of a path: one uses a && operator (wrong) and the other uses a || operator (correct)
One idea for an alternative algorithm is to make a breadth first traveral. This is interesting, since the constraint really means that all non-perfect nodes (i.e. that have at most one child) must appear in the bottom two levels of the tree.
By consequence, if we find 2 more levels after the first level where we find a non-perfect node, then we have a violation and can stop the traversal.
The down side is that it uses more memory.
Here is how it could be implemented:
int minmaxDepth(Node root) {
if (root == nullptr) {
return 1; // OK
}
std::vector<Node> level, nextLevel;
level.push_back(root);
int minDepth = INT_MAX;
int currDepth = 0;
while (level.size()) {
currDepth++;
nextLevel = {};
for (auto & parent : level) {
if (currDepth < minDepth &&
(parent->left == nullptr || parent->right == nullptr)) {
minDepth = currDepth; // Found a path with minimal length
}
if (parent->left != nullptr) {
nextLevel.push_back(parent->left);
}
if (parent->right != nullptr) {
nextLevel.push_back(parent->right);
}
if (nextLevel.size() && currDepth > minDepth) {
return 0; // Paths have lengths that differ more than 1
}
}
level = nextLevel;
}
return 1; // All nodes were visited: no violation found
}
There is no need to pre-compute the longest path. Compute all path lengths and on the fly,
store the first length,
if some other length differs by more than one, you are done;
else store the differing length, and if any other length differs from the two stored ones, you are done.
I studied that a recursive algorithm which checks if a certain value (key) is exists in a sorted given array, looks so:
int bin_search_rec(int key, int *a, int n)
{
if (n==0)
return 0;
if (key=a[n/2])
return key;
if (key<a[n/2])
return bin_search_rec(key,a,n/2);
else
return bin_search_rec(key,a+n/2 +1,n-n/2 -1 );
}
I also studied that this algorithm above can be modified - in order not only to check if a certain value exists in the array but also return the index of this value if it does exist - that way:
int bin_search_rec(int key, int *a, int n)
{
int pos;
if (n==0)
return -1;
if (key=a[n/2])
return key;
if (key<a[n/2])
return bin_search_rec(key,a,n/2);
else
pos= bin_search_rec(key,a+n/2 +1,n-n/2 -1 );
if (pos==-1)
return -1;
else
return pos+ n/2 +1;
}
I dont understand why in this modification we need to use the variable pos in order to find the index of the key.
Why is it wrong to just modify the algorithm that way instead:
int bin_search_rec(int key, int *a, int n)
{
if (n==0)
return -1;
if (key=a[n/2])
return n/2;
if (key<a[n/2])
return bin_search_rec(key,a,n/2);
else
return bin_search_rec(key,a+n/2 +1,n-n/2 -1 );
}
public class insSort {
int i,j,key; //j=1
public void rec(int a[],int pos){
if(pos>a.length-1){
return;
}
key= a[pos];
i=pos-1;
while((i>=0)&&(a[i]>key)){//swapping
a[i+1]=a[i];
i--;
a[i+1]=key;
}
pos++;
rec(a,pos);//post order
}
can it be considered as insertion sort? or should it be in-order?
Is it a universal practice to use in-order for recursive algorithms?if so why is it so?
The example code in the question is a tail recursive version, which a compiler may optimize into a loop (no recursion). I converted the example code to C++ with some minor clean up. The initial call should be rec(1) (initial value of pos == 1).
class insSort
{
public:
int a[8];
void rec(int pos){
int i,value;
if(pos >= (sizeof(a)/sizeof(a[0])))
return;
value = a[pos]; // get value
i = pos-1;
while((i >= 0) && (a[i] > value)){ // shift up
a[i+1] = a[i];
i--;
}
a[i+1] = value; // insert value
pos++;
rec(pos);
}
};
public static int sqrt(int x) {
if( x == 0 || x == 1){
return x;
}
long start = 0;
long end = x;
while ( end-start > 1){
long mid = (int)(end + start) / 2;
long s = mid * mid;
if(s == x){
return (int)mid;
}
else if(s > x){
end = mid;
}
else {
start = mid;
}
}
return (int)start;
}
Above is the working code snippet. I have questions as below. Thank you in advance for helping. ;-)
While(end-start > 1) why we need 1 here? just because the return signiture is int?
If we change while loop from while(end-start > 1) to while(end > start), we have to make end = mid-1; and start = mid + 1, correct? Still one step move, i wonder if this is also due to return type is integer?
Why we cannot return end? or (int)(start+end)/2?? I saw almost 99% answer return to the left bound of binary search. I just want to know if return to right boundry or the middle one is fine?
The return type int simply means you are return a result which is of type int. It has nothing to do with the algorithm inside the method body. Below I have tried to answer your questions:
While(end-start > 1) is used to make sure end is always greater than start. if end-start is 1 or less than 1, that means you have reached end of binary search.
You can go ahead and change while(end-start > 1) to while(end > start), it will still work. Don't have to make end = mid-1 and start = mid+1.
It all depends where you answer lies. It can be different for each problem.
You should try the link below to understand binary search algorithm better.
http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch
Hope it helps.