diff --git a/README.md b/README.md index 1a10114..761d5db 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,5 @@ We have also done some basic benchmarking with some of the most commonly used pa ### implemented collections: - [Stack (using linked list)](stack/README.md) -- [Queue (using linked list)](queue/README.md) \ No newline at end of file +- [Queue (using linked list)](queue/README.md) +- [Single linked list](list/singleLinkedList_README) \ No newline at end of file diff --git a/list/list_contract.go b/list/list_contract.go new file mode 100644 index 0000000..5bafebc --- /dev/null +++ b/list/list_contract.go @@ -0,0 +1,49 @@ +package list + +type IDeepCopy interface { + Copy() interface{} + Equal(val interface{}) bool +} + +type IList[T IDeepCopy] interface { + // Add adds data to the end of the list + Add(data *T) (resultIndex int) + // AddAtIndex adds data to the index in the list and shifts all data to right + // if the index is out of bound then return error + AddAtIndex(index int, data *T) (err error) + // Remove removes data from the list and returns error if the data is not found + Remove(data *T) (removedIndex int, err error) + // RemoveAtIndex removes data at the index, if the index is not valid then returns error + RemoveAtIndex(index int) (data *T, err error) + // Count return the count of elements in the list + Count() int + // Get gets data at the index, if index is not valid then it returns error + Get(index int) (data *T, err error) + // Set updates the data at the index, if index is not valid then returns error + Set(index int, data *T) error + // Find helps to get the first occourance if the data that matches according to the filter func and also returns index + // if index is -1 then the data is not found + Find(f filterfunc[T]) (index int) +} + +type filterfunc[T IDeepCopy] func(data *T) bool + +type IItratorList[T IDeepCopy] interface { + IList[T] + // Filter helps to get the all data that matches according to the filter func and also returns index + Filter(f filterfunc[T]) []T + // DeepCopy this is used to create a copy of the list + DeepCopy() []T +} + +type IIndexedItratorList[T IDeepCopy] interface { + IItratorList[T] + // FindByIndexedKey get the first occourance if the data that matches according to the filter func and also returns index, + // this helps in fast search ad it will do a binary search on the indexKey + // this is similar to Non-Clustered Index in database + FindByIndexedKey(indexKey string, key string) (data T, index int) + // FindByIndexedKey get the first occourance if the data that matches according to the filter func and also returns index, + // this helps in fast search ad it will do a binary search on the indexKey + // this is similar to Non-Clustered Index in database + FilterByIndexedKey(indexKey string, key string) []T +} diff --git a/list/singleLinkedList_README.md b/list/singleLinkedList_README.md new file mode 100644 index 0000000..92c962c --- /dev/null +++ b/list/singleLinkedList_README.md @@ -0,0 +1,107 @@ +# Single List list + +This package implements generic Single linked list, the data of the list should implement the `IDeepCopy` interface. + +## Quick Start +```go +package main + +import ( + "github.com/architagr/golang_collections/list" +) + + +type Integer int + +func InitInteger(val int) Integer { + return Integer(val) +} +func (obj Integer) Copy() interface{} { + return obj +} + +func (obj Integer) Equal(val interface{}) bool { + x := val.(Integer) + return int(x) == int(obj) +} + +func main() { + linkedList := InitSingleLinkedList[Integer]() + intVal := InitInteger(0) + linkedList.Add(&intVal) + intVal2 := InitInteger(1) + err := obj.AddAtIndex(0, &intVal2) + if err != nil{ + panic(fmt.Errorf("Error in adding at 0th index: %s", err.Error())) + } + data, err := linkedList.Get(0) + if err != nil{ + panic(fmt.Errorf("Error in getting value at a index: %s", err.Error())) + } + intVal3 := InitInteger(2) + err := linkedList.Set(0, &intVal3) + if err != nil { + panic(fmt.Errorf("Error in setting value at a index: %s", err.Error())) + } + index, err := linkedList.Remove(&intVal2) + if err != nil { + panic(fmt.Errorf("error when removing a node: %s", err.Error())) + } + + data, err := linkedList.RemoveAtIndex(1) + if err != nil { + panic(fmt.Errorf("error when removing a node at a index: %s", err.Error())) + } + + index := obj.Find(func(val *Integer) bool { + x := InitInteger(2) + return val.Equal(x) + }) + if index == -1 { + panic(fmt.Errorf("error when finding value: %s", err.Error())) + } + +} +``` +## Functions available + +the package exposes below listed functions + +### InitSingleLinkedList[T IDeepCopy] + +created a new single linked list that can have nodes that can hold data of type `IDeepCopy`. +T can be of any data type that implements `IDeepCopy`. + +### Method in the object of single linked list +#### Add(data *T) (resultIndex int) + +this is a function adds data to the end of the list. + +#### AddAtIndex(index int, data *T) (err error) + +this function adds data to the index in the list and shifts all data to right if the index is out of bound then return error. + +#### Remove(data *T) (removedIndex int, err error) + +This function removes data from the list and returns error if the data is not found. + +#### RemoveAtIndex(index int) (data *T, err error) + +This function removes data at the index, if the index is not valid then returns error. + +#### Count() int + +This function return the count of elements in the list. + +#### Get(index int) (data *T, err error) + +This function gets data at the index, if index is not valid then it returns error. + +#### Set(index int, data *T) error + +This function updates the data at the index, if index is not valid then returns error + +#### Find(f filterfunc[T]) (index int) + +This function helps to get the first occourance if the data that matches according to the filter func and also returns index, if index is -1 then the data is not found + diff --git a/list/single_linked_list.go b/list/single_linked_list.go new file mode 100644 index 0000000..b8ba91a --- /dev/null +++ b/list/single_linked_list.go @@ -0,0 +1,148 @@ +package list + +import "fmt" + +func InitSingleLinkedList[T IDeepCopy]() IList[T] { + return &singleLinkedList[T]{ + head: nil, + tail: nil, + indexMap: make(map[int]*singleLinkedListNode[T]), + } +} + +type singleLinkedListNode[T IDeepCopy] struct { + data *T + next *singleLinkedListNode[T] +} + +func initSingleLinkedListNode[T IDeepCopy](data *T) *singleLinkedListNode[T] { + return &singleLinkedListNode[T]{ + data: data, + next: nil, + } +} + +type singleLinkedList[T IDeepCopy] struct { + head, tail *singleLinkedListNode[T] + indexMap map[int]*singleLinkedListNode[T] +} + +func (l *singleLinkedList[T]) Add(data *T) (resultIndex int) { + newNode := initSingleLinkedListNode(data) + if l.head == nil { + l.head = newNode + l.tail = newNode + } else { + l.tail.next = newNode + l.tail = newNode + } + l.indexMap[l.Count()] = newNode + return l.Count() +} + +func (l *singleLinkedList[T]) AddAtIndex(index int, data *T) (err error) { + err = l.validateIndex(index) + if err != nil { + return + } + + newNode := initSingleLinkedListNode(data) + + for i := l.Count() - 1; i >= index; i-- { + l.indexMap[i+1] = l.indexMap[i] + } + l.indexMap[index] = newNode + + if index == 0 { + newNode.next = l.head + l.head = newNode + } else { + newNode.next = l.indexMap[index+1] + l.indexMap[index-1].next = newNode + } + + return +} + +func (l *singleLinkedList[T]) Remove(data *T) (removedIndex int, err error) { + temp := l.head + removedIndex = 0 + for temp != nil { + if (*data).Equal(*temp.data) { + _, err = l.RemoveAtIndex(removedIndex) + break + } + removedIndex++ + temp = temp.next + } + if removedIndex == l.Count() { + err = fmt.Errorf("data not found") + removedIndex = -1 + } + return +} +func (l *singleLinkedList[T]) RemoveAtIndex(index int) (data *T, err error) { + err = l.validateIndex(index) + if err != nil { + return + } + + if index == 0 { + l.head = l.head.next + } else if index+1 == l.Count() { + l.indexMap[index-1].next = nil + l.tail = l.indexMap[index-1] + } else { + l.indexMap[index-1].next = l.indexMap[index+1] + } + + initialCount := l.Count() + data = l.indexMap[index].data + delete(l.indexMap, index) + for i := index; i < initialCount-1; i++ { + l.indexMap[i] = l.indexMap[i+1] + } + delete(l.indexMap, initialCount-1) + return +} +func (l *singleLinkedList[T]) Count() int { + return len(l.indexMap) +} +func (l *singleLinkedList[T]) validateIndex(index int) error { + if index < 0 || index >= l.Count() { + return fmt.Errorf("invalid index") + } + return nil +} +func (l *singleLinkedList[T]) Get(index int) (data *T, err error) { + err = l.validateIndex(index) + if err != nil { + return + } + return l.indexMap[index].data, nil +} +func (l *singleLinkedList[T]) Set(index int, data *T) error { + err := l.validateIndex(index) + if err != nil { + return err + } + node := l.indexMap[index] + node.data = data + return nil +} + +func (l *singleLinkedList[T]) Find(f filterfunc[T]) (index int) { + temp := l.head + index = 0 + for temp != nil { + if f(temp.data) { + break + } + index++ + temp = temp.next + } + if index == l.Count() { + index = -1 + } + return +} diff --git a/list/single_linked_list_test.go b/list/single_linked_list_test.go new file mode 100644 index 0000000..45b5dde --- /dev/null +++ b/list/single_linked_list_test.go @@ -0,0 +1,358 @@ +package list + +import ( + "testing" +) + +type Integer int + +func InitInteger(val int) Integer { + return Integer(val) +} +func (obj Integer) Copy() interface{} { + return obj +} + +func (obj Integer) Equal(val interface{}) bool { + x := val.(Integer) + return int(x) == int(obj) +} + +var singleLinkedListSampleData = []int{1, 2, 3} + +func GetSingleLinkedList() IList[Integer] { + obj := InitSingleLinkedList[Integer]() + // adding dummy data + for _, val := range []int{1, 2, 3} { + intVal := InitInteger(val) + obj.Add(&intVal) + } + return obj +} + +func TestInitilizationSingleLinkedList(t *testing.T) { + obj := InitSingleLinkedList[Integer]() + + if obj == nil || obj.Count() != 0 { + t.Errorf("Single linked list not initilized correctly") + } +} + +// #region test Add node method +func TestAddNodeToNewSingleLinkedList(t *testing.T) { + obj := InitSingleLinkedList[Integer]() + val := InitInteger(1) + obj.Add(&val) + if obj.Count() != 1 { + t.Errorf("New Single linked list did not add 1 data correctly, expected element count is 1, got %d", obj.Count()) + } + + data, err := obj.Get(0) + if err != nil { + t.Fatalf("Single Linked List should not return error if trying to access valud index") + } + + if int(*data) != 1 { + t.Errorf("Single Linked List should return valid data") + } +} + +func TestAddNodeToSingleLinkedList(t *testing.T) { + obj := GetSingleLinkedList() + if obj.Count() != len(singleLinkedListSampleData) { + t.Errorf("adding multiple node to single lisnked list failed, expected count %d, got:%d", len(singleLinkedListSampleData), obj.Count()) + } + for validatingIndex, val := range singleLinkedListSampleData { + data, err := obj.Get(validatingIndex) + if err != nil { + t.Fatalf("Single Linked List should not return error if trying to access valud index") + } + + if int(*data) != val { + t.Errorf("Single Linked List should return valid data") + } + } +} + +// #endregion + +// #region test AddAtIndex +func TestAddNodeAtInvalidIndex(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.AddAtIndex(len(singleLinkedListSampleData)+10, &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to add at a invalid index") + } +} + +func TestAddNodeAtNegativeIndex(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.AddAtIndex(-1, &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to add at a invalid index") + } +} +func TestAddNodeAfterLastNode(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.AddAtIndex(len(singleLinkedListSampleData), &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to add at a invalid index") + } +} + +func TestAddNodeAtValidIndexInBetween(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + index := len(singleLinkedListSampleData) / 2 + err := obj.AddAtIndex(index, &intVal) + if err != nil { + t.Errorf("Single Linked List should not return error if we try to add at a valid index") + } + data, _ := obj.Get(index) + if int(*data) != 10 { + t.Errorf("Single Linked List should return valid data for the index") + } +} + +func TestAddNodeAtValidIndexAtBeginning(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + index := 0 + err := obj.AddAtIndex(index, &intVal) + if err != nil { + t.Errorf("Single Linked List should not return error if we try to add at a valid index") + } + data, _ := obj.Get(index) + if int(*data) != 10 { + t.Errorf("Single Linked List should return valid data for the index") + } + +} + +func TestAddNodeAtValidIndexAtEnd(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + index := len(singleLinkedListSampleData) - 1 + err := obj.AddAtIndex(index, &intVal) + if err != nil { + t.Errorf("Single Linked List should not return error if we try to add at a valid index") + } + data, _ := obj.Get(index) + if int(*data) != 10 { + t.Errorf("Single Linked List should return valid data for the index") + } + if obj.Count() != len(singleLinkedListSampleData)+1 { + t.Errorf("Single Linked List error when adding at end") + } +} + +// #endregion + +// #region test Get +func TestGetNodeWithValidIndex(t *testing.T) { + obj := GetSingleLinkedList() + for _, validatingIndex := range []int{1, 0} { + data, err := obj.Get(validatingIndex) + if err != nil { + t.Fatalf("Single Linked List should not return error if trying to access valid index") + } + + if int(*data) != singleLinkedListSampleData[validatingIndex] { + t.Errorf("Single Linked List should return valid data") + } + } + if obj.Count() == 0 { + t.Errorf("Single Linked List get function also removing nodes") + } +} + +func TestGetNodeWithNegativeIndex(t *testing.T) { + obj := GetSingleLinkedList() + _, err := obj.Get(-1) + if err == nil { + t.Errorf("Single Linked List should return error if we try to access negitive index") + } +} +func TestGetNodeWithOutofBoundIndex(t *testing.T) { + obj := GetSingleLinkedList() + _, err := obj.Get(len(singleLinkedListSampleData) + 10) + if err == nil { + t.Errorf("Single Linked List should return error if we try to access index that does not exist") + } +} + +func TestGetNodeInEmptyList(t *testing.T) { + obj := InitSingleLinkedList[Integer]() + _, err := obj.Get(0) + if err == nil { + t.Errorf("Single Linked List should return error if we try to access any index when list is empty") + } +} + +// #endregion + +// #region test Set +func TestSetNodeWithValidIndex(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.Set(0, &intVal) + if err != nil { + t.Fatalf("Single Linked List should not return error if trying to set valid index") + } + data, err := obj.Get(0) + if err != nil { + t.Fatalf("Single Linked List should not return error if trying to access valid index") + } + + if int(*data) != 10 { + t.Errorf("Single Linked List should return valid data") + } +} + +func TestSetNodeWithNegativeIndex(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.Set(-1, &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to access negitive index") + } +} +func TestSetNodeWithOutofBoundIndex(t *testing.T) { + obj := GetSingleLinkedList() + intVal := InitInteger(10) + err := obj.Set(len(singleLinkedListSampleData)+10, &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to set index that does not exist") + } +} + +func TestSetNodeInEmptyList(t *testing.T) { + obj := InitSingleLinkedList[Integer]() + intVal := InitInteger(10) + err := obj.Set(0, &intVal) + if err == nil { + t.Errorf("Single Linked List should return error if we try to set any index when list is empty") + } +} + +// #endregion + +// #region test Remove +func TestRemovingValidData(t *testing.T) { + obj := GetSingleLinkedList() + removingIndex := 0 + data, _ := obj.Get(removingIndex) + index, err := obj.Remove(data) + if err != nil { + t.Errorf("Single Linked List error while deleting a node") + } + if index != removingIndex { + t.Errorf("Single Linked List removed wrong node") + } +} +func TestRemovingDataThatDoesNotExist(t *testing.T) { + obj := GetSingleLinkedList() + val := InitInteger(10) + _, err := obj.Remove(&val) + if err == nil { + t.Errorf("Single Linked List should have error while deleting a node that does not exist") + } +} + +// #endregion + +// #region test RemoveAtIndex +func TestRemoveValidIndex(t *testing.T) { + obj := GetSingleLinkedList() + data, err := obj.RemoveAtIndex(1) + if err != nil { + t.Errorf("Single linked list should not throw error when trying to remove from valid index") + } + + if int(*data) != singleLinkedListSampleData[1] { + t.Errorf("Single linked list did not delete correct data") + } + if obj.Count() != len(singleLinkedListSampleData)-1 { + t.Errorf("Single linked list did not delete data") + } +} +func TestRemoveValidIndexFromBeginning(t *testing.T) { + obj := GetSingleLinkedList() + data, err := obj.RemoveAtIndex(0) + if err != nil { + t.Errorf("Single linked list should not throw error when trying to remove from valid index") + } + + if int(*data) != singleLinkedListSampleData[0] { + t.Errorf("Single linked list did not delete correct data") + } + + if obj.Count() != len(singleLinkedListSampleData)-1 { + t.Errorf("Single linked list did not delete data") + } +} + +func TestRemoveValidIndexFromEnd(t *testing.T) { + obj := GetSingleLinkedList() + data, err := obj.RemoveAtIndex(len(singleLinkedListSampleData) - 1) + if err != nil { + t.Errorf("Single linked list should not throw error when trying to remove from valid index") + } + + if int(*data) != singleLinkedListSampleData[len(singleLinkedListSampleData)-1] { + t.Errorf("Single linked list did not delete correct data") + } + + if obj.Count() != len(singleLinkedListSampleData)-1 { + t.Errorf("Single linked list did not delete data") + } +} + +func TestRemoveInValidNegitiveIndex(t *testing.T) { + obj := GetSingleLinkedList() + _, err := obj.RemoveAtIndex(-1) + if err == nil { + t.Errorf("Single linked list should throw error when trying to remove from invalid index") + } +} + +func TestRemoveInValidIndex(t *testing.T) { + obj := GetSingleLinkedList() + _, err := obj.RemoveAtIndex(obj.Count() + 10) + if err == nil { + t.Errorf("Single linked list should throw error when trying to remove from invalid index") + } +} + +// #endregion + +// #region test Find +func TestFindValidData(t *testing.T) { + obj := GetSingleLinkedList() + index := obj.Find(func(val *Integer) bool { + x := InitInteger(2) + return val.Equal(x) + }) + + if index != 1 { + t.Errorf("Single linked list should be able to find a valid data") + } + +} +func TestFindInValidData(t *testing.T) { + obj := GetSingleLinkedList() + index := obj.Find(func(val *Integer) bool { + x := InitInteger(20) + return val.Equal(x) + }) + + if index != -1 { + t.Errorf("Single linked list should return -1 if data is not found") + } + +} + +// #endregion diff --git a/queue/README.md b/queue/README.md index d8b3f79..bc19093 100644 --- a/queue/README.md +++ b/queue/README.md @@ -1,6 +1,6 @@ # Queue (using Linked list) -This package implements genric queue, we can have data in node of any type we want. +This package implements generic queue, we can have data in node of any type we want. Queue is a data structure that follows FIFO (First in First out). Examples of stacks are: diff --git a/stack/README.md b/stack/README.md index 17defe6..030f323 100644 --- a/stack/README.md +++ b/stack/README.md @@ -1,6 +1,6 @@ # Stack (using Linked list) -This package implements genric stack, we can have data in node of any type we want. +This package implements generic stack, we can have data in node of any type we want. Stack is a data structure that follows LIFO (Last in first out). Examples of stacks are: