데이터 구조 시리즈: 체인 테이블
소개하다.
우리는 포크로 스파게티를 먹고 숟가락으로 국을 먹고 젓가락으로 만두를 먹는다.모든 은기는 장단점이 있기 때문에 음식과 상호작용이 좋은 상황에서 다른 은기보다 더 잘 작동한다.이와 같이 상황/용례에 따라 서로 다른 데이터 구조가 다른 데이터 구조보다 더욱 적합하고 성능도 더욱 좋다.그것들은 각각 이로움과 폐단이 있다.이러한 장점과 단점을 이해하면 당신이 더 좋은 프로그래머가 되는 데 도움을 줄 수 있다. 왜냐하면 이것은 당신이 자신의 상황/목표에 따라 적당한 데이터 구조를 선택할 수 있고 응용 알고리즘의 성능을 대폭 향상시키는 데 도움이 될 것이다.만약 당신에게 어떤 문제가 있으면 언제든지 평론을 발표하세요!
카탈로그
1. What is Linked List?
2. Implementation in JavaScript
3. Helper Methods
4. Big O
5. Helpful Resources
1. 체인 시계는 무엇입니까?
A Linked List is a collection of data in a sequence, with each of the data referencing its next node (or previous node if it is a Doubly Linked List) from its 'head' to the 'tail'.
체인 테이블은 순서대로 집합하여 표시하는 데이터 형식이다.이 집합의 모든 단락의 데이터를 노드라고 하는데, 이것은 서열의 인접한 노드를 인용한다.체인 테이블의 첫 번째 노드를'머리'라고 하고 마지막 노드를'꼬리'라고 한다.체인 시계는 두 가지 유형이 있는데 그것이 바로 단일 체인 시계와 이중 체인 시계이다.이름과 같이 단일 링크 목록의 노드는 한 방향에서만 링크되기 때문에 모든 노드는 다음 노드를 인용한다.다른 한편, 쌍사슬표의 노드는 상기와 다음 노드를 동시에 인용한다.한 마디로 하면 체인 테이블은 일련의 데이터의 집합으로 모든 데이터는'머리'에서'꼬리'까지 그 다음 노드를 인용한다(쌍체인 테이블이라면 전 노드이다).
그것은 내장된 데이터 구조의 수조처럼 들리지 않습니까?다른 점은 수조가 메모리에서 모든 데이터를 연속적으로 저장하는 데 있는데 이것은 원소가 서로 인접하여 저장된다는 것을 의미한다.모든 요소는 위치 인덱스를 기반으로 하고, 모든 요소는 이 인덱스를 통해 직접 접근할 수 있다.동시에 체인표는 모든 데이터를 메모리의 어느 위치에 저장하지만 노드는 그 다음과 이전 노드를 인용한다.따라서 체인 테이블의 특정 노드에 접근하기 위해서는 찾고자 하는 노드를 찾을 때까지 목록의 머리나 꼬리에서 다른 끝까지 차례대로 이 목록을 옮겨다니야 한다.
이러한 차이로 인해 체인 테이블은 수조보다 더 잘 만들 수 있고 반대로도 마찬가지다.
수조는 더 빨리 검색할 수 있다
우리가 논의한 바와 같이, 수조는 무작위 접근을 지원하기 때문에, 우리는 (n) 색인에 있는 모든 요소를 신속하게 접근할 수 있고, 체인 테이블은 순서대로 접근할 수 있기 때문에, (n) 노드나 찾고 있는 노드의 값을 처음부터 끝까지 검색해야 하기 때문에, 요소를 검색하는 데 더 많은 시간이 필요하다.
체인 테이블은 더 빨리 삽입/삭제할 수 있다
그룹의 시작이나 중간에 있는 요소를 삽입하거나 삭제하려면, 연속적인 색인 위치가 바뀌기 때문에 오른쪽에 있는 모든 요소를 이동해야 합니다.따라서, 그룹의 마지막 요소를 삽입하거나 삭제하지 않으면, 그룹에 요소를 삽입하고 삭제하는 비용이 높을 수 있습니다.체인 테이블에 대해 첫 번째 요소와 마지막 요소를 삽입/삭제하는 데는 고정된 시간이 필요합니다. 왜냐하면 우리는 머리/꼬리만 업데이트할 수 있기 때문입니다.그러나 중간에 있는 요소를 삽입하거나 삭제하는 데는 선형 시간이 필요할 수도 있습니다. 한 번에 한 요소를 훑어보아야 목록을 삽입하거나 삭제할 위치를 찾을 수 있기 때문입니다.그러나 업데이트된 후에 나타나는 모든 요소는 필요 없고 인접한 노드만 다시 배열하면 된다.
2. 자바스크립트 구현
Singly Linked List
// each node references its NEXT node
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
class SinglyLinkedList {
constructor(){
this.head = null;
this.tail = null;
this.length = 0;
}
}
let SLL = new SinglyLinkedList();
let firstNode = new Node(16)
let secondNode = new Node(2)
let thirdNode = new Node(46)
// set the first new node as the SLL's head
SLL.head = firstNode;
SLL.length++;
// second as its next
firstNode.next = secondNode;
SLL.length++;
// the third as the second's next
// while also setting it as a tail since it's the last one.
secondNode.next = SLL.tail = thirdNode;
SLL.length++;
// This SLL will look something like this:
// (16) => (2) => (46)
Doubly Linked List
// each node references both its NEXT and PREVIOUS node
class Node {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
let DLL = new DoublyLinkedList();
let firstNode = new Node(361)
let secondnode = new Node(99)
let thirdNode = new Node(4)
// set the first new node as the DLL's head
DLL.head = firstNode;
DLL.length++;
// second as its next, and head as its prev
firstNode.next = secondNode;
secondNode.prev = firstNode;
DLL.length++;
// the third as the second's next
// while also setting it as a tail since it's the last one.
secondNode.next = DLL.tail = thirdNode;
thirdNode.prev = secondNode;
DLL.length++;
// This SLL will look something like this:
// (361) <=> (99) <=> (4)
We will set up a Node
class which accepts a value and set it to its value, with its next property (and prev if Doubly Linked List) initialized to null. Linked List class will be a sequential collection of these nodes, which will have its head and tail. We will want to keep track of the list's length, and increment/decrement it every time a new node is added or removed. Since Singly Linked Lists's nodes only reference the next
node and Doubly Linked Lists' nodes reference both their next
and previous
nodes, Singly Linked Lists are simpler but less powerful than Doubly Linked Lists.
If you were to implement a helper method to pop the last element of the list, it's easier to do that with Doubly Linked Lists as you simply have to remove the tail of the list, and set the new tail to be the previous node of the tail being removed. On the other hand, we can access the tail of the list, but will have to traverse the entire list and remember the previous node until you hit the tail so you can remove the tail and set the remembered previous node to be the new tail.
The main drawback of using Doubly Linked List vs Singly Linked List is that Doubly Linked List takes up more space than the Singly Linked List since you have to set each nodes' next and previous node. But in return, it opens up more doors to make your data and its algorithms efficient. With that being said, here are couple helper methods to utilize Linked Lists better. However, we will only focus on Doubly Linked Lists for this blog post.
3. 지원 방법(듀얼 링크 목록만 해당)
push()
// accepts a value as an argument
// appends a new node with the value passed at the end of the list
push(value) {
let newNode = new Node(value);
if(!this.head) {
this.head = this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
this.length++;
return this;
}
Pseudo code:
- Create a new node with the value passed to the function
- If the head property is
null
, set thehead
andtail
to be the newly created node - If the head is not
null
, set the next property on thetail
to be that node - Set the
prev
property on the newly created node to be thetail
- Set the
tail
to be the newly created node - Increment the
length
- Return the Linked List
pop()
// removes the last node (tail) of the list
pop() {
if(!this.head) return undefined;
let removedNode = this.tail;
if(this.length === 1) {
this.head = this.tail = null;
} else {
this.tail = removedNode.prev;
this.tail.next = null;
removedNode.prev = null;
}
this.length--;
return removedNode;
}
Pseudo code:
- If there is no
head
, returnundefined
- Store the current
tail
in a variable to return later - If the
length
is 1, set thehead
ortail
to benull
- Update the
tail
to be the previous Node - Set the new
tail
'snext
tonull
- Decrement the
length
- Return the node removed
unshift()
// accepts a value as an argument
// prepends a new node with the value passed at the beginning of the list
unshift(value) {
let newNode = new Node(value);
if(this.length === 0) {
this.head = newNode;
this.tail = this.head;
} else {
this.head.prev = newNode;
newNode.next = this.head;
this.head = newNode;
}
this.length++;
return this;
}
Pseudo code:
- Create a new node with the
value
passed to the function - If the
length
is 0, set thehead
andtail
to be the new node - Otherwise
- Set the
prev
property on thehead
to be the new node - Set the
next
property on the new node to be thehead
property - Update the
head
to be the new node
- Set the
- Increment the
length
- Return the Linked List
shift()
// removes the first node (head) of the list
shift() {
if(this.length === 0) return undefined;
let oldHead = this.head;
if(this.length === 1) {
this.head = null;
this.tail = null;
} else {
this.head = oldHead.next;
this.head.prev = null;
oldHead.next = null;
}
this.length--;
return oldHead;
}
Pseudo code:
- If
length
is 0, returnundefined
- Store the current
head
property in a variable - If the
length
is one, set thehead
andtail
to benull
- Update the
head
to be thenext
of the oldhead
- Set the
head
'sprev
property tonull
- Set the old
head
'snext
tonull
- Decrement the
length
- Return old
head
get()
// accepts an index as an argument
// returns the node at the index passed
get(idx) {
if(idx < 0 || idx >= this.length) return null;
let count, current;
if(idx <= this.length/2 ) {
count = 0;
current = this.head;
while (count !== idx) {
current = current.next
count++
}
return current;
} else {
count = this.length-1;
count = this.tail;
while (count !== idx) {
current = current.prev
count--
}
return current;
}
}
Pseudo code:
- If the index is less than 0 or greater or equal to the
length
, returnnull
- If the index is less than or equal to half the length of the list
- Loop through the list starting from the
head
and loop towards the middle - Return the node once it is found
- Loop through the list starting from the
- If the index is greater than half the length of the list
- Loop through the list starting from the
tail
and loop towards the middle - Return the node once it is found
- Loop through the list starting from the
set()
// accepts an index and value as arguments
// finds the node at the index, and updates the node's value to the value passed
// returns false if the node is not found, true if the value is updated
set(idx, value) {
let foundNode = this.get(idx);
if(!foundNode) return false;
foundNode.value = value;
return true;
}
Pseudo code:
- Create a variable which is the result of the
get
method at the index passed to the function - If the
get
method does not return a valid node, returnfalse
- Set the
value
of the node found fromget
method to thevalue
passed to the function - return
true
4. 대O
공간 복잡성:
밀어내기/팝업 및 스왑/스왑 취소:
O(1)시간 복잡도
가져오기/설정 및 삽입/삭제:
O(n)시간 복잡도
5. 유용한 자원
Online Course(내 과정)제 수업'자바스크립트 알고리즘과 데이터 구조 대가반'을 보세요!이것은 만들어진 것이다. 나는 이 블로그의 데이터 구조 실현 부분에서 그의 코드를 인용했다.개인적으로 나는 알고리즘과 데이터 구조, 특히 비기술적 배경에서 온 알고리즘과 데이터 구조를 어디서부터 배우기 시작했는지 모르겠다.이 과정의 구조는 매우 좋아서 초보자는 이런 주제에 기초를 닦을 수 있다.
Visual Animation(시각 알고리즘)
어떤 사람들에게는 코드/텍스트만 봐도 데이터 구조를 이해하기 어렵다.상기 과정의 강사는 VisuAlgo라는 사이트를 사용하는데 이 사이트는 애니메이션을 통해 알고리즘과 데이터 구조를 직관적으로 보여준다.
Data Structure Cheat Sheet(면접 케이크)
그 밖에 데이터 구조에 대한 아주 좋은 총결산 메모지/가시화도 있다.
Reference
이 문제에 관하여(데이터 구조 시리즈: 체인 테이블), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/chuckchoiboi/data-structure-series-linked-list-3eb2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)