22
33import { useState , useRef } from 'react' ;
44import Navbar from '@/components/navbar' ;
5- import { buildList , linkify , makeNode , randomValues } from '@/lib/algorithms/linkedList' ;
5+ import {
6+ buildList ,
7+ linkify ,
8+ randomValues ,
9+ reduceStructure ,
10+ insertActions ,
11+ deleteByValueActions ,
12+ deleteByIndexActions ,
13+ searchActions ,
14+ reverseActions ,
15+ } from '@/lib/algorithms/linkedList' ;
616import Canvas from './canvas' ;
717import Menu from './menu' ;
818
@@ -20,6 +30,7 @@ export default function LinkedList() {
2030 const [ listType , setListType ] = useState ( 0 ) ;
2131 const [ nodeState , setNodeState ] = useState ( { } ) ;
2232 const [ pointers , setPointers ] = useState ( [ ] ) ;
33+ const [ liftedId , setLiftedId ] = useState ( null ) ;
2334 const [ isRunning , setIsRunning ] = useState ( false ) ;
2435
2536 const nodesRef = useRef ( initial . nodes ) ;
@@ -33,160 +44,91 @@ export default function LinkedList() {
3344 const indexRef = useRef ( '1' ) ;
3445
3546 // --- state appliers (keep refs and React state in sync) ---
36- const applyNodes = ( arr ) => { nodesRef . current = arr ; setNodes ( arr ) ; } ;
37- const applyLinks = ( nx , pv ) => {
47+ const applyStructure = ( { nodes : n , nextOf : nx , prevOf : pv } ) => {
48+ nodesRef . current = n ; setNodes ( n ) ;
3849 nextOfRef . current = nx ; setNextOf ( nx ) ;
3950 prevOfRef . current = pv ; setPrevOf ( pv ) ;
4051 } ;
41- const relink = ( arr ) => {
42- const { nextOf : nx , prevOf : pv } = linkify ( arr ) ;
43- applyNodes ( arr ) ;
44- applyLinks ( nx , pv ) ;
52+ const relink = ( arr ) => applyStructure ( { nodes : arr , ...linkify ( arr ) } ) ;
53+
54+ // Apply one action: visual actions touch marks/pointers; everything else is
55+ // a structural change handled by the lib reducer.
56+ const applyAction = ( action ) => {
57+ switch ( action . type ) {
58+ case 'mark' :
59+ setNodeState ( ( s ) => ( { ...s , [ action . id ] : action . state } ) ) ;
60+ break ;
61+ case 'pointers' :
62+ setPointers ( action . items ) ;
63+ break ;
64+ case 'lift' :
65+ setLiftedId ( action . id ) ;
66+ break ;
67+ case 'drop' :
68+ setLiftedId ( null ) ;
69+ break ;
70+ case 'clear' :
71+ setNodeState ( { } ) ;
72+ setPointers ( [ ] ) ;
73+ setLiftedId ( null ) ;
74+ break ;
75+ default :
76+ if ( action . type === 'stageNode' ) setLiftedId ( action . id ) ;
77+ applyStructure ( reduceStructure (
78+ { nodes : nodesRef . current , nextOf : nextOfRef . current , prevOf : prevOfRef . current } ,
79+ action ,
80+ ) ) ;
81+ }
4582 } ;
46- const mark = ( id , kind ) => setNodeState ( ( s ) => ( { ...s , [ id ] : kind } ) ) ;
47- const clearMarks = ( ) => { setNodeState ( { } ) ; setPointers ( [ ] ) ; } ;
4883
49- const begin = ( ) => {
50- if ( isRunningRef . current ) return false ;
84+ const runActions = async ( actions ) => {
85+ if ( ! actions . length || isRunningRef . current ) return ;
5186 isRunningRef . current = true ;
5287 setIsRunning ( true ) ;
53- clearMarks ( ) ;
54- return true ;
55- } ;
56- const finish = ( ) => { isRunningRef . current = false ; setIsRunning ( false ) ; } ;
57-
58- // --- operations ---
59- const runInsert = async ( pos ) => {
60- if ( ! begin ( ) ) return ;
61- const arr = [ ...nodesRef . current ] ;
62- const idx = pos === 'head' ? 0
63- : pos === 'tail' ? arr . length
64- : Math . max ( 0 , Math . min ( Number ( pos ) || 0 , arr . length ) ) ;
65- const value = Number ( valueRef . current ) ;
66-
67- for ( let i = 0 ; i < idx ; i ++ ) {
68- mark ( arr [ i ] . id , 'active' ) ;
69- await sleep ( speedRef . current ) ;
70- mark ( arr [ i ] . id , 'done' ) ;
71- }
72- const node = makeNode ( Number . isFinite ( value ) ? value : 0 ) ;
73- arr . splice ( idx , 0 , node ) ;
74- relink ( arr ) ;
75- mark ( node . id , 'found' ) ;
76- await sleep ( speedRef . current ) ;
77- clearMarks ( ) ;
78- finish ( ) ;
79- } ;
80-
81- const runDeleteValue = async ( value ) => {
82- if ( ! begin ( ) ) return ;
83- const arr = [ ...nodesRef . current ] ;
84- let idx = - 1 ;
85- for ( let i = 0 ; i < arr . length ; i ++ ) {
86- mark ( arr [ i ] . id , 'active' ) ;
87- await sleep ( speedRef . current ) ;
88- if ( arr [ i ] . value === value ) { idx = i ; break ; }
89- mark ( arr [ i ] . id , 'done' ) ;
90- }
91- if ( idx >= 0 ) {
92- mark ( arr [ idx ] . id , 'remove' ) ;
93- await sleep ( speedRef . current ) ;
94- arr . splice ( idx , 1 ) ;
95- relink ( arr ) ;
96- await sleep ( speedRef . current ) ;
97- }
98- clearMarks ( ) ;
99- finish ( ) ;
100- } ;
101-
102- const runDeleteIndex = async ( index ) => {
103- if ( ! begin ( ) ) return ;
104- const arr = [ ...nodesRef . current ] ;
105- const idx = Number ( index ) ;
106- if ( idx >= 0 && idx < arr . length ) {
107- for ( let i = 0 ; i < idx ; i ++ ) {
108- mark ( arr [ i ] . id , 'active' ) ;
109- await sleep ( speedRef . current ) ;
110- mark ( arr [ i ] . id , 'done' ) ;
111- }
112- mark ( arr [ idx ] . id , 'remove' ) ;
113- await sleep ( speedRef . current ) ;
114- arr . splice ( idx , 1 ) ;
115- relink ( arr ) ;
116- await sleep ( speedRef . current ) ;
117- }
118- clearMarks ( ) ;
119- finish ( ) ;
120- } ;
121-
122- const runSearch = async ( value ) => {
123- if ( ! begin ( ) ) return ;
124- const arr = [ ...nodesRef . current ] ;
125- let found = false ;
126- for ( let i = 0 ; i < arr . length ; i ++ ) {
127- mark ( arr [ i ] . id , 'active' ) ;
128- await sleep ( speedRef . current ) ;
129- if ( arr [ i ] . value === value ) { mark ( arr [ i ] . id , 'found' ) ; found = true ; break ; }
130- mark ( arr [ i ] . id , 'done' ) ;
131- }
132- await sleep ( speedRef . current ) ;
133- if ( found ) await sleep ( speedRef . current ) ;
134- clearMarks ( ) ;
135- finish ( ) ;
136- } ;
137-
138- const runReverse = async ( ) => {
139- if ( ! begin ( ) ) return ;
140- const arr = [ ...nodesRef . current ] ;
141- if ( arr . length < 2 ) { finish ( ) ; return ; }
142-
143- const temp = { ...linkify ( arr ) . nextOf } ;
144- let prev = null ;
145- for ( let i = 0 ; i < arr . length ; i ++ ) {
146- const curr = arr [ i ] . id ;
147- const next = i + 1 < arr . length ? arr [ i + 1 ] . id : null ;
148- setPointers ( [
149- ...( prev != null ? [ { label : 'prev' , nodeId : prev } ] : [ ] ) ,
150- { label : 'curr' , nodeId : curr } ,
151- ...( next != null ? [ { label : 'next' , nodeId : next } ] : [ ] ) ,
152- ] ) ;
153- mark ( curr , 'active' ) ;
154- await sleep ( speedRef . current ) ;
155- temp [ curr ] = prev ;
156- applyLinks ( { ...temp } , prevOfRef . current ) ;
88+ setNodeState ( { } ) ;
89+ setPointers ( [ ] ) ;
90+ setLiftedId ( null ) ;
91+ for ( const action of actions ) {
92+ applyAction ( action ) ;
15793 await sleep ( speedRef . current ) ;
158- mark ( curr , 'done' ) ;
159- prev = curr ;
16094 }
161- // settle: physical order mirrors logical, forward links restored
162- const reversed = [ ...arr ] . reverse ( ) ;
163- relink ( reversed ) ;
164- setPointers ( [ ] ) ;
165- await sleep ( speedRef . current ) ;
166- clearMarks ( ) ;
167- finish ( ) ;
95+ isRunningRef . current = false ;
96+ setIsRunning ( false ) ;
16897 } ;
16998
17099 const handleVisualize = ( ) => {
171100 const op = operationRef . current ;
172- if ( op === 0 ) runInsert ( 'head' ) ;
173- else if ( op === 1 ) runInsert ( 'tail' ) ;
174- else if ( op === 2 ) runInsert ( indexRef . current ) ;
175- else if ( op === 3 ) runDeleteValue ( Number ( valueRef . current ) ) ;
176- else if ( op === 4 ) runDeleteIndex ( indexRef . current ) ;
177- else if ( op === 5 ) runSearch ( Number ( valueRef . current ) ) ;
178- else if ( op === 6 ) runReverse ( ) ;
101+ const list = {
102+ nodes : nodesRef . current ,
103+ nextOf : nextOfRef . current ,
104+ prevOf : prevOfRef . current ,
105+ listType : listTypeRef . current ,
106+ } ;
107+ const value = Number ( valueRef . current ) ;
108+ let actions = [ ] ;
109+ if ( op === 0 ) actions = insertActions ( list , 'head' , value ) ;
110+ else if ( op === 1 ) actions = insertActions ( list , 'tail' , value ) ;
111+ else if ( op === 2 ) actions = insertActions ( list , indexRef . current , value ) ;
112+ else if ( op === 3 ) actions = deleteByValueActions ( list , value ) ;
113+ else if ( op === 4 ) actions = deleteByIndexActions ( list , indexRef . current ) ;
114+ else if ( op === 5 ) actions = searchActions ( list , value ) ;
115+ else if ( op === 6 ) actions = reverseActions ( list ) ;
116+ runActions ( actions ) ;
179117 } ;
180118
181119 const handleRandomize = ( ) => {
182120 if ( isRunningRef . current ) return ;
183- clearMarks ( ) ;
121+ setNodeState ( { } ) ;
122+ setPointers ( [ ] ) ;
123+ setLiftedId ( null ) ;
184124 relink ( buildList ( randomValues ( 5 ) ) . nodes ) ;
185125 } ;
186126
187127 const handleReset = ( ) => {
188128 if ( isRunningRef . current ) return ;
189- clearMarks ( ) ;
129+ setNodeState ( { } ) ;
130+ setPointers ( [ ] ) ;
131+ setLiftedId ( null ) ;
190132 relink ( nodesRef . current . map ( ( n ) => ( { ...n } ) ) ) ;
191133 } ;
192134
@@ -214,6 +156,7 @@ export default function LinkedList() {
214156 listType = { listType }
215157 nodeState = { nodeState }
216158 pointers = { pointers }
159+ liftedId = { liftedId }
217160 />
218161 </ div >
219162 </ div >
0 commit comments