1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) 1649 assignment 1 (pass) FPT Greenwich
Data Structures and Algorithms ASSIGNMENT 1
Introduction to Abstract Data Type
In computer science, an abstract data type (ADT) serves as a mathematical model for a class of data structures that exhibit similar behaviors It is characterized by the operations that can be performed on it and the mathematical constraints governing those operations, including their effects and potential costs.
Java's List interface serves as a blueprint for List implementations, as it does not specify any behavior due to the absence of a concrete List class Instead, it outlines a collection of methods that must be implemented by other classes, such as ArrayList and LinkedList, to qualify as a List.
The following are the advantages of using the data structure:
• These are the essential ingredients used for creating fast and powerful algorithms
• They help us to manage and organize the data
• Data structures make the code cleaner and easier to understand
An index array of size 4 consists of index locations 0, 1, 2, and 3 In an array, elements are stored in contiguous memory locations, with the first element located at memory address 1000, the second at 1004, the third at 1008, and the fourth at 1012.
An integer array occupies 4 bytes per element, resulting in a 4-byte difference between the memory addresses of each element The array contains the values 10, 20, 30, and 40, along with their corresponding index positions and memory addresses as part of its implementation.
The abstract or logical view of the integer array can be stated as:
• It stores a set of elements of integer type
• It reads the elements by position, i.e., index
• It modifies the elements by index
The implementation view of the integer array:
1.4/ Comparison between ADT and OOP
Abstract ADT is abstraction that define set of value and set of operations on these values
It is a sell-contained component which consist of methods and properties to make certain type of data useful
Definition It is a type for objects whose behaviour is defined by a set of value and a set of operation
It is basic unit of OOP
Using User-defined data type It is an instance of class
Data type ATD is not definitely an OOP concept Objects is an OOP concept
Example List, Stacks, Queue, Sets, etc Test T=new Test
Use Allocate memory when data is stored When we instantiate an object then memory is allocated.
Linear Data Structures
Data structures are categorized into linear and non-linear types Linear data structures, where elements are organized in a sequential manner, are straightforward to implement However, they may not be ideal for complex programs Common examples of linear data structures include arrays, stacks, queues, and linked lists.
A linked list is a linear data structure that includes a series of connected nodes Here, each node stores the data and the address of the next node For example,
In a linked list, the first node is referred to as HEAD, while the last node is recognized by its next pointer, which points to NULL.
Singly linked lists are one of the various types of linked lists, which also include doubly and circular linked lists This article will specifically explore the characteristics and functionalities of singly linked lists For more information on other linked list types, please refer to the section on Types of Linked List.
Linked lists offer significant advantages over arrays in terms of memory efficiency and dynamic allocation While arrays require a predetermined number of memory cells at the time of creation, linked lists allocate memory only when data is added, allowing for more flexible and efficient use of memory resources.
• It can also store elements anywhere in memory without the need for contiguous memory cells like arrays
• Quick insertion (Add very quickly with complexity only O (1))
• Slow search: Searching is slow due to having to traverse many nodes to get to the node you are looking for
In Java programming, a Stack is utilized to store elements in a last-in, first-out (LIFO) manner For instance, when stacking 10 plates, the first plate placed is at the bottom, while the last one is at the top, meaning the top plate is the first to be removed This principle of operation distinguishes a Stack from a Queue, where the first element added is the first one to be retrieved.
The process of putting (push) a new data element onto the stack is also known as a PUSH operation The push operation includes the following steps:
Step 1: check if the stack is full or not
Step 2: if the stack is full, the process fails and exits
Step 3: if the stack is not full, increment top to point to the next piece of free memory
Step 4: add the data element to the position where top is pointing on the stack
The POP operation allows access to an element's content while removing it from the stack In an array implementation, the data element remains in memory, but the top pointer is adjusted to reference the next value Conversely, in a linked list implementation, the pop() operation deletes the data element and frees up memory space.
Step 1: check if the stack is empty or not
Step 2: if the stack is empty, the process fails and exits
Step 3: if the stack is not empty, access the data element at which top is pointing
Step 4: reduce the value of top by 1
In Java programming, the Queue collection is designed to store elements in a specific order, operating on a First In First Out (FIFO) principle This means that the first element added to the queue will be the first one to be removed, similar to how customers are served in a supermarket line—those who arrive first are served first, while those who arrive later must wait their turn.
Sorting Algorithms
Sorting involves organizing data in a specific sequence, such as ascending or descending alphabetical or numerical order In computer science, sorting algorithms play a crucial role in determining the method of arranging data effectively This process is akin to the way entries are organized in a dictionary, ensuring that information is easily accessible and systematically ordered.
Sorting algorithms are described in the following table along with the description
Bubble Sort is the most straightforward sorting algorithm, which works by repeatedly shifting the largest element to the end of the array This method involves comparing each element with its adjacent one and swapping them as needed.
Bucket sort, also referred to as bin sort, is a sorting algorithm that distributes elements into separate arrays known as buckets Each bucket is then sorted individually using a different sorting method.
Comb Sort is an improved version of Bubble Sort that enhances efficiency by eliminating small values, or "turtle values," located near the end of the list, rather than merely comparing adjacent values.
Counting Sort is a sorting algorithm that organizes objects based on their key values, which are typically small integers This technique counts the occurrences of each key and stores these values, allowing for the creation of a new array The new array is formed by accumulating the previous key elements and assigning them to the corresponding objects.
Heap sort involves maintaining a min heap or max heap from an array of elements, depending on the chosen method The sorting process is carried out by repeatedly removing the root element of the heap.
Insertion sort is a straightforward sorting algorithm that places each element of an array in its correct position This method is akin to how one would organize a deck of cards while playing bridge.
Merge sort is a sorting algorithm that utilizes the divide and conquer strategy It begins by splitting the list into equal subsets, then recursively sorts each half using merge sort Finally, the sorted halves are merged back together to create a fully sorted array.
Quick sort is an efficient sorting algorithm that operates with a time complexity of O(n log n) for comparisons Similar to merge sort, it utilizes the divide and conquer strategy to achieve optimal sorting performance.
9 Radix Sort In Radix sort, the sorting is done as we do sort the names according to their alphabetical order It is the linear sorting algorithm used for Integers
Selection sort is an algorithm that identifies the smallest element in an array and positions it at the beginning of the list It then locates the second smallest element and places it in the second position, continuing this process until all elements are sorted However, selection sort has a time complexity of O(n²), making it less efficient than insertion sort.
11 Shell Sort Shell sort is the generalization of insertion sort which overcomes the drawbacks of insertion sort by comparing elements separated by a gap of several positions
Bubble Sort is a straightforward sorting algorithm that operates by comparing two adjacent elements and swapping them if they are out of order This process can be executed in a top-down (left to right) or bottom-up (right to left) manner Also referred to as a direct comparison sort, Bubble Sort is classified as a comparison sort algorithm due to its reliance on element comparisons.
Selection Sort is a straightforward sorting algorithm that operates through in-place comparisons It divides the list into two sections: a sorted portion on the left and an unsorted portion on the right At the start, the sorted section is empty, while the unsorted section comprises the entire original list.
The selection sort algorithm involves choosing the smallest element from an unsorted array and swapping it with the leftmost element, effectively placing it in the sorted array This procedure is repeated until all elements from the unsorted array are transferred to the sorted array.
Searching Algorithms
In computer science, a search algorithm processes a problem and provides a solution by evaluating various potential answers Most algorithms analyzed by computer scientists for problem-solving are categorized as search algorithms The complete range of possible solutions to a given problem is referred to as the search space Brute-force search is one method used within this context.
Primary search algorithms utilize straightforward and intuitive methods, while informed search algorithms leverage heuristics to enhance efficiency by applying knowledge of the search space's structure, thereby reducing search time.
Each element is checked, and if any match is found, that element is returned; If not found, the search continues until all data is found
Binary Search has a great advantage in terms of time complexity when compared to Linear Search Linear Search has a worst-case complexity of Ο(n) while Binary Search is Ο(log n)
Binary Search efficiently locates a specific element by comparing it to the middle element of a sorted data set If a match is found, the index of the element is returned If the search element is greater than the middle value, the search continues in the right subarray; if it is smaller, the left subarray is examined This process repeats until the element is found or the subarray is exhausted.
P2/ Determine the operations of a memory stack and how it is used to implement function calls in a computer.
Memory stack
Stack memory is a temporary data storage mechanism that operates on a first-in-last-out basis, utilizing a register known as the Stack Pointer to track the current memory location The Stack Pointer is automatically adjusted during stack operations, which commonly include PUSH and POP commands at the start and end of functions or subroutines When a function begins, the current register contents from the calling program are saved to stack memory via PUSH operations, and at the function's conclusion, these values are restored using POP operations It is crucial for each PUSH operation to have a corresponding POP operation; failure to do so can lead to unpredictable behaviors, such as returning to incorrect addresses.
The Push Operation
The push operation is the process of adding data items to a stack, similar to how plates are inserted into a restaurant plate dispenser This operation effectively increases the number of plates available in the dispenser.
In a stack data structure, the first plate is pushed to the bottom, with all subsequent plates following in order Consequently, the first data item inserted becomes the most inaccessible, residing at the bottom of the stack.
The Pop Operation
The pop operation is essential for removing data items from a stack, where the last item added is the first to be removed In our plate dispenser example, the top plate is the first to be popped out, similar to how a spring loading system pushes the remaining plates upward with each removal This process continues in memory, ensuring that items are consistently popped out in a last-in, first-out order.
P3 Using an imperative definition, specify the abstract data type for a software stack.
Formal specification
Formal specifications in computer science are mathematical techniques designed to assist in the implementation of systems and software They describe systems, analyze behaviors, and support design by verifying essential properties using effective reasoning tools These specifications are considered formal due to their defined syntax, domain semantics, and ability to infer valuable information.
As computer systems grow increasingly powerful and influential in society, there is a pressing need for improved techniques to ensure reliable software design and implementation Established engineering disciplines rely on mathematical analysis for product design and validation, highlighting the importance of official specifications in enhancing software engineering reliability While testing remains a widely used method for improving code quality, the integration of formal specifications can significantly bolster the reliability of software systems.
• A higher level of rigor enables a better understanding of the problem
• Defects are uncovered that would likely go unnoticed with traditional specification methods
• identify defects earlier in the life cycle
• Can guarantee the absence of certain defects
• Used as an adjunct to, not a replacement for, standard quality assurance methods
• Formal methods are not a panacea, but can increase confidence in a product’s reliability if applied with care and skill
• Very useful for consistency checks, but cannot assure completeness of a specification
What are the pre-condition, post-condition, and error-condition?
In programming, public methods should validate parameters and throw specified exceptions, as assertions are intended for correctness checks rather than exception handling When an assertion fails, it raises an AssertionError, which is a generic exception that may not be easily managed by the programmer Conversely, private methods can be utilized to verify the validity of variables regardless of user input By placing assertions before executing operations, we effectively perform precondition checks.
A post condition is a statement that outlines the expected outcome of an action once the operation has successfully completed It specifies the conditions that will hold true after the action has finished executing.
A postcondition for a method invocation is a requirement that must hold true upon returning from the method For instance, when a natural logarithm method is called with an input \( X \) and returns a value \( Y \), the relationship must be valid within the specified precision limits.
An error message will be generated if there is no active ON-unit when the ERROR condition occurs, or if the ON-unit fails to utilize a GOTO statement to exit the block and recover from the condition.
The ERROR condition serves as a universal action for various situations, allowing for a streamlined approach to condition checking Instead of evaluating each condition individually, this method enables a more efficient assessment of multiple conditions simultaneously.
The ERROR condition is raised under the following circumstances:
• As a result of the implicit action for a condition, which is to raise the ERROR condition
• As a result of the normal return action for some conditions, such as SUBSCRIPTRANGE CONVERSION or when no retry is attempted
• As a result of an error (for which there is no other PL/I-defined condition) during program execution
• As a result of a SIGNAL ERROR statement
In order to prevent a loop of ERROR conditions, the first statement in any ON ERROR block should be
3/ Specify stack Operation using this formal language
After a function completes its execution, a new set of conditions, known as postconditions, must be evaluated to ensure that the function operates as intended These postconditions are essential for verifying that a specific implementation meets the required standards For instance, in the context of a mathematical square-foot function defined using the VDM approach, both preconditions and postconditions play a crucial role in validating its functionality.
SQRT(x:real)y:real pre x >=0 post y*y = x and y >= 0
The input for the square root function must be zero or positive, ensuring that the output, when squared, equals the input and remains non-negative, as sqrt() always yields the positive square root This relationship can also be expressed using a Hoare triple {P}A{Q}, where P and Q are predicates, and A represents a program that successfully terminates upon execution.
The Vienna Development Method (VDM), based on Hoare Logic, established a foundation for software design concepts, although there are now several less formal specification languages available One such language is OCL, which complements the Unified Modeling Language (UML) by enabling the use of formal English to articulate conditions and invariants.
Formal methods such as VDM and OCL are characterized by their representation as Design by Contract This approach has led to further denormalization, which may not have been Bertrand Meyer's original intention when he introduced the metaphor to illustrate the relationship between client code and the service-providing code.
Applying VDM to a stack ADT, a possible set of operations on a could be formulated as:
The Stack class is initialized with a maximum size, creating a new Stack instance where the size is set to zero, and it is both empty and not full The size of the Stack can range from zero to its maximum size, and it is defined as the difference between the number of valid pushes and pops since its creation The Stack can be checked for emptiness, returning true if its size is zero, and for fullness, returning true if its size equals the maximum size The top function retrieves the item at the top of the Stack, ensuring that the Stack is not empty before performing the operation.
{defines i, S after i has been popped off and pushed back on again} push(S: Stack, i: Item): None
P a g e | 21 post top(S) = i and size(S) = n + 1 pop(S: Stack): r: Item pre not is_empty(S) and i = top(S) and n = size(S) post r = i and size(S) = n - 1
The precondition true indicates that the operation is valid for any stack state, meaning there are no preconditions An ADT invariant defines conditions that are always true for any instance and remain true throughout its lifetime, regardless of the operations performed Consequently, it is suitable to position the invariant after the postconditions when initializing an instance.
When an item is added to the stack, it becomes the top element, and calling pop() will remove this item This behavior defines a stack as a Last-In-First-Out (LIFO) structure, in contrast to a queue, which operates on a First-In-First-Out (FIFO) basis.
A traditional stack implementation, avoiding OOP techniques, is ideally placed in a separate file named 'stack.py' The initial step involves defining a record to store the persistent data for the stack instance.
Persistent data refers to information that remains accessible across multiple accesses of a stack For data to persist between different runs of a program, it must be stored in backing storage, such as a file saved to disk or another medium.
To implement a stack, an array is needed to store items in the order they are pushed, with a stack pointer indicating the next available position In a zero-based indexed array, the size of the stack and the value of the stack pointer are identical, meaning only the stack pointer is necessary The maximum size of the stack corresponds to the size of the array In Python, this can be achieved using the following code: ```pythonfrom array_types import array_class Stack: def init (self, max_size): self.array = array_(max_size) self.SP = 0 # the stack pointer self.max_size = max_size```
Now define the stack operations: def push(stack, item): stack._array[stack.SP] = item stack.SP += 1 def pop(stack): stack.SP -= 1 return stack.array[stack.SP]