1. print_levels needs to use a queue where it adds children to the back of the queue and visits nodes at the front of the queue. Such a visitation or search pattern is a called breadth-first search since it spreads through the tree level-by-level. Each time print_levels visits a node it will add the node's children to the back of a queue. It will visit nodes by removing items from the front of the queue. Note that since print_levels visits each node only once that the algorithm is O(n).
    class level_rec {
        treenode *node;
        int level;
        level_rec(int l, treenode *n): node(n), level(l) {}
    };
    
    
    void print_levels(treenode *root) {
        queue node_queue;
    
        level_rec *current_item = new level_rec(0, root);
    
        node_queue->append(current_item);
        while (!node_queue->isEmpty()) {
          current_item = node_queue->remove_first_item();
          int current_level = current_item->level;
          treenode *node = current_item->node;
          printf("%d \t %d\n", current_level, node->value);
          if (node->left_child != 0)
            node_queue->append(new level_rec(current_level + 1, node->left_child));
          if (node->right_child != 0) {
            node_queue->append(new level_rec(current_level + 1, node->right_child));
          }
        }
    }
    
  2. A log n multiplication algorithm can be achieved by writing the multiplier as a bit pattern and performing the following summation:
       
     log multiplier
       ---
       \                    i
       /    multiplicand * 2  * multiplier_bit[i]
       ---
       i=0
    
    where multiplier_bit[i] is the ith bit of the multiplier. Since the multiplier can be represented using log(multiplier) bits, the algorithm is O(log n).
    int multiply(int factor1, int factor2) {
        int multiplier;
        int multiplicand;
    
        if (factor1 < factor2) {
    	multiplier = factor1;
    	multiplicand = factor2;
        }
        else {
    	multiplier = factor2;
    	multiplicand = factor1;
        }
     
        int product = 0;
        while (multiplier > 0) {
            /* inspect the first bit position of the multiplier and add 2i
                to the product if the bit position is set to 1 */
    	if ((multiplier & 1) == 1) {
    	    product += multiplicand;
    	}
            /* multiply the multiplicand by 2 and right shift the multiplier so
               we can check its next bit */
    	multiplicand = multiplicand << 1;
    	multiplier = multiplier >> 1;
        }
        return product;
    }
    

  3. Make the sentinel value be the same as the key we are inserting. Since the insert algorithm ceases when the parent is less than or equal to the key that we are pushing up the tree, a sentinel value that is equal to the key will cause the insertion algorithm to cease.

  4. Use a single sentinel node and make left and right children that would otherwise be null (0) point to this single sentinel node. Now the find routine only has to initialize one sentinel node to the key.

  5. The solution to this problem lies in being able to eliminate a row or column with a constant number of comparisons. At the termination of the algorithm, either the entire matrix has been eliminated or the key value has been found. The central observation is that because all rows and columns are sorted in increasing ordrer, any row or column containing a value less than the key adjacent to a value greater than the key does not contain the key.

    Adding up the comparisons from steps 1 and 2 we see that our procedure requires at most 4n comparisons of the key to array values plus 4n comparisons of indices to array bounds.

    void find(int key, int **matrix, int n) {
      int x = 1;
      int y = n;
      int flag;
    
      do
      {
        flag = 0;
        while (x <= n && matrix[y][x] < key) {x++; flag++;}
        while (n > 0  && matrix[y][x] > key) {y--; flag++;}
      } while (flag);
    
      if (y > 0 && x <= n) printf("found\n");
      else printf("not found\n");
    }
    

    We can use sentinel values to avoid boundary checks on the matrix. Note that filling in all the sentinel values at the beginning would require placing 4n+4 sentinels. Instead it is possible to place a sentinel at the end of a row or column only when we start searching along it.

    The following solution uses the sign function and switch statement as well as minimizing the number of sentinels placed. Movement is assumed to start in the first column, so an initial sentinel is placed in that column. A flag is used to keep track of the direction of movement.

    void find(int key, int **matrix, int n) {
      int done;
      int xflag;
    
      int s = 1;
    
      int x = 1;
      int y = n;
    
      matrix[0][1] = key;
      xflag = 0;
    
      for (done = 0; !done; )
      {
        switch (sign(matrix[y][x] - key))
          {
          case -1:
            {x++; if (!xflag) {matrix[y][n+1] = key; s++;} xflag = 1;}
    	break;
          case 1:
            {y--; if (xflag)  {matrix[0][x]   = key; s++;} xflag = 0;}
    	break;
          default:
    	done = 1;
    	break;
          }
      }
    
      if (y > 0 && x <= n) printf("found");
      else printf("not found");
    
      printf (", sentinels = %d\n", s);
    }
    
    When the key value is less than any value in the array, this algorithm places only one sentinel value. It is possible to eliminate the initial sentinel at the cost of introducing an extra flag to indicate the first iteration.