简体   繁体   中英

writing a binary search tree in C without pointers

Is it possible to write a binary search tree in C without pointers?

I have written using pointers as follows.

Working BST code in C using pointers

#include <stdio.h>
#include <malloc.h>

typedef struct node
{
 int data;
 struct node* left;
 struct node* right; 
}Node;
Node* root = NULL;

int insert(int);
int display(Node*);

int main(void)
{
 int n = 0;

 while(1)
 {
  printf("Enter data : ");
  scanf("%d",&n);

  if(n == -1)
   break;

  insert(n);
 }

 display(root);

 return 0;
}

int insert(int data)
{
 Node* node = malloc(sizeof(Node));
 node->data = data;
 node->left = NULL;
 node->right = NULL; 
 Node* parent;
 Node* trav;

 if(root == NULL)
  root = node;
 else
 {
  trav = root;

  while(trav != NULL)
  {
   parent = trav;

   if(node->data < trav->data)
    trav = trav->left;
   else
    trav = trav->right;
  }

  if(node->data < parent->data)
   parent->left = node;
  else
   parent->right = node;
 }
}

int display(Node* node)
{
 if(node == NULL)
  return 0;

 display(node->left);
 printf("%d ",node->data);
 display(node->right);
}

Is is possible to write a BST without pointers, and using Nodes only. So, I want to access the left as node.left instead of node->left and so on. Even the members of the structure node should be like

typedef struct node
    {
     int data;
     struct node left;
     struct node right; 
    }Node;

and the Node members would be declared as

Node root; Node node;

and not as

Node* root; Node* node;

If it's not possible to write BST using the above structures, why is it so? Is it because, NULL is a pointer which has a value reserved for indicating that the pointer does not refer to a valid object. So, if we were using just a structure, we wouldn't know when to stop. So, I commented out the NULL lines in the above code, and made changes to access as structure members and not pointers. I was expecting it to atleast compile, although it would be an infinite loop at places. However, it gives me some compilation errors as well.

Tried BST code in C without using pointers, does not compile

#include <stdio.h>
#include <malloc.h>

typedef struct node
{
 int data;
 struct node left;
 struct node right; 
}Node;
//Node root = NULL;
Node root;

int insert(int);
int display(Node);
int rootformed = 0;

int main(void)
{
 int n = 0;

 while(1)
 {
  printf("Enter data : ");
  scanf("%d",&n);

  if(n == -1)
   break;

  insert(n);
 }

 display(root);

 return 0;
}

int insert(int data)
{
 Node node = malloc(sizeof(Node));
 node.data = data;
 node.left = NULL;
 node.right = NULL; 
 Node parent;
 Node trav;

 if(rootformed == 0)
 {
  root = node;
  rootformed = 1;
 }
 else
 {
  trav = root;

  //while(trav != NULL)
  while(1)
  {
   parent = trav;

   if(node.data < trav.data)
    trav = trav.left;
   else
    trav = trav.right;
  }

  if(node.data < parent.data)
   parent.left = node;
  else
   parent.right = node;
 }
}

int display(Node node)
{
 //if(node == NULL)
  //return 0;

 display(node.left);
 printf("%d ",node.data);
 display(node.right);
}

However, I was going through how a binary search tree is implemented in Java, as follows. As seen below, members are accessed using the dot symbol. I'm curious to understand how it's done here.

If class is a structure, can I say that an object is a pointer to the structure. The only difference being that in C, a pointer to a structure uses the notation -> to access the internal members of the structure, whereas, an object just uses . to access the internal memebers of the structure(class)

Working BST code in java, which uses the . notation, got me thinking on how I can emulate this in C to use the . symbol and not ->

public class BinarySearchTree 
{
    public Node root;
    public BinarySearchTree()
    {
        this.root = null;
    }

    public boolean find(int id)
    {
        Node current = root;
        while(current!=null)
        {
            if(current.data == id)
            {
                return true;
            }
            else if(id < current.data)
            {
                current = current.left;
            }
            else
            {
                current = current.right;
            }
        }

        return false;
    }

    public boolean delete(int id)
    {
        Node parent = root;
        Node current = root;
        boolean isLeftChild = false;

        while(current.data != id)
        {
            parent = current;
            if(id < current.data)
            {
                isLeftChild = true;
                current = current.left;
            }
            else
            {
                isLeftChild = false;
                current = current.right;
            }
            if(current ==null)
            {
                return false;
            }
        }
        //if i am here that means we have found the node
        //Case 1: if node to be deleted has no children
        if(current.left==null && current.right==null)
        {
            if(current==root)
            {
                root = null;
            }
            if(isLeftChild ==true)
            {
                parent.left = null;
            }
            else
            {
                parent.right = null;
            }
        }
        //Case 2 : if node to be deleted has only one child
        else if(current.right==null)
        {
            if(current==root)
            {
                root = current.left;
            }
            else if(isLeftChild)
            {
                parent.left = current.left;
            }
            else
            {
                parent.right = current.left;
            }
        }
        else if(current.left==null)
        {
            if(current==root)
            {
                root = current.right;
            }
            else if(isLeftChild)
            {
                parent.left = current.right;
            }
            else
            {
                parent.right = current.right;
            }
        }
        else if(current.left!=null && current.right!=null)
        {   
            //now we have found the minimum element in the right sub tree
            Node successor   = getSuccessor(current);
            if(current==root)
            {
                root = successor;
            }
            else if(isLeftChild)
            {
                parent.left = successor;
            }
            else
            {
                parent.right = successor;
            }           
            //successor.left = current.left;
        }       
        return true;        
    }

    public Node getSuccessor(Node deleteNode)
    {
        Node successsor =null;
        Node successsorParent =null;
        Node current = deleteNode.right;
        while(current!=null)
        {
            successsorParent = successsor;
            successsor = current;
            current = current.left;
        }
        //check if successor has the right child, it cannot have left child for sure
        //if it does have the right child, add it to the left of successorParent.
        //successsorParent
        if(successsor!=deleteNode.right)
        {
            successsorParent.left = successsor.right;
            successsor.right = deleteNode.right;
        }

        if(successsor==deleteNode.right)
        {
            /* Then no more right tree */

        }

        successsor.left = deleteNode.left;
        return successsor;
    }

    public void insert(int id)
    {
        Node newNode = new Node(id);
        if(root==null)
        {
            root = newNode;
            return;
        }
        Node current = root;
        Node parent = null;
        while(true)
        {
            parent = current;
            if(id < current.data)
            {               
                current = current.left;
                if(current==null)
                {
                    parent.left = newNode;
                    return;
                }
            }
            else
            {
                current = current.right;
                if(current==null)
                {
                    parent.right = newNode;
                    return;
                }
            }
        }
    }

    public void display(Node root)
    {
        if(root != null)
        {
            display(root.left);
            System.out.print(" " + root.data);
            display(root.right);
        }
    }

    public static void main(String arg[])
    {
        BinarySearchTree b = new BinarySearchTree();

        b.insert(3);b.insert(8);
        b.insert(1);b.insert(4);b.insert(6);b.insert(2);b.insert(10);b.insert(9);
        b.insert(20);b.insert(25);b.insert(15);b.insert(16);

        System.out.println("Original Tree : ");
        b.display(b.root);      
        System.out.println("");
        System.out.println("Check whether Node with value 4 exists : " + b.find(4));
        System.out.println("Delete Node with no children (2) : " + b.delete(2));        
        b.display(root);
        System.out.println("\n Delete Node with one child (4) : " + b.delete(4));       
        b.display(root);
        System.out.println("\n Delete Node with Two children (10) : " + b.delete(10));      
        b.display(root);
    }
}

class Node
{
    int data;
    Node left;
    Node right;

    public Node(int data)
    {
        this.data = data;
        left = null;
        right = null;
    }
}

In stead of pointers to memory objects, you can allocate a large array of Node objects and store indexes into this array in the left and right members.

Array entry 0 is the root node. You must keep track of the first unused array element to store a new Node . You can use calloc to allocate the array and realloc to enlarge the array.

You must keep track of deleted items: keep track of the first one and put in left the index of the next deleted item (kind of linked list). You can also keep track of the last deleted item to quickly append another deleted iem to the list.

You can implement binary search in an array, using array indexes instead of pointers. In C the array is just a language construct that automates pointer arithmetic and keeps it out of your code. If you malloc the entire array of structs and make the left and right members integers of some appropriate size, it can work.

But in structs created individually with malloc you can't do it without pointers because...

In C the structure is just memory allocated in a contiguous block. The . operator translates to a simple offset from the beginning of a block.

When you try to use the . operator to refer to .left or .right you are referring to a different struct that you created with a different malloc and it could be anywhere in heap memory. So the simple offset from the beginning of the current node is unknown.

So in C you need a pointer, to store the address of the left or right node.

In Java these are object references, essentially nicely wrapped and managed pointers. The JVM is managing allocation and tracking memory addresses, and this is mostly transparent to your code. You are, in effect, using pointers in the Java code, at run-time but your source code is written in terms of object references.

You could also implement binary search in C using a file or memory mapped file, using offsets into that file instead of C pointers. This is probably not what you intend in your question, but is/was often done in applications with large sorted data sets that need binary search.

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