본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

기본 자료구조 문서
기본 자료구조
핵심 개념동작 원리구현복잡도 분석연습 문제실무 활용
토론

기본 자료구조 안내서 · 1/6

핵심 개념

자료구조는 데이터를 메모리에 배치하는 방식과, 그 배치에서 싸게 할 수 있는 연산을 함께 묶은 것입니다. 자료구조를 고르는 일은 언제나 맞바꿈입니다. 위치로 찾는 일이 즉시 끝나는 배치는 가운데 삽입이 느릴 수 있고, 삽입이 싼 배치는 찾기가 느릴 수 있습니다. 이 안내서는 거의 모든 프로그램에서 만나는 다섯 가지 구조, 즉 배열과 동적 배열, 연결 리스트, 스택, 큐와 덱, 해시 테이블을 다룹니다.

추상 자료형과 구현

구조가 약속하는 것과 그 약속을 지키는 방법을 나눠 생각하면 이해가 쉬워집니다.

  • 추상 자료형(ADT) 은 연산과 그 의미를 정합니다. 스택은 push, pop, peek 를 제공하고, 마지막에 넣은 것이 먼저 나온다고 약속합니다.
  • 구현 은 구체적인 메모리 배치입니다. 같은 스택을 동적 배열 위에 만들 수도 있고 연결 리스트 위에 만들 수도 있습니다.

면접 질문이나 라이브러리 문서는 이 둘을 섞어 말하는 경우가 많습니다. "어떤 클래스를 쓸까?"보다 "어떤 연산이 얼마나 자주 필요한가?"를 먼저 묻는 습관을 들이면 좋습니다.

연속 메모리와 연결 메모리

여기서 다루는 구조는 모두 두 가지 생각 중 하나에서 출발합니다.

방식저장 방법장점단점
연속한 덩어리 메모리에 나란히 저장위치로 O(1) 접근, 캐시 친화적가운데에 넣으면 뒤쪽을 모두 밀어야 함
연결따로 떨어진 노드를 포인터로 연결쥐고 있는 노드 옆이라면 O(1) 삽입·삭제k번째 항목까지 O(n), 캐시 효율이 낮음

요즘 CPU는 메모리를 캐시 라인 단위로 읽기 때문에, 빅오가 같더라도 연속 배열을 훑는 쪽이 연결 리스트를 훑는 쪽보다 몇 배 빠른 경우가 흔합니다.

다섯 가지 구조

  • 배열 / 동적 배열: 연속된 메모리 블록입니다. 동적 배열(Python list, C++ std::vector, Java ArrayList, JavaScript 배열)은 여유 용량을 두었다가 가득 차면 더 큰 블록을 잡아 복사하므로, 끝에 추가하는 비용이 분할 상환 O(1)입니다.
  • 연결 리스트: 값과 다음 노드에 대한 참조를 가진 노드의 사슬입니다(단일 연결). 양쪽 이웃을 모두 가리키면 이중 연결 리스트입니다.
  • 스택: 나중에 들어간 것이 먼저 나옵니다(LIFO). 쌓아 둔 접시나 실행 중인 프로그램의 호출 스택을 떠올리면 됩니다.
  • 큐 / 덱: 큐는 먼저 들어간 것이 먼저 나옵니다(FIFO). 매표소 줄과 같습니다. 덱(양방향 큐)은 양쪽 끝에서 넣고 뺄 수 있습니다.
  • 해시 테이블: 키를 값에 대응시킵니다. 해시 함수가 키를 버킷 번호로 바꾸므로 찾기가 평균 O(1)입니다.

표준 라이브러리에 이미 있습니다

실무 코드에서 이 구조를 직접 짜는 일은 드물지만, 어떤 내장 타입이 어떤 생각에 해당하는지는 알아야 합니다.

개념PythonC++JavaTypeScript
동적 배열liststd::vectorArrayListArray
스택list (append / pop)std::vector 또는 std::stackArrayDequeArray (push / pop)
큐 / 덱collections.dequestd::deque, std::queueArrayDeque원형 버퍼를 직접 작성
해시 맵dictstd::unordered_mapHashMapMap
해시 집합setstd::unordered_setHashSetSet

리스트를 스택으로 쓸 때는 끝에서만 넣고 뺍니다.

stack = []
stack.append("a")      # push
stack.append("b")
print(stack[-1])       # peek -> b
print(stack.pop())     # pop  -> b
print(stack)           # ['a']

큐가 필요하면 list.pop(0) 대신 deque 를 씁니다. pop(0) 은 남은 원소를 전부 한 칸씩 당기기 때문입니다.

from collections import deque

queue = deque()
queue.append("job-1")      # 오른쪽 끝에 넣기(enqueue)
queue.append("job-2")
print(queue.popleft())     # 왼쪽 끝에서 꺼내기(dequeue) -> job-1
queue.appendleft("urgent") # 덱은 앞쪽에도 넣을 수 있습니다
print(list(queue))         # ['urgent', 'job-2']

딕셔너리와 집합은 해시 테이블입니다. 키는 해시할 수 있어야 하며, 실제로는 변경 불가능한 값이어야 한다는 뜻입니다.

stock = {"apple": 3, "pear": 0}
stock["kiwi"] = 7                 # 삽입
stock["apple"] += 1               # 갱신
print(stock.get("plum", 0))       # 없는 키는 기본값 -> 0
seen = {"apple", "kiwi"}
print("pear" in seen)             # 평균 O(1) 포함 검사 -> False
# stock[["a", "b"]] = 1 은 TypeError: 리스트는 해시할 수 없습니다

무엇을 고를까

다음 질문을 차례로 던져 봅니다.

  1. 위치(인덱스)로 접근해야 하는가? 동적 배열을 씁니다.
  2. 한쪽 끝에서만 넣고 빼는가? 스택을 씁니다(동적 배열이면 충분합니다).
  3. 한쪽에서 넣고 반대쪽에서 빼는가? 큐나 덱을 씁니다. 0번 위치에서 빼는 일반 배열은 피합니다.
  4. 키로 찾거나 포함 여부를 검사하는가? 해시 맵이나 해시 집합을 씁니다.
  5. 노드를 쥔 채 가운데에서 자주 끼우고 빼는가? 연결 리스트를 고려하되, 먼저 측정합니다. 배열이 이기는 경우가 많습니다.

용어

용어뜻
용량(capacity)동적 배열이 확보한 칸 수(비어 있는 칸 포함)
분할 상환 비용긴 연산 열에서 연산 하나당 평균 비용
해시 함수키를 정수로 바꾸는 함수
버킷해시 테이블 배열의 한 칸
충돌서로 다른 두 키가 같은 버킷에 들어가는 일
적재율(load factor)저장한 항목 수를 버킷 수로 나눈 값

정리

자료구조는 접근 방식 사이의 맞바꿈입니다. 연속 배열은 빠른 인덱싱과 좋은 캐시 효율을, 연결 노드는 국소적인 끼워 넣기를, 스택과 큐는 끝에서만 다루는 단순함을, 해시 테이블은 키로 찾는 평균 O(1)을 줍니다. 필요한 연산에서 출발해, 그 연산을 싸게 만들어 주는 내장 타입을 고르면 됩니다.

이 장에 대해 질문이 있나요?
다음 장동작 원리

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.