Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add single linked list #10

Merged
merged 1 commit into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
- [Queue (using linked list)](queue/README.md)
- [Single linked list](list/singleLinkedList_README)
49 changes: 49 additions & 0 deletions list/list_contract.go
Original file line number Diff line number Diff line change
@@ -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
}
107 changes: 107 additions & 0 deletions list/singleLinkedList_README.md
Original file line number Diff line number Diff line change
@@ -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

148 changes: 148 additions & 0 deletions list/single_linked_list.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading