C

Linked List

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
    int number;
    struct node *next;
}
node;

int main(int argc, char *argv[])
{
    node *list = NULL;

    for (int i = 1; i < argc; i++)
    {
        int number = atoi(argv[i]);
        node *n = (node *)malloc(sizeof(node));
        if (n == NULL)
        {
            return 1;
        }
        // n->number means 'dereference the pointer, n,
        // and access its property, number'
        // another way of saying it is go to the property
        // of the pointer's memory address
        // another way of thinking about it is
        // accessing the property of a referenced object in javascript,
        // but it's a little different because of the pointer abstraction
        // and separate memory address

        // n->number = number; will assign the value input
        // on the command line to the property of
        // the pointer's memory address
        n->number = number;
        // n->next = list; will assign the node data structure's
        // next pointer (there is only next because it is a singly
        // linked list) to the current list, which can be thought
        // of as HEAD like in git, or the current commit,
        // before this update is made
        // a singly-linked list adds each node by preprending it
        // (to the left); that means the "list" (or HEAD) is always
        // going to be penultimate or second to the most current node
        // if we consider the one being currently added and linking back
        // the node we add links back to list (HEAD), this way it keeps
        // track of its direct ancestor; then we change what list points to,
        // which will be the temporary value in the loop, n, so now HEAD
        // (list) points to the most recently added node which linked to
        // the previously most recently added node
        // now n is free to be reallocated for the next node to be added
        n->next = list;
        // n->next = list; list = n; that is a pattern that
        // you need to memorize for it to make sense, just like your times tables
        list = n;
    }
    // print the list
    // node *ptr = list; this creates a pointer variable with its own memory address, to point to the list, which will initially be pointing at the first memory address in the list
    // while (ptr != NULL) ... ptr = ptr->next; this loops over the list,
    // and after printing the int value at ptr->number, it reassigns ptr to
    // the next node in the linked list (which is referenced by the pointer
    // in the node data structure defined as *next and which is accessed with
    // ptr->next)
    // notably, when ptr->next is referenced, it initially refers to the
    // first item in the list (like the first index in an array); when ptr is
    // reassigned, it moves to the next node (to the right) in the linked list
    node *ptr = list;
    while (ptr != NULL)
    {
        printf("in the while loop\n");
        printf("%i\n", ptr->number);
        ptr = ptr->next;
    }
}

Ordered Linked List

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
    int number;
    struct node *next;
}
node;

// Function to print the linked list in order
void print_list(node *list) {
    node *ptr = list;
    while (ptr != NULL) {
        printf("%i\n", ptr->number);
        ptr = ptr->next;
    }
}

int main(int argc, char *argv[])
{
    node *list = NULL;

    for (int i = 1; i < argc; i++)
    {
        int number = atoi(argv[i]);
        node *n = (node *)malloc(sizeof(node));

        if (n == NULL)
        {
            return 1;
        }

        n->number = number;
        n->next = NULL;

        // if list is empty
        if (list == NULL){
            list = n;
        }

        // if number belongs earlier in list than current node,
        // prepend to beginning of list and set the list to the new node
        // this sets "list" to always be the smallest value
        else if (n->number < list->number){
            n->next = list;
            list = n;
        }

        // if n->number is greater than the currently smallest value (list->number)
        else {
            // iterate over nodes in list
            for (node *ptr = list; ptr != NULL; ptr = ptr->next){
                // first check to see if we haven't found any node's value in the list
                // greater than n->number
                // if that is the case (n->number is larger than any in list)
                // append n node by setting ptr->next to n
                if (ptr->next == NULL){
                    ptr->next = n;
                    break;
                }
                // at this point, n->number is >= list->number
                // if n->number is >= list->number
                // (and subsequently, ptr->number, as we iterate over the list)
                // and n->number is < ptr->next->number ...
                // (if n->number is between the current ptr number and its next pointer's number)
                // insert node in ptr
                if (n->number < ptr->next->number){
                    // set n's next pointer to ptr's next node
                    // in order to put n between ptr and ptr->next
                    n->next = ptr->next;
                    // shift ptr's next to point to n
                    ptr->next = n;
                    break;
                }
            }
        }


    }



    // Print the sorted list
    print_list(list);

    // Optional: Free the allocated memory
    node *current = list;
    while (current != NULL) {
        node *temp = current;
        current = current->next;
        free(temp);
    }

    return 0;
}

// /usr/bin/clang++ -std=c++17 -g ./ordered-linked-list.c -o ./ordered-linked-list
// ./ordered-linked-list 1 2 3 77 4 99 5
// should print 1 2 3 4 5 77 99

Binary Search Tree

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct node {
    int number;
    struct node *left;
    struct node *right;
} node;

bool search(node *tree, int number){
    if (tree == NULL) return false;
    if (tree->number == number) return true;
    if (number < tree->number) return search(tree->left, number);
    else return search(tree->right, number);
}

int main(void) {
     // Create a sample binary search tree
    node *root = (node *)malloc(sizeof(node));
    root->number = 5;
    root->left = (node *)malloc(sizeof(node));
    root->left->number = 3;
    root->left->left = (node *)malloc(sizeof(node));
    root->left->left->number = 1;
    root->left->left->left = NULL;
    root->left->left->right = NULL;
    root->left->right = (node *)malloc(sizeof(node));
    root->left->right->number = 4;
    root->left->right->left = NULL;
    root->left->right->right = NULL;
    root->right = (node *)malloc(sizeof(node));
    root->right->number = 7;
    root->right->left = (node *)malloc(sizeof(node));
    root->right->left->number = 6;
    root->right->left->left = NULL;
    root->right->left->right = NULL;
    root->right->right = (node *)malloc(sizeof(node));
    root->right->right->number = 8;
    root->right->right->left = NULL;
    root->right->right->right = NULL;

    // Test the search function
    int numbers_to_search[] = {1, 4, 6, 9};
    int num_searches = sizeof(numbers_to_search) / sizeof(numbers_to_search[0]);

    for (int i = 0; i < num_searches; i++) {
        int number = numbers_to_search[i];
        bool found = search(root, number);
        printf("Number %d is %s in the tree.\n", number, found ? "found" : "not found");
    }


    free(root->right->right);
    free(root->right->left);
    free(root->right);
    free(root->left->right);
    free(root->left->left);
    free(root->left);
    free(root);

    return 0;
}

Tree Structure Diagram

5 / \ 3 7 / \ / \ 1 4 6 8

Balanced Search Tree With Dynamic Insertion

// balanced-tree.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

// ========================
// Node Definition
// ========================

typedef struct node {
    int number;
    struct node *left;
    struct node *right;
} node;

// ========================
// Insert Function
// ========================

/**
 * Inserts a number into the BST.
 * If the number already exists, it is discarded.
 *
 * @param tree The root of the BST.
 * @param number The number to insert.
 * @return The root of the BST after insertion.
 */

// If the current tree node is NULL,
// a new node is created and returned.
// If the number is less than the current node's number,
// recurse into the left subtree.
// If the number is greater,
// recurse into the right subtree.
// If the number is equal to an existing node's number,
// it's considered a duplicate and is not inserted.

node* insert_bst(node *tree, int number) {
    if (tree == NULL) {
        node *new_node = (node *)malloc(sizeof(node));
        if (new_node == NULL) {
            perror("Failed to allocate memory for new BST node");
            exit(EXIT_FAILURE);
        }
        new_node->number = number;
        new_node->left = new_node->right = NULL;
        return new_node;
    }

    if (number < tree->number) {
        tree->left = insert_bst(tree->left, number);
    } else if (number > tree->number) {
        tree->right = insert_bst(tree->right, number);
    }
    return tree;
}

// ========================
// Search Function
// ========================

/**
 * Searches for a number in the BST.
 *
 * @param tree The root of the BST.
 * @param number The number to search for.
 * @return true if found, false otherwise.
 */
bool search_bst(const node *tree, int number){
    if (tree == NULL) return false;
    if (tree->number == number) return true;
    if (number < tree->number) return search_bst(tree->left, number);
    else return search_bst(tree->right, number);
}

// ========================
// In-order Traversal
// ========================

/**
 * Performs in-order traversal of the BST and prints the numbers.
 *
 * @param root The root of the BST.
 */

 // Traverses the BST in in-order
 // (left, root, right) to print
 // the numbers in ascending order.
 //   In-order traversal of the BST:
 //   1 2 3 4 5
void inorder_traversal(const node *root) {
    if (root == NULL) return;
    inorder_traversal(root->left);
    printf("%d ", root->number);
    inorder_traversal(root->right);
}

// ========================
// Free BST Memory
// ========================

/**
 * Frees all nodes in the BST.
 *
 * @param root The root of the BST.
 */
void free_bst(node *root) {
    if (root == NULL) return;
    free_bst(root->left);
    free_bst(root->right);
    free(root);
}

// ========================
// Main Function
// ========================

int main(int argc, char *argv[]) {
    if (argc < 2) {
        int predefined_numbers[] = {54, 1, 22, 9, 7, 6, 88, 44, 22, 30, 1, 3, 5, 7, 9, 6, 2, 4};
        int num_elements = sizeof(predefined_numbers) / sizeof(predefined_numbers[0]);

        argc = num_elements + 1;  // +1 for the program name
        char **new_argv = (char **) malloc(argc * sizeof(char*));
        if (new_argv == NULL) {
            perror("Failed to allocate memory for new argv");
            return EXIT_FAILURE;
        }

        new_argv[0] = argv[0];  // Program name
        for (int i = 0; i < num_elements; i++) {
            char *num_str = (char *) malloc(12 * sizeof(char));  // Enough for any 32-bit integer
            if (num_str == NULL) {
                perror("Failed to allocate memory for number string");
                return EXIT_FAILURE;
            }
            /*
             * makes the i+1th element of new_argv point to the
             * memory location where the string representation of a number is stored.
             */
            snprintf(num_str, 12, "%d", predefined_numbers[i]);
            /*
             * after new_argv[i + 1] = num_str; ,
             * the i-th + 1 element of new_argv is supplied with
             * the ith element of the loop converted to a string
             */
            new_argv[i + 1] = num_str;
        }

        argv = new_argv;
    }

    node *bst_root = NULL; // Root of the BST

    for (int i = 1; i < argc; i++) {
        char *endptr;
        errno = 0; // To distinguish success/failure after the call
        long val = strtol(argv[i], &endptr, 10);

        // Check for conversion errors
        if (errno != 0 || *endptr != '\0') {
            fprintf(stderr, "Invalid number '%s'. Skipping.\n", argv[i]);
            continue;
        }

        // Check for integer overflow
        if (val < INT_MIN || val > INT_MAX) {
            fprintf(stderr, "Number '%s' out of range. Skipping.\n", argv[i]);
            continue;
        }

        int number = (int)val;
        bst_root = insert_bst(bst_root, number);
    }

    // Print the BST in-order
    printf("In-order traversal of the BST:\n");
    inorder_traversal(bst_root);
    printf("\n");

    // Define the constant integer value to search for
    const int SEARCH_VALUE = 4;

    // Perform the search
    bool found = search_bst(bst_root, SEARCH_VALUE);
    printf("Number %d is %s in the tree.\n", SEARCH_VALUE, found ? "found" : "not found");

    // Free allocated memory
    free_bst(bst_root);

    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            free(argv[i]);
        }
        free(argv);
    }

    return EXIT_SUCCESS;
}

/**
 *  /usr/bin/clang -std=c17 -g ./balanced-tree.c -o ./balanced-tree
 *  ./balanced-tree 54 1 22 97 6 88 44 22 30 1 3 5 7 9 6 2
 *  In-order traversal of the BST:
 *  1 1 2 3 5 6 6 7 9 22 30 44 54 88 97
 *  Number 4 is not found in the tree.
 *  ./balanced-tree 54 1 22 9 7 6 88 44 22 30 1 3 5 7 9 6 2 4
 *  In-order traversal of the BST:
 *  1 2 3 4 5 6 7 9 22 30 44 54 88
 *  Number 4 is found in the tree.
 **/