Trie

트라이

A tree that shares common prefixes among words and follows one character per edge.

Also known as: Prefix tree
···
html
<canvas id="stage"></canvas><div class="controls"><button id="restart" type="button">처음부터</button><button id="step" type="button">한 단계</button><label>속도 <input id="speed" type="range" min="1" max="5" value="3"></label></div>
css
body{display:block}#stage{position:absolute;inset:0;width:100%;height:100%}.controls{position:absolute;bottom:9px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:7px;white-space:nowrap}.controls button,.controls label{font:12px sans-serif;color:var(--fg);background:var(--surface);border:1px solid var(--line);border-radius:6px;padding:5px 8px}.controls button{cursor:pointer}.controls input{width:75px;vertical-align:middle;accent-color:var(--accent)}@media(max-width:500px){.controls{display:none}}
js
const frames = [{"kind":"graph","caption":"빈 접두사 트리","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0"],"hideUnvisited":true}},{"kind":"graph","caption":"CAT: C 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C"],"active":"C","hideUnvisited":true,"selected":["0-C"]}},{"kind":"graph","caption":"CAT: CA 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A"],"active":"A","hideUnvisited":true,"selected":["0-C","A-C"]}},{"kind":"graph","caption":"CAT: CAT 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T"],"active":"T","hideUnvisited":true,"selected":["0-C","A-C","A-T"]}},{"kind":"graph","caption":"CAR: C 재사용","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T"],"active":"C","hideUnvisited":true,"selected":["0-C"]}},{"kind":"graph","caption":"CAR: CA 재사용","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T"],"active":"A","hideUnvisited":true,"selected":["0-C","A-C"]}},{"kind":"graph","caption":"CAR: CAR 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T","R"],"active":"R","hideUnvisited":true,"selected":["0-C","A-C","A-R"]}},{"kind":"graph","caption":"DOG: D 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T","R","D"],"active":"D","hideUnvisited":true,"selected":["0-D"]}},{"kind":"graph","caption":"DOG: DO 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T","R","D","O"],"active":"O","hideUnvisited":true,"selected":["0-D","D-O"]}},{"kind":"graph","caption":"DOG: DOG 추가","data":{"nodes":[{"id":"0","x":0.5,"y":0.05},{"id":"C","x":0.28,"y":0.35},{"id":"D","x":0.73,"y":0.35},{"id":"A","x":0.28,"y":0.64},{"id":"O","x":0.73,"y":0.64},{"id":"T","x":0.16,"y":0.94},{"id":"R","x":0.4,"y":0.94},{"id":"G","x":0.73,"y":0.94}],"edges":[["0","C"],["0","D"],["C","A"],["D","O"],["A","T"],["A","R"],["O","G"]],"visited":["0","C","A","T","R","D","O","G"],"active":"G","hideUnvisited":true,"selected":["0-D","D-O","G-O"]}}];
const title = "트라이 · 접두사 공유";
const canvas = document.getElementById('stage');
const ctx = canvas.getContext('2d');
const restart = document.getElementById('restart');
const step = document.getElementById('step');
const speed = document.getElementById('speed');
let index = 0, last = performance.now(), width = 300, height = 200;
function colors(){ const s=getComputedStyle(document.documentElement); const v=(key)=>s.getPropertyValue(key).trim(); return {bg:v('--bg'),fg:v('--fg'),muted:v('--muted'),line:v('--line'),surface:v('--surface'),accent:v('--accent'),accent2:v('--accent-2'),accent3:v('--accent-3')}; }
function resize(){ const dpr=Math.min(devicePixelRatio||1,2); width=innerWidth;height=innerHeight;canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);draw(); }
function text(value,x,y,color,size=12,align='left'){ ctx.fillStyle=color;ctx.font='600 '+size+'px sans-serif';ctx.textAlign=align;ctx.textBaseline='middle';ctx.fillText(String(value),x,y); }
function line(x1,y1,x2,y2,color,thick=2){ctx.strokeStyle=color;ctx.lineWidth=thick;ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();}
function dot(x,y,r,color){ctx.fillStyle=color;ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();}
function draw(){
  if(!frames.length)return;
  const p=colors(), frame=frames[index], d=frame.data, detail=width>500;
  ctx.fillStyle=p.bg;ctx.fillRect(0,0,width,height);
  text(title,12,18,p.fg,detail?16:13);text(frame.caption,12,detail?43:39,p.muted,detail?13:10);
  text((index+1)+' / '+frames.length,width-12,18,p.muted,10,'right');
  const top=detail?62:53, bottom=detail?height-47:height-9, span=Math.max(10,bottom-top), areaW=Math.min(width-22,detail?710:width-22), left=(width-areaW)/2;
  if(frame.kind==='graph'){
    const nodes=d.nodes, edges=d.edges, positions=Object.fromEntries(nodes.map(n=>[n.id,{x:left+areaW*(.06+.88*n.x),y:top+span*(.11+.78*n.y)}]));
    for(const edge of edges){const a=edge[0],b=edge[1],weight=edge[2],key=[a,b].sort().join('-'),pa=positions[a],pb=positions[b];
      if(d.hideUnvisited&&(!d.visited.includes(a)||!d.visited.includes(b)))continue;
      const chosen=d.selected?.includes(key), active=d.activeEdge===key;ctx.globalAlpha=active||chosen?1:.58;line(pa.x,pa.y,pb.x,pb.y,active?p.accent2:chosen?p.accent:p.muted,active?4:chosen?3:2);ctx.globalAlpha=1;
      if(d.directed){const angle=Math.atan2(pb.y-pa.y,pb.x-pa.x),x=pb.x-Math.cos(angle)*(detail?18:13),y=pb.y-Math.sin(angle)*(detail?18:13);line(x-Math.cos(angle-.55)*7,y-Math.sin(angle-.55)*7,x,y,p.muted,1.5);line(x-Math.cos(angle+.55)*7,y-Math.sin(angle+.55)*7,x,y,p.muted,1.5);}
      if(weight>0&&!d.hideUnvisited)text(weight,(pa.x+pb.x)/2,(pa.y+pb.y)/2-7,p.muted,10,'center');
    }
    for(const node of nodes){if(d.hideUnvisited&&!d.visited.includes(node.id))continue;const pt=positions[node.id],selected=d.visited?.includes(node.id),front=d.frontier?.includes(node.id),active=d.active===node.id;let color=active?p.accent2:front?p.accent3:selected?p.accent:p.surface;
      if(d.groups){const group=d.groups[nodes.findIndex(n=>n.id===node.id)];color=[p.accent,p.accent2,p.accent3][nodes.findIndex(n=>n.id===group)%3];}
      dot(pt.x,pt.y,detail?18:12,color);ctx.strokeStyle=p.line;ctx.lineWidth=1;ctx.stroke();text(node.id,pt.x,pt.y,selected||active||front||d.groups?'#fff':p.fg,detail?13:10,'center');
      if(d.distances)text(Number.isFinite(d.distances[node.id])?d.distances[node.id]:'∞',pt.x,pt.y+(detail?29:22),p.muted,10,'center');
    }
  } else if(frame.kind==='grid'||frame.kind==='table'||frame.kind==='bits'){
    const table=frame.kind==='table',bits=frame.kind==='bits';const rows=bits?1:table?d.values.length:d.rows,cols=bits?d.bits.length:table?d.values[0].length:d.cols;
    const labelX=table?24:0,labelY=table?18:0,cell=Math.min((areaW-labelX)/cols,(span-labelY)/rows,detail?56:32),ox=left+(areaW-(cols*cell+labelX))/2+labelX,oy=top+(span-(rows*cell+labelY))/2+labelY;
    if(table){d.colLabels.forEach((label,c)=>text(label,ox+c*cell+cell/2,oy-10,p.muted,10,'center'));d.rowLabels.forEach((label,r)=>text(label,ox-12,oy+r*cell+cell/2,p.muted,10,'center'));}
    for(let r=0;r<rows;r++)for(let c=0;c<cols;c++){const n=r*cols+c,x=ox+c*cell,y=oy+r*cell,active=bits?d.active?.includes(c):table?d.active?.[0]===r&&d.active?.[1]===c:d.active===n,source=table?d.sources?.some(v=>v[0]===r&&v[1]===c):false,visited=bits?d.bits[c]:table?false:d.visited?.includes(n),front=d.frontier?.includes(n),blocked=d.blocked?.includes(n),path=d.path?.includes(n),queen=d.queens?.[r]===c;
      ctx.fillStyle=blocked?p.line:active?p.accent2:path||queen?p.accent3:front?p.accent2:visited?p.accent:source?p.accent3:bits&&d.bits[c]?p.accent:p.surface;ctx.fillRect(x+2,y+2,cell-4,cell-4);ctx.strokeStyle=p.muted;ctx.globalAlpha=.42;ctx.strokeRect(x+1,y+1,cell-2,cell-2);ctx.globalAlpha=1;
      const value=bits?d.bits[c]:table?d.values[r][c]:queen?'Q':d.start===n?'S':d.goal===n?'G':blocked?'':visited?'·':'';
      if(value!==''&&value!==undefined)text(value,x+cell/2,y+cell/2,(active||path||queen||front||visited||bits&&d.bits[c])?'#fff':p.fg,Math.min(13,cell*.43),'center');
      if(bits)text(c,x+cell/2,y+cell+11,p.muted,9,'center');
    }
  } else if(frame.kind==='intervals'){
    const rowH=Math.min(31,span/d.intervals.length),unit=(areaW-28)/10,ox=left+10,oy=top+(span-rowH*d.intervals.length)/2;
    d.intervals.forEach((it,i)=>{const y=oy+i*rowH;ctx.fillStyle=d.active===i?p.accent2:d.selected.includes(i)?p.accent:p.line;ctx.fillRect(ox+it[0]*unit,y+3,Math.max(5,(it[1]-it[0])*unit-3),rowH-7);text(i+1,ox-5,y+rowH/2,p.muted,10,'right');});
  } else if(frame.kind==='ring'){
    const cx=width/2,cy=top+span/2,r=Math.min(areaW,span)*.35,loc=(v,rr)=>({x:cx+Math.sin(v*2*Math.PI)*rr,y:cy-Math.cos(v*2*Math.PI)*rr});ctx.strokeStyle=p.muted;ctx.globalAlpha=.6;ctx.lineWidth=2;ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.stroke();ctx.globalAlpha=1;
    d.keys.forEach((key,i)=>{const point=loc(key,r),server=d.servers.find(s=>s.id===d.owners[i]),dest=loc(server.pos,r);line(point.x,point.y,dest.x,dest.y,d.active===i?p.accent2:p.line,1);dot(point.x,point.y,d.active===i?6:4,d.active===i?p.accent2:p.fg);});
    d.servers.forEach((server,i)=>{const point=loc(server.pos,r);dot(point.x,point.y,detail?15:11,[p.accent,p.accent2,p.accent3,p.fg][i]);text(server.id,point.x,point.y,i===3?p.bg:'#fff',11,'center');});
  } else if(frame.kind==='points'){
    const side=Math.min(areaW,span)-8,ox=(width-side)/2,oy=top+(span-side)/2;ctx.strokeStyle=p.muted;ctx.globalAlpha=.5;ctx.strokeRect(ox,oy,side,side);ctx.globalAlpha=1;ctx.beginPath();ctx.arc(ox,oy+side,side,Math.PI*1.5,0);ctx.strokeStyle=p.muted;ctx.stroke();
    d.points.forEach(([x,y])=>dot(ox+x*side,oy+side-y*side,detail?2.5:1.7,x*x+y*y<=1?p.accent:p.accent2));
  }
}
function advance(){index=(index+1)%frames.length;draw();}
restart.addEventListener('click',()=>{index=0;last=performance.now();draw();});
step.addEventListener('click',()=>{advance();last=performance.now();});
addEventListener('resize',resize);resize();
function loop(now){const delay=900-Number(speed.value)*140;if(now-last>delay){advance();last=now;}requestAnimationFrame(loop);}requestAnimationFrame(loop);

Think of CAT and CAR sharing the same CA drawer. Follow one path for their common prefix and branch only where the letters differ. The demo inserts words in sequence and highlights shared branches.

Start at the root and follow characters one by one, creating a child when absent. Mark word endings so the presence of CAT does not imply CA is itself a word. Insertion and exact lookup for a string of length L take O(L); prefix lookup follows the same path to its last node.

Use tries for autocomplete, dictionaries, and prefix queries. Child storage can consume substantial memory, and listing every word under a prefix still costs time proportional to the output.

When to use

Use for frequent prefix searches. Design both end-of-word markers and child storage.

Open as page ↗