알고리즘/이론정리

연결리스트 (2)

팽팽 2021. 9. 9. 19:21

연결리스트는 삽입과 삭제가 유연하다는 것이 가장 큰 장점이다.

(1)에서는 특정번째를 지정하여 원소를 삽입/삭제하는 연산을 구현해보았다면,

(2)에서는 특정원소의 바로 다음을 지정하여 원소를 삽입/삭제하는 연산을 정의하고 구현해 볼 것이다.

 

그러자면, 맨 앞에 원소를 추가 (삽입) 하거나 맨 앞의 원소를 제거 (삭제) 하는 연산을 지정하기가 애매해짐

이런 경우에도 동일한 방법을 적용하기 위해서, 이번에는 연결 리스트의 맨 앞에다가 데이터 원소를 담고 있지 않은, 그냥 자리만 차지하는 노드 -dummy node 리스트를 정의하자

0번째 노드에 dummy node를 정의하자..!

 

class Node:

	def __init__(self, item):
		self.data = item
		self.next = None


class LinkedList:

	def __init__(self):
		self.nodeCount = 0
		self.head = Node(None)
		self.tail = None
		self.head.next = self.tail


	def __repr__(self):
		if self.nodeCount == 0:
			return 'LinkedList: empty'

		s = ''
		curr = self.head
		while curr.next:
			curr = curr.next
			s += repr(curr.data)
			if curr.next is not None:
				s += ' -> '
		return s


	def getLength(self):
		return self.nodeCount


	def traverse(self):
		result = []
		curr = self.head
		while curr.next:
			curr = curr.next
			result.append(curr.data)
		return result


	def getAt(self, pos):
		if pos < 0 or pos > self.nodeCount:
			return None

		i = 0
		curr = self.head
		while i < pos:
			curr = curr.next
			i += 1

		return curr


	def insertAfter(self, prev, newNode):
		newNode.next = prev.next
		if prev.next is None:
			self.tail = newNode
		prev.next = newNode
		self.nodeCount += 1
		return True


	def insertAt(self, pos, newNode):
		if pos < 1 or pos > self.nodeCount + 1:
			return False

		if pos != 1 and pos == self.nodeCount + 1:
			prev = self.tail
		else:
			prev = self.getAt(pos - 1)
		return self.insertAfter(prev, newNode)


	def concat(self, L):
		self.tail.next = L.head.next
		if L.tail:
			self.tail = L.tail
		self.nodeCount += L.nodeCount

연산정의


1.길이 얻어내기
2.리스트순회
3.특정 원소 참조
4.원소삽입

insertAfter(prev, newNode)
: 노드를 주고 그 뒤에다가 삽입하기

	def insertAfter(self, prev, newNode):
		newNode.next = prev.next
		if prev.next is None:
			self.tail = newNode #맨끝에서 newNode삽입하는 경우에 tail을 옮겨주자
		prev.next = newNode
		self.nodeCount += 1
		return True

 

prev가 가리키는 node의 다음에 newNode를 삽입하고
성공/실패에 따라 True/False를 리턴

 

insertAfter(prev, newNode) -> 맨앞에 node는 어떻게 삭제할 것인가..
popAfter(prev) -> 맨 앞에서는 어떻게?

---> dummy node를 이용하자 0번째 node!

 


이미 구현한 insertAfter()를 호출하여 이용하는 것으로
(1) pos범위 조건확인
(2) pos == 1인경우에는 head뒤에 새 node 삽입
(3) pos == nodeCount + 1인 경우는 prev <- tail : 앞에서 찾아갈 필요없이 tail을 prev로 삼으면 됨
(4) 그렇지 않은 경우에는 prev <- getAt() : 앞에서 찾아감

def insertAt(self, pos, newNode):
		if pos < 1 or pos > self.nodeCount + 1:
			return False

		if pos != 1 and pos == self.nodeCount + 1:
			prev = self.tail
		else:
			prev = self.getAt(pos - 1)
		return self.insertAfter(prev, newNode)


5.원소삭제

 

prev의 다음 node를 삭제하고 그 node의 data를 리턴


(1) prev가 마지막 node 일 때 prev.next == None
-> 삭제할 node없음
-> return None
(2) 리스트 맨끝의 node를 삭제할 때 curr.next = None
-> tail조정 필요
**연습문제 popAfter, popAt 구현하기

def popAfter(self, prev):

        #삭제할 다음 노드가 없을 경우
        if prev.next == None: 
            return None
        #일반적인 경우 삭제
        curr = prev.next
        prev.next = curr.next
        
        #맨 끝의 노드 삭제
        if curr.next == None:
            self.tail = prev
            prev.next = None
		self.nodeCount -= 1
        return curr.data


    def popAt(self, pos):
        if pos < 1 or pos > self.nodeCount:
            raise IndexError
        prev = self.getAt(pos-1)
        return self.popAfter(prev)



6.두 리스트 합치기
self.tail.next = L2.head.next
dummy node 제외하고 이어줌