Skip to content

Commit 4037fd7

Browse files
Om1kamisiriak
andauthored
Add Linked Cycle List struct (#293)
Co-authored-by: Andrii Siriak <[email protected]>
1 parent a50e4a6 commit 4037fd7

File tree

4 files changed

+285
-0
lines changed

4 files changed

+285
-0
lines changed
256 KB
Loading
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Linked Cyclic List
2+
***
3+
## What is it?
4+
***
5+
Loop lists are single or doubly-linked lists that chase their own tail:
6+
A points to B, B points to C, C points to D, and D points to A.
7+
They are better suited for cyclic data such as train schedules.
8+
These lists are missing the first and last items.
9+
Therefore, it is necessary to introduce the concept of the current position.
10+
11+
This picture shows similar lists:
12+
![Alt text](./Linked_Cyclic_List.jpg?raw=true)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Package cyclicallylinkedlist demonstration of Linked List Cycle in golang
2+
3+
package cyclicallylinkedlist
4+
5+
import "fmt"
6+
7+
// Node of List.
8+
type ClNode struct {
9+
Val interface{}
10+
prev *ClNode
11+
next *ClNode
12+
}
13+
14+
// The Cycling list in this implementation is addressed
15+
// by means of the element located at the current position.
16+
type ClList struct {
17+
Size int
18+
CurrentItem *ClNode
19+
}
20+
21+
// Create new list.
22+
func NewList() *ClList {
23+
return &ClList{0, nil}
24+
}
25+
26+
// Create new node.
27+
func NewNode(val interface{}) *ClNode {
28+
return &ClNode{val, nil, nil}
29+
}
30+
31+
// Inserting the first node is a special case. It will
32+
// point to itself. For other cases, the node will be added
33+
// to the end of the list. End of the list is prev field of
34+
// current item. Complexity O(1).
35+
func (cl *ClList) Add(val interface{}) {
36+
n := NewNode(val)
37+
cl.Size++
38+
if cl.CurrentItem == nil {
39+
n.prev = n
40+
n.next = n
41+
cl.CurrentItem = n
42+
} else {
43+
n.prev = cl.CurrentItem.prev
44+
n.next = cl.CurrentItem
45+
cl.CurrentItem.prev.next = n
46+
cl.CurrentItem.prev = n
47+
}
48+
}
49+
50+
// Rotate list by P places.
51+
// This method is interesting for optimization.
52+
// For first optimization we must decrease
53+
// P value so that it ranges from 0 to N-1.
54+
// For this we need to use the operation of
55+
// division modulo. But be careful if P is less than 0.
56+
// if it is - make it positive. This can be done without
57+
// violating the meaning of the number by adding to it
58+
// a multiple of N. Now you can decrease P modulo N to
59+
// rotate the list by the minimum number of places.
60+
// We use the fact that moving forward in a circle by P
61+
// places is the same as moving N - P places back.
62+
// Therefore, if P > N / 2, you can turn the list by N-P places back.
63+
// Complexity O(n).
64+
func (cl *ClList) Rotate(places int) {
65+
if cl.Size > 0 {
66+
if places < 0 {
67+
multiple := cl.Size - 1 - places/cl.Size
68+
places += multiple * cl.Size
69+
}
70+
places %= cl.Size
71+
72+
if places > cl.Size/2 {
73+
places = cl.Size - places
74+
for i := 0; i < places; i++ {
75+
cl.CurrentItem = cl.CurrentItem.prev
76+
}
77+
} else if places == 0 {
78+
return
79+
} else {
80+
for i := 0; i < places; i++ {
81+
cl.CurrentItem = cl.CurrentItem.next
82+
}
83+
84+
}
85+
}
86+
}
87+
88+
// Delete the current item.
89+
func (cl *ClList) Delete() bool {
90+
var deleted bool
91+
var prevItem, thisItem, nextItem *ClNode
92+
93+
if cl.Size == 0 {
94+
return deleted
95+
}
96+
97+
deleted = true
98+
thisItem = cl.CurrentItem
99+
nextItem = thisItem.next
100+
prevItem = thisItem.prev
101+
102+
if cl.Size == 1 {
103+
cl.CurrentItem = nil
104+
} else {
105+
cl.CurrentItem = nextItem
106+
nextItem.prev = prevItem
107+
prevItem.next = nextItem
108+
}
109+
cl.Size--
110+
111+
return deleted
112+
}
113+
114+
// Destroy all items in the list.
115+
func (cl *ClList) Destroy() {
116+
for cl.Delete() {
117+
continue
118+
}
119+
}
120+
121+
// Show list body.
122+
func (cl *ClList) Walk() *ClNode {
123+
var start *ClNode
124+
start = cl.CurrentItem
125+
126+
for i := 0; i < cl.Size; i++ {
127+
fmt.Printf("%v \n", start.Val)
128+
start = start.next
129+
}
130+
return start
131+
}
132+
133+
// https://en.wikipedia.org/wiki/Josephus_problem
134+
// This is a struct-based solution for Josephus problem.
135+
func JosephusProblem(cl *ClList, k int) int {
136+
for cl.Size > 1 {
137+
cl.Rotate(k)
138+
cl.Delete()
139+
cl.Rotate(-1)
140+
}
141+
retval := cl.CurrentItem.Val.(int)
142+
cl.Destroy()
143+
return retval
144+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package cyclicallylinkedlist
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func fillList(list *ClList, n int) {
9+
for i := 1; i <= n; i++ {
10+
list.Add(i)
11+
}
12+
}
13+
14+
func TestAdd(t *testing.T) {
15+
list := NewList()
16+
fillList(list, 3)
17+
18+
want := []interface{}{1, 2, 3}
19+
var got []interface{}
20+
var start *ClNode
21+
start = list.CurrentItem
22+
23+
for i := 0; i < list.Size; i++ {
24+
got = append(got, start.Val)
25+
start = start.next
26+
}
27+
if !reflect.DeepEqual(got, want) {
28+
t.Errorf("got: %v, want: %v", got, want)
29+
}
30+
}
31+
32+
func TestWalk(t *testing.T) {
33+
list := NewList()
34+
fillList(list, 3)
35+
36+
want := 1
37+
got := list.Walk()
38+
39+
if got.Val != want {
40+
t.Errorf("got: %v, want: nil", got)
41+
}
42+
}
43+
44+
func TestRotate(t *testing.T) {
45+
type testCase struct {
46+
param int
47+
wantToReturn int
48+
}
49+
list := NewList()
50+
fillList(list, 3)
51+
52+
testCases := []testCase{
53+
{1, 2},
54+
{3, 2},
55+
{6, 2},
56+
{7, 3},
57+
{-2, 1},
58+
{5, 3},
59+
{8, 2},
60+
{-8, 3},
61+
}
62+
for idx, tCase := range testCases {
63+
list.Rotate(tCase.param)
64+
got := list.CurrentItem.Val
65+
if got != tCase.wantToReturn {
66+
t.Errorf("got: %v, want: %v for test id %v", got, tCase.wantToReturn, idx)
67+
}
68+
}
69+
}
70+
71+
func TestDelete(t *testing.T) {
72+
list := NewList()
73+
fillList(list, 3)
74+
75+
want := 2
76+
wantSize := 2
77+
list.Delete()
78+
got := list.CurrentItem.Val
79+
if want != got {
80+
t.Errorf("got: %v, want: %v", got, want)
81+
}
82+
if wantSize != list.Size {
83+
t.Errorf("got: %v, want: %v", got, want)
84+
}
85+
}
86+
87+
func TestDestroy(t *testing.T) {
88+
list := NewList()
89+
fillList(list, 3)
90+
wantSize := 0
91+
list.Destroy()
92+
93+
got := list.CurrentItem
94+
95+
if got != nil {
96+
t.Errorf("got: %v, want: nil", got)
97+
}
98+
99+
if wantSize != list.Size {
100+
t.Errorf("got: %v, want: %v", got, wantSize)
101+
}
102+
}
103+
104+
func TestJosephusProblem(t *testing.T) {
105+
type testCase struct {
106+
param int
107+
wantToReturn int
108+
listCount int
109+
}
110+
111+
testCases := []testCase{
112+
{5, 4, 8},
113+
{3, 8, 8},
114+
{8, 5, 8},
115+
{8, 5, 8},
116+
{2, 14, 14},
117+
{13, 56, 58},
118+
{7, 5, 5},
119+
}
120+
121+
for _, tCase := range testCases {
122+
list := NewList()
123+
fillList(list, tCase.listCount)
124+
got := JosephusProblem(list, tCase.param)
125+
if got != tCase.wantToReturn {
126+
t.Errorf("got: %v, want: %v", got, tCase.wantToReturn)
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)