C Pointers

I found some great tutorials on C pointers. Attempt the tutorials below by coding the examples found within the tutorials.

https://www.programiz.com/c-programming/c-pointers

https://www.programiz.com/c-programming/c-pointers-arrays

https://www.programiz.com/c-programming/c-pointer-functions

(optional) https://www.programiz.com/c-programming/c-dynamic-memory-allocation

(optional) – https://www.programiz.com/c-programming/c-pointer-examples

Singly Linked List

The  linked list ADT consists of nodes that are linked together by a pointer.  We can think of a node programmatically as a C Structure, Node, which contains 2 variables:

  1. A variable called value , which is used to store the data in the list. Value can be set to any data type depending on what is needed. (Note that some implementations name this variable as ‘element’)
  2. A variable called next, which is a pointer to a node.

Our linked list class contains one global variable of type node pointer, which points to the first item in the list.

The list can be thought of a queue of people in a line with name tags (data), however, in this queue, people can join at the front, back and cut in line.

Operations performed on a linked list are shown below:

OperationExplanationExample
add(value) or addLast(value)Adds the value to the end of the list.Someone joins the end of the line
addFirst(value)Adds a value to the top/front of the listSomeone joins the line from the front.
insert(value, i)Adds/inserts a value at location i counting from top/frontSomeone cuts in line at the become the ith position in the list.  If i exceeds the size of the list, the person is added to the end of the list
size()Returns the size of the listFind out the amount of people on the line
isEmpty()Returns true if the queue is empty, i.e, size==0Find out if the line is empty.
delete(i)Deletes/removes a node at location i, and returns the valueSomeone leaves the line from the ith location
Some operations that are performed on a linked list.

Some Linked List algorithms

Performing the operations above is not as straightforward as our previous ADTs.  In this discussion, we examine the algorithms for performing operations in the list.

Initialization

First<--null

addFirst

AddFirst(newValue)
{
	Create the new Node newListItem
	newListItem.value <-- newValue
	newListItem.next  <--first
	first<--newListItem
}

add last

AddLast(newValue)
{
Create the new Node newListItem
newListItem.value  newValue

If the list is empty Then
       first <-- newListItem
       newListItem.next <-- NULL
Else
	Create Nodes currentNode, previousNode
	while(currentNode!=NULL)
        {
           previousNode=currentNode;
            currentNode=currentNode->next;
       	}
        
        previousNode->next=newNode;
}

In class assignment/homework 1

The rest of the operations  on the linked list were omitted:

NumberOperation
1size()
2isEmpty()
3insert(value, i)
4delete(i)

Using to the algorithms above as a guide, write separate algorithms which implements the operation listed in the table above.

(Solution here: Solution: Singly Linked List -In class assignment/homework 1

Code for a linked list in C

//Note that the operations from the in class assignment/homework are not included.
#include <stdio.h>
#include <stdlib.h>


/*keep track of the size of the list.
Initially, the size is 0,
thus  the count is 0.*/
int count = 0;

/*create a structure called "Node_n"
and give it a type definition
named Node.
We do this to avoid using the
"struct" keyword everytime
we declare a  Node_n structure.

It's simply a quality of life improvement
*/
typedef struct Node_n
{
    int data;
    struct Node_n * next;/*note that we cant
                        use the typedef here ,
                        e.g "struct Node * next;"
                        since the type definition
                        has not been created yet.
                        if used it will produce
                        a warning when you try to
                        assign Node Structures to
                        each other such as in the
                        add function etc:
                        "assignment from incompatible
                        pointer type
                         [-Wincompatible-pointer-types]|"
                             */
} Node;



/*we declare Node structure to
represent the first item at the
start of the list.

It is assigned a mull pointer value,
which can be thought of a value of
0 for the pointer.
*/
Node * first;

/*functionDeclarations 
that were IMPLEMWNTED 
in this code*/
void addFirst(int value);
void addLast(int value);
int size();
void printList();

/*functionDeclarations 
that were NOT IMPLEMENTED 
in this code. It was left 
for student discussion  
on how this should be implemented*/

void deleteNode(int location);//for delete(i) 
                              //algorithm from above                                
void insert(int value, int location);

int main()
{
    //initialization
    first=NULL;


    //codetesting
    addFirst(2);
    addFirst(1);
    addLast(3);
    addLast(4);
    printList();

    return 0;
}

void addFirst(int value)
{
    //create new node....
    /*we use malloc
        to allocate a portion  of
        RAM memory to store the
        data contained within a node.
        The amount of memory allocated is
        the size of the node datatype.


        the newly allocated node
        Node structure's pointer is now assigned
        to  our pointer named "newNode"

        note that "sizeof" is a unary operator
        much like the address operator "&"

        sizeof only requires parentheses
        only when  it is evaluating a data type
        so :

        int x;
        printf("%d", sizeof x);

        would complile and work fine.
        */


    Node * newNode = malloc(sizeof (Node));
    /*Question: how different would it be create
    new node without a pointer and achieve the
    same result in the line above?*/

    //.....store the data in the new node
    newNode->data=value;


    /*It is best practice to check to see of the list
    is empty and not based on the integer size being
    zero. This is because most list traversal happens
    through pointers  and must be stopped when null
    is reached. If using size to check if it's empty,
    the programmer may forget to set the last link
    in the list to be null resulting in infinite
    traversal looping*/
    //if the list is empty..
    if( first==NULL)
    {

        first=newNode;
        first->next=NULL;/*set next to null,
                        indicating that
                        this is the last item
                        in the list so far*/

    }
    else// the list contains items///
    {
        //create links..
        newNode->next=first;
        first=newNode;
        //recall that the list is null terminated still.
    }
    count++;
}

/*Future exercise 3:
adde a node called last which always
stores the last item in the list.
modify addFirst to ensure that
add first is being tracked.
modify this code to omit the
traversal using the while loop*/

void addLast(int value)
{
    /*What would happen if we used node
    structures instead of pointers?
    try to implement this function without
    delaring pointers,
    i.e declare newNode,currentNode,
    previousNode as a node and not a pointer
    to a Node
    Answer: when current is set to null, we will
    not be able to dereference the null value to
    a node so that current can now bw assigned
    to a NULL node. There is no such thing
    as a null struct, only null pointers*/
    Node * newNode = malloc(sizeof(Node));;

    newNode->data = value;
    newNode->next = NULL;

    //if the list is empty
    if(first==NULL)
        first=newNode;
    else
    {
        Node * currentNode , * previousNode;

        currentNode=first;
        previousNode=first;

        /*while  the end of the list
        has not been reached*/
        while(currentNode!=NULL)
        {
            previousNode=currentNode;
            currentNode=currentNode->next;
        }
        /*loop terminates with previousNode
        being the last one in the list
        */
        previousNode->next=newNode;
    }
    count++;
}

void printList()
{
    printf("Start list...\n");
    Node * current = first;
    while (current !=NULL)
    {
        printf("%d\n", current->data);
        current=(Node *)(current->next);
    }
    printf("End list...\n");
}



int size()
{
    return count;
}

In class assignment/homework 2

The rest of the operations on the linked list  were omitted in the code:

NumberOperation
1IsEmpty()
2Insert(int value, int i)
3deleteNode(i)

Using to the Code  above as a guide, write functions which implements the operation listed in the table above. Update your main method to test the operations so that they behave as expected.

Solution here: Solution: Singly Linked List -In class assignment/homework 2

© 2020  Vedesh Kungebeharry. All rights reserved. 

Updates to this post

2024 – 09 – 21

  • changed function names from upper camel case to lower camel case.
  • Added links to solutions.

Binary Search Implementation (with animated code)

This shows  a binary search for 1 then 39 then 24. See here: Binary Search Animation (Animated C code)

Introduction

In a binary search, an ordered list is searched for a key starting with the item at the middle of the list. If the item is not found at the location ,  a determination is made on which half of the list may contain the key, either the upper or lower half. This method is applied to search the midpoint of the half of the list which may contain the key.  This repeats until the key is found, or until the search space is reduced to a single location and  the list can no longer be divided into halves. If the key is found, the location of the element is returned, otherwise a dummy value is returned (e.g -1)

This method of searching fall in the category of algorithms called “Divide and Conquer” since a large portion of the search space is split and discarded on each iteration.

Table of Contents

  1. Introduction
  2. Binary Search Algorithm
  3. Implementation in C code
  4. Alternate Implementation In C using Recursion
  5. Updates

Binary Search Algorithm

Given a list of items of a finite size and a Key we traverse the list to find the key and output the location of the key found in the list.

Consider the scenario where we have a list, with unique items (in an array) and we wish to find a key:

Algorithm:
//initialization
//Assume our data is stored in an  array named  list of size N.
list[0..N-1] = {Assume a sorted list}
first=1
last=N-1
result=-1 //used to store the index of a successful search. If result is -1 , the search has failed
middle = (first-last)/2
while (first<=last)
	if list[middle]==key then
		result= middle
break
	else if key<list[middle] then
		last=middle-1;
	        else
		first=middle+1;
	        endif
	middle=(first-last)/2 + first
endif
endwhile
return result.

Implementation in C code

/*
Demo: Searching a sorted list using 
binary search
*/

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

int binarySearch(int key, int  list[], int listSize);

int main()
{
    int list[10];
    list[0]=5;
    list[1]=10;
    list[2]=15;
    list[3]=20;
    list[4]=25;
    list[5]=30;
    list[6]=35;
    list[7]=40;
    list[8]=45;
    list[9]=50;

    int key;

    printf("\nenter a search key\n");
    scanf("%d",&key);
    while (key!=-1)
    {
        int result = binarySearch(key,list,10);
        printf("\nSearch result %d\n",result);
        printf("\nenter a search key\n");
        scanf("%d",&key);
    }

    return 0;
}

int binarySearch(int key, int  list[], int listSize)
{
    /*we use first and last to represent a 
    portion of the array. Initially, first and last
    points to the start of the array and the end of 
    the array respectively. Middle indexes the location
     to the middle of the array.
    */
    int result=-1;//-1 represents a failed search
    int first=0;
    int last=listSize-1;
    int middle = (last-first)/2;

    while (first<=last)//while first and last have not crossed...
    {
        if(key==list[middle])//if the key is found....
        {
            result=middle;
            return result;//return the current index and exit function
        }
        else//the key was not found
        {
            if(key<list[middle])//if the key would be found 
                                //from first to middle
                last=middle-1;//we now search the lower half
            else //key would be found from middle to last
                first=middle+1;//we now search the upper half
            middle=((last-first)/2)+first;//find the new midpoint.
        }
    }
    return result;
}

Alternate Implementation In C using Recursion

/*
Demo: Searching a sorted list using
binary search implemented using recursion
*/

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

int binarySearch(int key, int  list[], int first, int last);

int main()
{
    int list[10];
    list[0]=5;
    list[1]=10;
    list[2]=15;
    list[3]=20;
    list[4]=25;
    list[5]=30;
    list[6]=35;
    list[7]=40;
    list[8]=45;
    list[9]=50;

    int key;

    printf("\nenter a search key\n");
    scanf("%d",&key);
    while (key!=-1)
    {
        int result = binarySearch(key,list,0,9);
        printf("\nSearch result %d\n",result);
        printf("\nenter a search key\n");
        scanf("%d",&key);
    }

    return 0;
}

int binarySearch(int key, int  list[], int first, int last)
{
    /*we use first and last to represent a
    portion of the array. Initially, first and last
    points to the start of the array and the end of
    the array respectively. Middle indexes the location
     to the middle of the array.
    */
    int result=-1;//-1 represents a failed search
    int middle = (last-first)/2+first;

    if (first<=last)//if first and last have not crossed...
    {
        if(key==list[middle])//if the key is found....
        {
            result=middle;
            return result;//return the current index and exit function
        }
        else//the key was not found
        {
            if(key<list[middle])//if the key would be found
            {                   //from first to middle

               //we now search the lower half
                result= binarySearch(key,list,first,middle-1);
                return result;
            }

            else //key would be found from middle to last
            {
                result= binarySearch(key,list,middle+1,last);//we now search the upper half
                return result;
            }

        }
    }
    return result;
}

Updates

2023/12/26

Added animated image and link to its source post.

2023/12/17

Fixed binary search algorithm , the element at middle was included every time then a partition made:

else if key<list[middle] then
		last=middle;
	        else
		first=middle;
	        endif
	middle=(first-last)/2 + first

It was changed to:

else if key<list[middle] then
		last=middle-1;
	        else
		first=middle+1;
	        endif
	middle=(first-last)/2 + first

Added a paragraph on Divide and Conquer

2022/2/3

Added introduction paragraph “In a binary … value is returned (e.g -1)

Added heading “Binary Search Algorithm”

© 2020  Vedesh Kungebeharry. All rights reserved. 

Bubble Sort Implementation & Demonstration

Bubble Sort (Ascending)

  1. In this algorithm we compare  the first two adjacent items in the  list. If the first item is larger than the item next to it, we swap them, else we do nothing.
  2. This process (step1) is repeated until we have reached the end of the list.  At this point, the largest item is at the end of the list, we say it was “bubbled” up the list.
  3. Repeat step 1 and 2 for items 1 .. n-1, then 1 .. n-2 and so on until we reach 1..2

It seems difficult to understand but we can observe the algorithm in action in the following flowchart:

You can download the file here:

https://drive.google.com/file/d/1RvZ-dBvbHf0icL083d7vSnnce3_XDyyj/

Demonstration

See : https://youtu.be/KgqM6sC5Q4M

Exercise

Implement the algorithm in c with the following modifications:

  • The array stores double data
  • Print the array to the screen as a comma separated list before sorting.
  • Print the array to the screen as a comma separated list after sorting.

Challenge

Create a function with the following functions declaration which will sort any double array using bubble sort:

            void bubbleSort(double array[], int sizeOfArray);

© 2020  Vedesh Kungebeharry. All rights reserved. 

Debugging and Refactoring exercise

0008.2

Definition: Debugging is the process of finding and fixing errors in program code.

Definition: Factoring (Decomposition) is the breaking of a complex task to its atomized sub parts. The goal of factoring is to reach a level of detail that can be represented in an algorithm and eventually programming code.  

Definition: Refactoring – Changing program code’s structure to improve readability or efficiency without changing it’s behavior; or, to change a code’s factoring

Instructions

Copy and paste the following code into the main.c file of  a new project:

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


int main()
{
    float a,b,c,D,root1,root2;

    printf("Please enter a value for a...\n");
    scanf("%f",a);

    printf("Please enter a value for b...\n");
    scanf("%f",&b);

    printf("Please enter a value for c...\n");
    scanf("%f",&c);

    printf("\n");

    D=(b*b)-(4*a*c);


    if(D<0)
    {
        printf("no roots\n Exiting program...\n");
    }
    else if (D=0)
    {
        root1=(b*b)/(2*a);
        printf("Equation has only one root = %.2f \n Exiting program...\n ");

    }
    else
    {
        root1=((b*b)-sqrt(D))(2*a);
        root2=((b*b)+sqrt(D))/(2*a);
        printf("Roots are %.2f and %.2f \n Exiting program...\n",root1,root2);

    }
    return 1;




}

  1. Debug your program so that it produces correct results in all cases.
  2. Modify your code to use functions.
    • Move the code for calculating the Discriminant into another function. Do this by
      • Create a new function,which returns the value of the discriminant. The new function should implement the function declaration: float discriminant (acoef,bcoef,ccoeff);
      • Change the line which contains
        D=(b*b)-(4*a*c); to D=discriminant(a,b,c);
    • Move the code for calculating root 1 and root 2 into 2 separate functions. To do this, you must create suitable function declarations and accompanying function declarations which can accomplish the task.

© 2020  Vedesh Kungebeharry. All rights reserved. 

Introduction to functions

0008.1

Required reading: C functions (This is a link to an external website)

Exercise

Similar to our previous example, create a program which contains:

  1. A function which returns the area of a circle given its radius (assume all variables used are double)
  2. A Function which returns the volume of a circle given the radius(assume all variables used are double)
  3. A function which does not return any data and takes no parameters but outputs the author of your program .  Name your function Signature.

Solution

Note the function declarations.  This is necessary for proper execution of our code.

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

//global variables
double area,volume;
double radius;
const double PI = 3.14;

//function declarations
double Area(double radius);
double Volume(double radius);
void Signature();

int main()
{
    //initialization
    area,radius,volume = -999;

    printf("enter radius\n");
    scanf("%lf", &radius);

    area    =   Area(radius);
    volume  =   Volume(radius);

    printf("The  Area is \t: \t%.2f\n",area);
    printf("The  Volume is \t: \t%.2f\n",volume);

    Signature();



}

double Volume(double radius)
{
    return((4/3)*PI*radius*radius*radius);
}

double Area(double radius)
{
    return (PI*radius*radius);
}

void Signature()
{
	//ASCII Art Generated using https://patorjk.com/software/taag/#p=testall&f=Big&t=kunge
    printf("Thanks for using my program!  \n");
    printf("  _  __                       \n");
    printf(" | |/ /                       \n");
    printf(" | ' /_   _ _ __   __ _  ___  \n");
    printf(" |  <| | | | '_ \ / _` |/ _ \ \n");
    printf(" | . \.|_| | | | | (_| |  __/ \n");
    printf(" |_|\_\__,_|_| |_|\__, |\___| \n");
    printf("                   __/ |      \n");
    printf("                  |___/       \n");
    printf("Author: Vedesh Kungebeharry   \n");
    printf("contact: VKunge@gmail.com     \n");
    printf("exiting...  \n");


}

© 2020  Vedesh Kungebeharry. All rights reserved. 

Exercises – Finding the Volume Of Sphere, Quadratic Equation Roots

0007.(1 and 2)

Attempt the 2 c programming exercises below.  If you are having a hard time starting, try:

  1. Examining the solution the exercise 2 for guidance.
  2. Attempt exercise 1 (no solution was provided here)
  3. Ideally, attempt exercise 2 without looking at the solution.

Exercises:


1. Create a program which accepts the radius for a circle/sphere  and outputs the volume and maximum cross sectional area.  Your program must prompt for input, and produce relevant results.

2. Given the coefficient  a b and c of a quadratic equation in the form y=ax^2+bx+c output the solution to the equation when y=0

Solution to Find the roots of a Quadratic Equation:

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


int main()
{
    float a,b,c,D,root1,root2;


    printf("Please enter a value for a...\n");
    scanf("%f",&a);

    printf("Please enter a value for b...\n");
    scanf("%f",&b);

    printf("Please enter a value for c...\n");
    scanf("%f",&c);

    printf("\n");

    D=(b*b)-(4*a*c);


    if(D<0)
    {
        printf("no roots\n Exiting program...\n");
    }
    else if (D==0)
    {
        root1=(-b)/(2*a);
        printf("Equation has only one root = %.2f \n Exiting program...\n ");

    }
    else
    {
        root1=((-b)-sqrt(D))/(2*a);
        root2=((-b)+sqrt(D))/(2*a);
        printf("Roots are %.2f and %.2f \n Exiting program...\n",root1,root2);

    }
    return 1;




}

Strategy for Coding using Exercise 1 as an example

© 2020  Vedesh Kungebeharry. All rights reserved. 

Stacks and Queues Discussion and Variant implementation

This post is intended for use in class and review. In this lesson we will observe some multiple choice question examples to provoke thoughts on the fundamental principles governing stack and queue operations.

Note that the multiple choice questions are not included on this website.

Discussion: Stacks and Queues

If a stack is implemented using a one-dimensional array and the top of the stack is always found at location 0 in the array, how would you implement push and pop?

That is, at the end of any push or pop operation, the top of the stack is stored at location 0.

Write separate algorithms for your push and pop operations.


After considering the scenario above do you think it’s possible to implement a stack using the wrap-around technique (that was used in the implementation of a queue ) to omit the intermediate copying of elements? Consider this scenario where the first element that was pushed onto the stack goes into location 0, and every time a pop operation occurs top is incremented (unless the stack is empty) , and every time a push operation occurs top is decremented.

Justify your conclusion.


Write an algorithm which can be used to reverse the elements of a queue by using a stack. Accomplish this task by only using valid stack and queue operations.


Assuming you are required to write a c program that must use two separate stacks and five separate queues , what modifications would you need to make to the coded implementations that will already demonstrated in class.


Write the algorithm for the dequeue operation in a traditional queue with no wrap around, i.e every time an element is dequeued , all elements of are copied down one location such that location 0 in the array stores the new head of the queue.


© 2020  Vedesh Kungebeharry. All rights reserved.