Palindrome check of an Integer without converting it to a string

Note: The first idea while checking a palindrome that comes to our mind is converting the integer to a string and then reversing it. And then comparing that two strings. But that takes extra space. But if we can check without converting the integer to a string, then both time and space complexity decrease for sure.

The below code is for input -2^31 < x < 2^31.

Time complexity: O(ceil (size of the integer / 2 )). That means O(1) as constant time.
Space complexity: O(1).

#include <bits/stdc++.h>

using namespace std;

bool isPalindrome(int x)
{
    if(x<0 || (x!=0 && x%10==0)) return false;
    int num=x, reverted_num_half=0;
    while(reverted_num_half < num)
    {
        int residue=num%10;
        num=num/10;
        reverted_num_half=(reverted_num_half*10)+residue;
    }
    if(num==reverted_num_half/10 || num==reverted_num_half) return true;
    else return false;
}

int main()
{
    int x=1;
    while(x!=0)
    {
        cout << "Enter an integer" << endl;
        cin>>x;
        if(isPalindrome(x)==1) cout<<"Palindrome"<<endl;
        else cout<<"Not palindrome"<<endl;
        cout<<endl;
    }

    return 0;
}

Topological Sorting: Kahn’s algorithm

This algorithm requires no Queue or Stack.

#include <bits/stdc++.h>
#define MAX  2000

using namespace std;

vector<int> topSort(int node, vector<int> &indeg, vector<vector<int> > &adj)
{
    vector<int> ans;
    for(int i=0; i<node; i++) //Inserting all independent nodes first
    {
        if(indeg[i]==0) ans.push_back(i);
    }

    for(int i=0; i<ans.size(); i++)
    {
        int u=ans[i];
        for(int j=0; j<adj[u].size(); j++)
        {
            int v=adj[u][j];
            indeg[v]--;
            if(indeg[v]==0) ans.push_back(v);
        }
    }
    if(ans.size()!=node) ans.clear(); //cycle found, so returning empty vector
    return ans;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

    int node, edge;
    cout<<"Enter the number of nodes and edges: ";

    while(cin>>node>>edge) //assuming node>=0, edges>=0
    {
        vector<int> indeg(node,0);
        vector<vector<int> > adj(node);
        /* //for string input
        unordered_map<string,int> mp;
        unordered_map<int,string> mp;
        int c=0;
        */

        if(edge==0) //Print all nodes as all are independent
        {
            for(int i=0; i<node; i++) cout<<i<<" ";
            cout<<endl;
        }

        cout<<"Enter the relations(pair of edges): "<<endl;

        for(int i=0; i<edge; i++) //Populating Adjacency list and indeg array for TopSort
        {
            int from,to;
            //string from,to;
            cin>>from>>to;

            /* //Processing string input

            if(mp1[from]==0)
            {
                mp1[from]=++c;
                mp2[c]=from;
            }
            if(mp1[to]==0)
            {
                mp1[to]=++c;
                mp2[c]=to;
            }
            g[mp1[from]].pb(mp1[to]);
            indeg[mp1[to]]++;
            */
            adj[from].push_back(to);
            indeg[to]++;
        }

        vector<int> ans=topSort(node, indeg, adj);

        if(ans.size()>0) //Ordering possible
        {
            cout<<"The correct ordering is:";
            for(int i=0; i<ans.size(); i++)
            {
                cout<<" "<<ans[i];
                //cout<<mp2[ans[i]]<<" ";
            }
            cout<<endl;
        }
        else cout<<"Cycle exists"<<endl; //Ordering not possible
    }
    return 0;
}

Iterative DFS for Detecting Cycle in any Directed Graph

Note:

The algorithm for detecting cycles in any directed graph is easier using recursive DFS. But Iterative DFS approach to detect a cycle is a bit complex and tricky. I have commented on the code where necessary to make the algorithm more understandable.

In any case, the time complexity of the algorithm is O(V+E).

#include <bits/stdc++.h>

using namespace std;

#define MAX 100005

bool detectCycle(int nodes, vector<vector<int> >& adjList)
{
    if(nodes==1) return false;
    stack<int> st;
    bool on_stack[MAX+1]= {0};
    bool visited[MAX+1]= {0};
    for(int i=0; i<nodes; i++) //In case the graph is disconnected
    {
        if(visited[i]) continue; //For time efficiency
        st.push(i);
        while(!st.empty())
        {
            int u=st.top();
            if(!visited[u])
            {
                visited[u]=1;
                on_stack[u]=1;
            }
            else //backtracking, but keeping the visited array unchanged for time efficiency
            {
                on_stack[u]=0;
                st.pop();
                continue;
            }
            for(int j=0; j<adjList[u].size(); j++)
            {
                int v=adjList[u][j];
                if(!visited[v])
                {
                    st.push(v);
                }
                else
                {
                    if(on_stack[v]) return true; //cycle detected
                }
            }
        }
    }
    return false;
}


int main()
{
    vector<vector<int> > adjList(MAX+1);
    int nodes, edges;
    cout<<"Enter the number of nodes: ";
    cin>>nodes;
    cout<<endl;
    cout<<"Enter the number of edges: ";
    cin>>edges;
    cout<<endl;
    cout<<"Enter the pair of edges: "<<endl;
    for(int i=0; i<edges; i++)
    {
        int from, to;
        cin>> from>> to;
        adjList[from].push_back(to);
    }
    if(detectCycle(nodes, adjList)==1) cout<<"Cycle found!"<<endl;
    else cout<<"Cycle not found!"<<endl;
    return 0;
}

In-order Successor of any Binary Tree

1. Binary tree:

The successor of a node p is the node with the smallest key greater than p.val.

So there can be three cases to find the successor of a node:

  1. If the node has the right child, the successor must reside in its right subtree. If not, go to case 2. Note that, I didn’t mention the right “child”, I mentioned “subtree”. Because the node’s right child may not have the smallest value greater than p.value. In that case, the smallest value would be the leftmost child of the right child.
  2. If the node has no right child, then in the inorder traversal process, the successor node to be traversed would be this(node p’s) subtree’s parent node, which is the immediate node in the stack. If the stack is empty already, then go to case 3.
  3. If case 1 and 2 fails, that means node p has no successor.

We can use this process to find the inorder successor of a BST(Binary Search Tree) too. But in that case, we are not using the properties of BST, so that won’t be optimized. That is, this code’s time and space complexity is O(N), N being the number of nodes. In the case of BST, if we use the BST properties, then this problem can be solved in O(N) time and O(1) space.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        stack<TreeNode*> st;
        TreeNode *node=root;
        bool c=0;
        while(!st.empty() || node)
        {
            while(node)
            {
                st.push(node);
                node=node->left;
            }
            node=st.top();
            st.pop();
            if(c==1) return node;
            if(node==p) 
            {
                c=1;
                if(!node->right)
                {
                    if(!st.empty()) return st.top();
                    else return NULL;
                }
            }
            node=node->right;
        }
        return NULL;  
    }
};

2. Binary Search Tree:

Here using the BST properties, each time, we will discard half of the tree. Thus our usual time complexity for a balanced binary tree would be logarithmic instead of linear i.e. O(log(N)), and our space complexity would be constant i.e O(1) as we are not using any extra data structure to store the tree. Though in case the BST is not balanced, so we will get a skewed tree, in that worst-case scenario, the time complexity would be O(N).

Here, all the above 3 cases are taken care of at once.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        TreeNode *node=root;
        TreeNode *successor=NULL;
        while(node)
        {
            if(node->val > p->val)
            {
                successor=node;
                node=node->left;
            }
            else if(node->val <= p->val)
            {
                node=node->right;
            }
        }
        return successor;  
    }
};

Binary Search

Note:

Binary Search is a core algorithm of Computer Science. It can be found everywhere but I am particularly writing it here as I just got to know about new knowledge or change in the logic of the traditional algorithm. The algorithm has been made more appropriate recently with the memory and capacity evolving in recent age. For detail and in-depth knowledge please see this – https://ai.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html

So basically in the traditional algorithm while dividing the array or vector space into two halves, the proper logic would be –

mid = first+(last-first)/2;

instead of

mid = (first+last)/2;

Otherwise, you would get a runtime error in the case that the sum of the first and last is

greater than the maximum positive int value (231 – 1). 

Code:

#include <bits/stdc++.h>

using namespace std;

int a[10000007];
int not_found_pos=-1; //Declaring it global to get the index if the item is not found

bool binarySearch(int item, int n) 
{
    int first=0, last=n-1, mid=0;
    while(first<=last)
    {
        mid=first+(last-first)/2; //for avoiding overflow of summation of first and last
        if(item>a[mid]) first=mid+1;
        else if(item<a[mid]) last=mid-1;
        else return mid; //item=a[mid] //item found
    }
    //Item was not found
    not_found_pos=first; //But if it was found it would be at index 'first'
    return -1; 
}

int main()
{
    int n,item;
    cin>>n;
    for(int i=0; i<n; i++) cin>>a[i]; // Must be sorted.
    cout<<"Enter the item that to be searched"<<endl;
    cin>>item;
    int pos=binarySearch(item,n);
    if(pos==-1)
    {
        cout<<"The item "<<item<<" was not found but if it was found, it would be at index "<< not_found_pos << endl;
    }
    else //Assuming Indexing from "0"
    {
        cout<<"The item "<<item<<" was found at position "<<pos<<endl;
    }
    return 0;
}