단일 연결리스트 (Singly Linked Lists)
주요 용어  설명
- Node : 리스트의 각 요소 (val,next)
 
- Head : 리스트의 시작 부분
 
- Tail : 노드의 마지막 요소
 
단일 연결리스트의 장단점 및 특징, 사용
사용
- 무작위 접근은 필요하지 않고 순서를 가진 리스트 형태의 데이터가 필요한 상황이라면 혹은 10번째, 50번째 요소에 접근할 필요가 없고 순서대로 요소들에 접근해도 되는 상황이라면 단일 연결리스트를 사용하는것이 좋다
 
특징
- 각 노드의 값에는 숫자, 배열, 스트링 모두 저장 가능하다
 
- 배열과 달리 인덱스가 없다
 
- 리스트는 삽입과 삭제에 특화 되어 있다. 특히 처음과 끝에 삽입 삭제가 쉽다.
 
장점
- 배열의 경우 원소의 삭제 또는 삽입 이벤트가 있을때 인덱스의 변화가 생겨 시간복잡도 O(n)을 가지지만 리스트의 경우 시간복잡도 O(1)을 가져서
대량의 데이터에서 순서가 상관 없는경우 더 유리하다. 
- 삽입과 삭제를 맨 앞 부분에서 자주 해야 하는 경우에는 배열에 비해 좋다.
 
단점
- 인덱스가 없기 때문에 배열 처럼 특정 인덱스의 원소에 접근이 불가능하고 항상 처음 Head에서부터 탐색을 해야한다.
 
시간복잡도
- 삽입(Insertion) - O(1)
 
- 삭제(Removal) - O(1)(처음일 경우) or O(N) (가장 안좋은 경우는 가장 마지막 삭제)
 
- 검색(Searching) - O(N)
 
- 접근(Get,Accesee) - O(N) 
 
노드 및 리스트 생성자 생성
- Node : 노드 자신의 값과 다음을 가르키는 next를 가진다.
 
- SinglyLinkedList : 리스트의 시작을 가르키는 head와 마지막을 가르키는 tail, 사이즈 length를 가진다.
 
class Node {
  constructor(val){
    this.val = val;
    this.next = null;
    }
}
class SinglyLinkedList{
  constructor(){
    this.head = null;
    this.tail = null;
    this.length = 0;
  }
}
리스트 Push method 구현
- 노드 생성
 
- head 값이 null 인지 체크
 
- head 값이 null 일 경우 head와 tail값 초기화
 
- head 값이 null이 아닌 경우 tail값을 바꿔 준다.
 
- 사이즈를 증가시켜준뒤 리턴한다.
 
push(val){
  let newNode = new Node(val)
  if(!this.head){
    this.head = newNode;
    this.tail = this.head;
  }else{
    this.tail.next = newNode;
    this.tail = newNode;
   }
   this.length++
   return this
}
리스트 Pop method 구현
- 리스트에 노드가 없다면 return undefined
 
- 리스트 전체를 tail까지 탐색한다.
 
- tail까지 가는 변수와 tail 전까지 가는 변수를 생성한다.
 
- 뒤에서 두번째 노드의 next를 null로 만들어준뒤 tail의 값을 뒤에서 두번째 노드로 변경한다.
 
- 길이 - 1
 
- 제거된 노드를 리턴한다.
 
pop(){
  if(!this.head) return undefined
  let fastNode = this.head
  let currentNode = fastNode
  while(fastNode.next){
    currentNode = fastNode
    fastNode = fastNode.next
    }
   this.tail = currentNode
   this.tail.next = null
   this.length--
   if(this.length === 0){
    this.head = null
    this.tail = null
   }
   return fastNode
}
리스트 Shift method 구현
- 리스트에 노드가 없다면 return undefined.
 
- 노드가 있으면 변수에 헤드를 저장.
 
- 헤드의 next를 바꿔준다.
 
- 길이 - 1
 
- 삭제된 헤드를 리턴해준다.
 
shift(){
  if(!this.head) return undefined
  let headNode = this.head
  this.head = headNode.next
  this.length--
  if(this.length===0){
    this.tail = null
    }
  return headNode
}
리스트 Unshift method 구현
- 노드 생성.
 
- head가 있는지 체크하고 없으면 head,tail모두 새로운 노드가 되도록 설정한다.
 
- head가 있다면 새로 생성된 노드의 next를 현재 head로 설정해주고 리스트의 head를 새로 만든 노드로 설정한다.
 
- 길이 + 1 
 
- 리스트 리턴
 
unshift(val){
  let newNode = new Node(val)
  if(!this.head){
    this.head = newNode
    this.tail = this.head
  }else{
    newNode.next = this.head
    this.head = newNode
  }
  this.length++
  return this
리스트 Get method 구현
- 숫자나 인덱스, 위치를 입력하는 메소드로 해당 위치의 노드를 출력해준다.
 
- head에서 시작하여 n번째 위치하는 노드를 찾아 출력해준다.
 
- 함수에 인덱스를 받는다.
 
- 인덱스의 값이 음수 or 길이보다 크거나 같은 경우 return null.
 
- 인덱스에 도달 할때까지 루프를 돈다.
 
get(idx){
  if(idx < 0 || this.length <= idx) return null
  let currentNode = this.head
  for(let i=0; i<idx; i++){
    currentNode = currentNode.next
  }
  return currentNode
}
리스트 Set method 구현
- set(position|index , val) 해당 위치 노드값을 바꾼다.
 
- get 함수를 이용하여 위치를 확인한다.
 
- 노드를 찾을 수 없다면 return false
 
- 찾았다면 값을 바꾸고 출력 해준다.
 
set(idx,val){
    let foundNode = this.get(idx)
    if(foundNode){
      foundNode.val = val
      return true
    }
    return false
  }
리스트 Insert method 구현
- insert(position|index , val) 해당 위치에 새로운 노드를 삽입한다.
 
- 인덱스의 값이 음수 or 길이보다 클 경우 return false.
 
- 인덱스의 값이 길이와 같다면 맨뒤에 삽입해준다.(Push method)
 
- 인덱스의 값이 0이면 Unshift method 사용
 
- 1,2,3 조건 모두 아닐경우  get method 를 사용하여 index-1을 찾는다(삽입 전 노드를 찾아야함)
 
- 변수를 생성하여 찾은 노드의 next 값을 저장한다.
 
- 새로운 노드를 생성한다.
 
- 찾은 노드의 next 값을 새로만든 노드로 바꿔준다.
 
- 새로만든 노드의 next 값을 원래 next였던 노드에 연결해준다.
 
- 길이 + 1
 
insert(idx,val){
  if(idx < 0 || this.length < idx) return false
  // ! = false !! = true
  if(idx === this.length) return !!this.push(val)
  if(idx === 0) return !!this.unshift(val)
  let preNode = this.get(idx-1)
  let tmp = preNode.next
  let newNode = new Node(val)
  preNode.next = newNode
  newNode.next = tmp
  this.length++
  return true
}
리스트 Remove method 구현
- remove(index) 해당 인덱스에 위치한 값을 제거한다.
 
- 인덱스가 0보다 작거나 길이보다 크거나 같다면 return undefined
 
- 인덱스가 길이-1 같은경우(마지막) Pop method 사용
 
- 인덱스가 0이라면 shift method 사용
 
- 1,2,3의 경우가 아니라면 get(index-1)을 하여 삭.
 
- 삭제할 노드의 전 노드를 찾는다.
 
- 찾은 노드의 next를 next.next로 설정한다
 
- 길이 - 1
 
- 삭제한 노드의 값을 출력한다.
 
remove(idx){
  if(idx < 0 || this.length <= idx) return undefined;
  if(idx === 0) return this.shift()
  if(idx === this.length-1) return this.pop()
  let preNode = this.get(idx-1)
  let delNode = preNode.next
  preNode = delNode.next
  this.length--
  return delNode
}
리스트 Reverse method 구현
- 리스트를 그 상태로 뒤집는것이다. 
 
- 사본을 만들 필요가 없다.(복사를 해서 새로운 리스트를 출력하는것이 아니다.) => 앞으로 순회를 하면서 하나씩 뒤집는다.
 
- nextNode, preNode, currentNode(head에서 시작) 변수를 생성한다.
 
- head와 tail을 바꾼다
 
- 루프를 돌며 바꿔준다.
 
reverse(){
  let currentNode = this.head
  let preNode = null
  let nextNode;
  this.head = this.tail
  this.tail = currentNode
  while(currentNode){
    nextNode = currentNode.next
    currentNode.next = preNode
    preNode = currentNode
    currentNode = nextNode
  }
  return this
}