배열은 순차적으로 연결된 공간을 예약을 해놓고(확보를 해놓고) 거기에 데이터를 하나씩 넣어서 나열하는 데이터 구조// 그러므로 새로운 정보를 추가할 수 없음. 이런 단점을 해결하기 위해 나온 것이 링크드 리스트다
링크드 리스트는 예약을 해놓지 않고 필요할 때마다 추가// 공간을 만들고 그 안에 데이터와 다음 데이터 주소를 적는다 / 다음 데이터는 어느 공간이든 간에 새로운 노드를 만들고 이 노드의 주소를 앞에 있는 노드의 포인터에 넣는다// 그러므로 맨앞 노드의 주소만 알면 전체 데이터를 확인할 수 있다(A를 알면 다음 데이터 주소가 있고 그 다음 B를 가고 다음 데이터 주소를 보고 C를 찾아가고 계속 이어지다가 마지막 데이터 주소가 null일 때 끝이라는 것을 보면 된다) // 즉 링크드 리스트는 무한정으로 추가할 수 있다
링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조
본래 C언어에서는 주요한 데이터 구조이지만, 파이썬은 리스트 타입이 링크드 리스트의 기능을 모두 지원
링크드 리스트 기본 구조와 용어
노드(Node): 데이터 저장 단위 (데이터값, 포인터) 로 구성
포인터(pointer): 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간
* 일반적인 링크드 리스트 형태
: 배열과 달리 특정 데이터를 저장할 때 저장할 공간과 그 뒤에 나올 데이터가 저장되어 있는 공간을 가리키는 주소값을
search=False // data가 1인 애를 찾으면 그 값을 가진 채 while문이 끝난다
else:
node=node.next // 1을 못 찾으면 계속 node는 node의 next가 된다
node_next=node.next // data가 1인 node가 가진 주소가 다음 node의 주소니까
node.next=node3 // data가 1인 node가 가리키는 주소를 추가하는 node3로 하고
node3.next=node_next // node3의 포인트 즉 가리키는 다음 주소 값을 node_next
(1인 data가 원래 가리켰던 data가 2였던 노드의 주소)
node=head
whilenode.next:
print(node.data)
node=node.next
print (node.data)
1 1.5 2 3 4 5 6 7 8 9
5. 파이썬 객체지향 프로그래밍으로 링크드 리스트 구현하기
: 각각의 노드를 저장, 생성할 수 있는 클래스 만들기
In [15]:
classNode:
def__init__(self, data, next=None):
self.data=data
self.next=next
classNodeMgmt: //링크드 리스트를 관리할 수 있는 클래스. 맨 앞에 노드의 값을 가질 수 있는 걸 만들어놓는다
def__init__(self, data):
self.head=Node(data)
defadd(self, data): // 해당 링크드 리스트의 맨 끝에 노드를 추가하는 함수
ifself.head=='': // 방어 코드 : 만약 head값이 없으면 추가할 데이터를 가지고 맨 앞 노드를 만든다
self.head=Node(data)
else:
node=self.head
whilenode.next: //none이 아니면
node=node.next // node는 다음 주소를 가지도록
node.next=Node(data)
// node.next = none으로 while문이 끝났을 때 마지막 node.next는 새롭게 넣은 Node(data)가 되도록
defdesc(self): // 해당 링크드 리스트의 전체를 출력할 수 있는 함수
node=self.head
whilenode: // node가 있으면
print (node.data) // 해당 node의 data를 가져오고
node=node.next // 다음으로 넘어가 또 node를 불러온다
linkedlist1=NodeMgmt(0) // 0이라는 data를 가진 node가 객체로 만들어진다
linkedlist1.desc()
0
fordatainrange(1, 10):
linkedlist1.add(data)
linkedlist1.desc()
0 1 2 3 4 5 6 7 8 9
6. 링크드 리스트의 복잡한 기능2 (특정 노드를 삭제)
다음 코드는 위의 코드에서 delete 메서드만 추가한 것이므로 해당 메서드만 확인하면 됨
1. head 삭제
2. 마지막 노드 삭제
3. 중간 노드 삭제
// 각각의 경우의 수가 있고 이를 구현하는 것이 매우 복잡
classNode:
def__init__(self, data, next=None):
self.data=data
self.next=next
classNodeMgmt:
def__init__(self, data):
self.head=Node(data)
defadd(self, data):
ifself.head=='':
self.head=Node(data)
else:
node=self.head
whilenode.next:
node=node.next
node.next=Node(data)
defdesc(self):
node=self.head
whilenode:
print (node.data)
node=node.next
defdelete(self, data):
ifself.head=='': //head가 없으면
print ("해당 값을 가진 노드가 없습니다.")
return // 끝낸다. 여기까지 방어 코드
ifself.head.data==data: // head에 있는 data가 data인 경우 (head 삭제하는 경우)
temp=self.head
self.head=self.head.next // 첫 node를 삭제할 거니까 head를 다음 주소로 바꾸고
deltemp // 첫 node 삭제
else:
node=self.head // 삭제할 데이터가 head가 아닌 경우
whilenode.next: // 2번째가 삭제할 데이터인지 확인하는 것
ifnode.next.data==data:
temp=node.next
node.next=node.next.next // 2번째를 삭제하니까 첫번째가 가리키는 주소인 node.next가 세번째 주소를 가리키도록
deltemp
return //
else:
node=node.next
테스트를 위해 1개 노드를 만들어 봄
In [19]:
linkedlist1=NodeMgmt(0)
linkedlist1.desc()
0
head 가 살아있음을 확인
linkedlist1.head
<__main__.Node at 0x1099fc6a0> // 0 node가 생성된 것을 확인할 수 있다
head 를 지워봄(위에서 언급한 경우의 수1)
linkedlist1.delete(0)
다음 코드 실행시 아무것도 안나온다는 것은 linkedlist1.head 가 정상적으로 삭제되었음을 의미
다시 하나의 노드를 만들어봄
linkedlist1=NodeMgmt(0)
linkedlist1.desc()
0
이번엔 여러 노드를 더 추가해봄
fordatainrange(1, 10):
linkedlist1.add(data)
linkedlist1.desc()
0 1 2 3 4 5 6 7 8 9 // 0은 위에서 만든
노드 중에 한개를 삭제함 (위에서 언급한 경우의 수2)
linkedlist1.delete(4)
특정 노드가 삭제되었음을 알 수 있음
linkedlist1.desc()
0 1 2 3 5 6 7 8 9
linkedlist1.delete(9)
linkedlist1.desc()
0 1 2 3 5 6 7 8
# 링크드 리스트 강의 4번
연습1: 위 코드에서 노드 데이터가 2인 노드 삭제해보기
node_mgmt.delete(2)
node_mgmt.desc()
연습2: 위 코드에서 노드 데이터가 특정 숫자인 노드를 찾는 함수를 만들고, 테스트해보기 테스트: 임의로 1 ~ 9까지 데이터를 링크드 리스트에 넣어보고,
데이터 값이 4인 노드의 데이터 값 출력해보기
classNode:
def__init__(self, data):
self.data=data
self.next=None
classNodeMgmt:
def__init__(self, data):
self.head=Node(data)
defadd(self, data):
ifself.head=='':
self.head=Node(data)
else:
node=self.headwhilenode.next:node=node.nextnode.next=Node(data)defdesc(self):node=self.headwhilenode:print (node.data)node=node.nextdefdelete(self, data):ifself.head=='':print ('해당 값을 가진 노드가 없습니다.')returnifself.head.data==data: # 경우의 수1: self.head를 삭제해야할 경우 - self.head를 바꿔줘야 함temp=self.head# self.head 객체를 삭제하기 위해, 임시로 temp에 담아서 객체를 삭제했음self.head=self.head.next# 만약 self.head 객체를 삭제하면, 이 코드가 실행이 안되기 때문!deltempelse:node=self.headwhilenode.next: # 경우의 수2: self.head가 아닌 노드를 삭제해야할 경우ifnode.next.data==data:temp=node.nextnode.next=node.next.nextdeltemppasselse:node=node.nextdefsearch_node(self, data):node=self.headwhilenode:ifnode.data==data:returnnodeelse:node=node.next
def__init__(self, data, prev=None, next=None): // prev, next를 둘 다 넣어준다
self.prev=prev
self.data=data
self.next=next
classNodeMgmt:
def__init__(self, data): // default 데이터
self.head=Node(data) // head
self.tail=self.head // 뒤에서부터 검색할 수도 있으니까 tail도 있다
definsert(self, data): // 맨 뒤에다 하나 추가하는
ifself.head==None: // head가 없으면 head를 만든다
self.head=Node(data)
self.tail=self.head // tail도 만든다 (만약 tail이 있으면 head가 있다는 말이고 그 말은 node가 있다는 말)
else:
node=self.head // 그러므로 head를 node라는 변수에 넣어놓고
whilenode.next:
// 만약 node.next가 마지막 주소라면 None그러므로 이 반복문이 계속 된다는 건 마지막 주소가 아니라는 것 (node의 끝을 찾아가려고 하는 부분)
node=node.next // 마지막 node를 가리키는 주소
new=Node(data) // new라는 이름의 새로운 node를 만들고 [node - node.next - new]
node.next=new // 다음 node의 주소를 new로 정하고 (node기준, 다음을 new)
new.prev=node // 새로운 것의 이전 주소를 node (new기준, 이전을 prev)
self.tail=new // 새로운 node가 생성되었으니까 맨끝을 수정해준다
desc는 나열이기 때문에 위와 다르지 않음
defdesc(self):
node=self.head
whilenode: //none이 아닌 동안
print (node.data)
node=node.next // 다음 주소를 넣는다
test해보기
double_linked_list=NodeMgmt(0) // 초기에 넣을 값
for datainrange(1, 10):
double_linked_list.insert(data)
double_linked_list.desc()
0 1 2 3 4 5 6 7 8 9
연습3: 위 코드에서
노드 데이터가 특정 숫자인 노드 앞에 데이터를 추가하는 함수를 만들고, 테스트해보기 - 더블 링크드 리스트의 tail 에서부터 뒤로 이동하며, 특정 숫자인 노드를 찾는 방식으로 함수를 구현하기 - 테스트: 임의로 0 ~ 9까지 데이터를 링크드 리스트에 넣어보고,
데이터 값이 2인 노드 앞에 1.5 데이터 값을 가진 노드를 추가해보기
classNode:
def__init__(self, data, prev=None, next=None):
self.prev=prev
self.data=data
self.next=next
classNodeMgmt:
def__init__(self, data):
self.head=Node(data)
self.tail=self.head
definsert(self, data):
ifself.head==None:
self.head=Node(data)
self.tail=self.head
else:node=self.head
whilenode.next:
node=node.next
new=Node(data)
node.next=new
new.prev=node
self.tail=new
defdesc(self):
node=self.head
whilenode:
print (node.data)
node=node.next
defsearch_from_head(self, data): // 앞부터 검색
ifself.head==None: // 방어 코드 (아예 head부터 없으면 false되도록)
returnFalse
node=self.head // node에 head를 넣고
whilenode:
ifnode.data==data: // 우리가 넣은 인자 data와 같으면
returnnode //해당 node를 반환한다
else:
node=node.next // 다음 node를 또 살핀다
// while문이 끝났다는 건 계속 일치하는 data를 찾았는데 return값이 없다는 것이므로 return false
returnFalse
defsearch_from_tail(self, data):
ifself.head==None:
returnFalse
node=self.tail // 뒤에서부터 찾기 시작
whilenode: // None이 아닐 때
ifnode.data==data: // 찾고 있는 값과 같으면 반환
returnnode
else: // 다르면 그 앞 값으로 계속
node=node.prev
returnFalse // while문이 끝나면 false반환
definsert_before(self, data, before_data): //self, data, before_data - before_data값을 가진 노드 앞에 data를 가진 노드를 만들겠다 //뒤에서부터 오는 것이기 때문에 before_data가 사실 더 뒤에 있는 거 (뒤에서 봤을 때 before)
// 그러므로 before앞에 data를 넣어야 하니까 찾는 값이 before와 같으면 그 앞에 insert하면 된다
ifself.head==None:
self.head=Node(data) // 만약 head가 없으면 node가 아예 없는거니까 data를 가진 node를 만들어주면 됨
returnTrue
else: // head가 있으면
node=self.tail // 뒤에서부터 검색(연습4의 문제 조건이니까)
whilenode.data !=before_data: // tail부터 시작해서 node에 있는 data가 before_data가 아니라면
node=node.prev // 앞으로 하나씩 움직이면서 확인한다
ifnode==None:
returnFalse // 찾은 뒤 그 앞에 insert해야하는데 아예 없으니까 return false
new=Node(data) // while문을 벗어났다는 것은 같은 것을 찾았다는 것 그러므로 new를 만들어서
before_new=node.prev // 기준으로 하고 있는 node 앞을 before_new로 잡는다
before_new.next=new // 원래 node앞에 있는 node를 가리키도록 : before_new.next(다음에 오도록 가리킬 주소 : 포인터) 는 새로 생성된 new를 가리키도록
new.prev=before_new // 새로 생성한 new를 before_data 즉 node의 앞에 넣어야. 근데 before_new는 before_data와 일치한 node.prev. 그러므로 new를 node앞에 넣으면 before_new의 뒤에 넣어야함. 그러므로 new의 앞은 before_new
new.next=node // before_data값 앞에 data(new로 생성)를 삽입하는 건데
before_data를 찾아서 일치한 것이 node니까 new는 node의 앞에 있어야 함.
그러므로 new의 기준으로 next가 node
node.prev=new// 위와 똑같은 말. but 더블이라 각각 prev, next가 있어야
returnTrue
테스트 : 2 앞에 1.5 넣기
double_linked_list=NodeMgmt(0)
for datainrange(1, 10):
double_linked_list.insert(data)
double_linked_list.desc()
0 1 2 3 4 5 6 7 8 9
In [15]:
node_3=double_linked_list.search_from_head(3)
node_3.data // 만약 node_3을 치면 객체가 반환된다
Out[15]:
3
In [18]:
double_linked_list.insert_before(1.5, 2)
double_linked_list.desc()
0 1 1.5 2 3 4 5 6 7 8 9
In [19]:
node_3=double_linked_list.search_from_tail(1.5)
node_3.data
Out[19]:
1.5
연습4: 위 코드에서 노드 데이터가 특정 숫자인 노드 뒤에 데이터를 추가하는 함수를 만들고, 테스트해보기 - 더블 링크드 리스트의 head 에서부터 다음으로 이동하며, 특정 숫자인 노드를 찾는 방식으로 함수를 구현하기 - 테스트: 임의로 0 ~ 9까지 데이터를 링크드 리스트에 넣어보고, 데이터 값이 1인 노드 다음에 1.7 데이터 값을 가진 노드를 추가해보기