파이썬 리스트로 구현하는 연결리스트 탐색, 삽입, 삭제연산 + 원형 연결리스트

1. 기본 세팅

 

after[i] = i번 위치의 다음 위치 

 

before[i] = i번 위치의 이전 위치

 

A[i] = i번 위치의 실제 데이터

 

#노드 수
n = 8

#데이터
A = [3,1,7,2,5,4,9,10]

#다음 위치, 이전 위치
after = [-1]*8
before = [-1]*8

 

 

단일 연결 리스트면, after나 before 하나만 사용해서 한방향으로 연결하고

 

이중 연결 리스트면 after, before 모두 사용해서 양방향으로 연결

 

0 > 1 > 2 > 3 > 4 > 5 > 6 > 7로 연결되어있다고 가정하자

 

반대로 0 < 1 < 2 < 3 < 4 < 5 < 6 < 7로도 연결되어있다고 가정해

 

for i in range(n-1):
    
    after[i] = i+1

for i in range(1,n):
    
    before[i] = i-1

print(after)
print(before)

[1, 2, 3, 4, 5, 6, 7, -1]
[-1, 0, 1, 2, 3, 4, 5, 6]

 

 

-1은 연결된 곳이 없다는 뜻임

 

7번 노드는 after[7] = -1이므로 그 다음 노드가 없다는 뜻이고

 

0번 노드는 before[0] = -1이므로 그 이전 노드가 없다는 뜻

 

 

2. 탐색 연산

 

연결 리스트에서 어떤 원소 a가 몇번째 위치에 있는가?

 

head에서 시작해서 방향을 따라 O(N)으로 탐색하여 a를 찾아야한다.

 

head를 0번이라고 한다면... i != -1이 아니면 while문을 반복하여, i = after[i]로 after 방향을 따라 탐색해준다

 

[3,1,7,2,5,4,9,10]

i = 0
a = 4

while i != -1:
    
    if A[i] == a:
        
        break

    i = after[i]

print(i)
5

 

 

 

3. 삭제 연산

 

어떤 원소 a를 연결 리스트에서 삭제하려면?

 

그 원소 a의 위치를 O(N)에 찾고 그 노드가 i라면, i를 삭제하고, i의 이전 노드와 i의 다음 노드를 서로 연결시켜주면 된다.

 

다만 a의 위치를 이미 알고 있다면 O(1)에 가능하다.

 

5번 노드를 삭제하고 싶다면?

 

5번 노드의 이전 노드 before[5]와 5번 노드의 이후 노드 after[5]를 서로 연결시킨다.

 

 

 

 

after[before[5]] = after[5]

 

before[after[5]] = before[5]로 수정하면 된다.

 

이후에 after[5] = -1, before[5] = -1로 삭제한다면 그림적으로는 이쁘긴한데 반드시 필요한게 아니라면 그럴 필요는 없긴하다

 

 

i = 5

before[after[i]] = before[i]

after[before[i]] = after[i]


#before[i] = -1
#after[i] = -1
#A[i] = -1

print(before)
print(after)

 

 

다만 주의해야할 점은 시작 노드와 끝 노드를 삭제할 때이다.

 

시작 노드의 경우는 before이 없고, 끝 노드의 경우는 after이 없기 때문에

 

생각하지 않으면 에러날 수 있다

 

i = 5

a = after[i]
b = before[i]

if a != -1:
    
    before[a] = b

if b != -1:

    after[b] = a


#before[i] = -1
#after[i] = -1
#A[i] = -1

print(before)
print(after)

 

 

시작 노드나 끝 노드 삭제하면 딱히 연결할건 없지

 

만약에 시작 노드나 끝 노드에 데이터를 넣어두지 않고 시작+1~ 끝-1 사이에서만 데이터를 다룬다면?

 

그러면 이런 if문으로 예외를 둘 필요가 없겠지?

 

왜냐하면 데이터가 없는 노드를 삭제할 이유는 없으니까

 

그러면 삭제하려는 모든 노드는 before과 after값을 가지기 때문에 서로 연결하면 된다

 

 

 

 

4. 삽입 연산

 

현재 5번 노드를 삭제한 연결 리스트에서 0 > 1 > 2 > 3 > 4 >  6 > 7에서 

 

2번 노드와 3번 노드 사이에 데이터 6을 넣고 싶다면 어떻게 해야할까?

 

0 > 1 > 2 > (6) > 3 > 4 > 6 > 7

 

이렇게 넣으면 될거잖아?

 

3번 노드 4번 노드 6번 노드 7번 노드는 뒤로 한칸씩 밀어서 4번,5번,7번,8번 노드로 이름을 바꾸고

 

0 > 1 > 2 > 3 > 4 > 5 > 7 > 8 이렇게 하면 2번 노드와 3번 노드 사이에 6을 넣고 이름을 3번 노드로 한건가..?

 

 

 

 

이렇게 뒤로 밀 필요 없이, 그냥 비어있는 위치에 새 데이터를 넣기만 하면 된다.

 

0,1,2,3,4,5,6,7번 인덱스에 [3 1 7 2 5 4 9 10] 각각 있잖아

 

그러면 8번 인덱스에 6을 넣어서 [3 1 7 2 5 4 9 10 6]하고

 

0 > 1 > 2 > 8 > 3 > 4 > 6 > 7 이렇게 넣으면 끝

 

 

 

 

after[2] = 8, before[8] = 2

 

after[8] = 3, before[3] = 8 이렇게 연결해주면 끝

 

m = 6

A.append(m)

unused = 8 #사용하지 않는 방 번호

a = 2
b = 3

after[2] = unused
before[unused] = 2
after[unused] = 3
before[3] = unused

unused += 1 #다음 빈방

print(after)
print(before)

 

 

 

 

 

배열을 이용해서 구현할때 주의해야할 점은, after, before을 원래 가지고 있는 데이터보다 충분히 크게 잡아야한다는 점

 

안그러면 위처럼 인덱스 에러날 가능성이 높다

 

배열은 한번 잡으면 크기를 못바꾸니까

 

 

5. 갱신 연산

 

i번째 위치의 데이터 a를 다른 데이터 b로 바꾸고 싶다면?

 

그냥 A[i] = b로 하면 되니까 O(1)에 가능

 

이런건 after, before뿐만 아니라 각 위치 i번 위치의 데이터 값들을 A로 관리하고 있어야한다는거지

 

 

6. 원형 연결리스트

 

원형 연결리스트는, 시작과 끝이 연결되어있는 형태이다.

 

선형 연결리스트는 0 > 1 > 2 > 3 > 4 > 5 ... > n으로 끝이 있지만,

 

원형 연결리스트는 n번을 시작인 0번과 연결해주고, 0번은 끝인 n번과 연결해주면 된다.

 

그냥 시작과 끝을 연결해주기만 하면 끝

 

따라서 원형 연결리스트는 끝없이 이동이 가능하다.

 

left = [-1]*(n+1)
right = [-1]*(n+1)
A = list(range(n+1))

#head인 경우 tail과 연결하고, tail인 경우는 head와 연결함
for i in range(1,n+1):

    if i-1 == 0:

        left[i] = n
    
    else:
        
        left[i] = i-1
    
    if i+1 == n+1:
        
        right[i] = 1
    
    else:
        
        right[i] = i+1

 

 

 

https://www.acmicpc.net/problem/1158

 

 

 

deque로도 해결할 수 있지만, 이중 원형 연결리스트를 구현해서도 해결할 수 있다.

 

1번부터 n번까지 이중 원형 연결리스트를 구현하고, 1번부터 k번째 탐색해서 그 위치를 삭제하고,

 

삭제한 위치부터 다음 k번째를 탐색해서 삭제하고,

 

삭제한 위치부터 다음 k번째를 탐색해서 삭제하고,...

 

n,k = map(int,input().split())

left = [-1]*(n+1)
right = [-1]*(n+1)
A = list(range(n+1))

for i in range(1,n+1):

    if i-1 == 0:

        left[i] = n
    
    else:
        
        left[i] = i-1
    
    if i+1 == n+1:
        
        right[i] = 1
    
    else:
        
        right[i] = i+1

answer = []

x = 1

for _ in range(n):

    for i in range(k-1):

        x = right[x]

    answer.append(A[x])

    a = left[x]    
    b = right[x]

    right[a] = b
    left[b] = a

    x = b

print('<',end='')

for i in range(len(answer)-1):
    
    print(answer[i],end=', ')

print(answer[-1],end='>')

 

 

 

728x90