-
Notifications
You must be signed in to change notification settings - Fork 0
[Assignment] 재영 - 양방향 연결 리스트 구현 #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
The head ref may contain hidden characters: "7/\uC7AC\uC601/\uC591\uBC29\uD5A5\uC5F0\uACB0\uB9AC\uC2A4\uD2B8"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| ## HOW TO CHECK | ||
|
|
||
| 루트 디렉토리를 기준으로 다음을 순차적으로 입력해주세요. | ||
|
|
||
| ### 현재 과제 디렉토리로 이동 | ||
|
|
||
| ```bash | ||
| cd JavaScript | ||
| cd 07주차 | ||
| cd assignment | ||
| cd 재영 | ||
| ``` | ||
|
|
||
| ### 커맨드 실행 | ||
|
|
||
| ```bash | ||
| npm test | ||
| ``` | ||
|
|
||
| `pnpm`을 쓰신다면 `pnpm`으로 테스트 가능합니다. | ||
| ```bash | ||
| pnpm test | ||
| ``` | ||
|
|
||
| `yarn`을 쓰신다면 `yarn`으로 테스트 가능합니다. | ||
| ```bash | ||
| yarn test | ||
| ``` | ||
|
|
||
| ### 테스트 코드 작성 | ||
|
|
||
| `test.js`에서 테스트가 가능합니다. | ||
|
|
||
| #### 작성 방법 | ||
|
|
||
| `expect`가 같은 것임을 확인하는 함수라면, `describe`는 일련의 `expect`를 단위로 묶어주는 grouping을 위한 함수입니다. | ||
|
|
||
| `describe`에 첫 번째 인자는 `"테스트 그룹명"`을, 두 번째 인자에는 `expect들의 배열`을 넣어주세요. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| // [x] 양방향 연결리스트는 각 노드의 prev와 next로 이동할 수 있어야 합니다. | ||
| class Node { | ||
| constructor(val, prev = null, next = null) { | ||
| this.val = val; | ||
| this.prev = prev; | ||
| this.next = next; | ||
| } | ||
| } | ||
|
|
||
| export class DoubleLinkedList { | ||
| constructor(val) { | ||
| this.node = val ? new Node(val) : null; | ||
| this.head = val ? this.node : null; | ||
| this.tail = val ? this.node : null; | ||
|
|
||
| this.cnt = val ? 1 : 0; | ||
| } | ||
|
|
||
| get prevNode() { | ||
| return this.node.prev ?? this.node; | ||
| } | ||
|
|
||
| get nextNode() { | ||
| return this.node.next ?? this.node; | ||
| } | ||
|
|
||
| // [x] insert메서드를 통해 특정 인덱스의 노드에 삽입할 수 있어야 해요. | ||
| insert(val) { | ||
| if (!this.cnt) { | ||
| const nowNode = new Node(val); | ||
| this.node = nowNode; | ||
| this.head = nowNode; | ||
| this.tail = nowNode; | ||
| } else { | ||
| const nowNode = new Node(val, this.node, this.node.next ?? this.head); | ||
| this.nextNode.prev = nowNode; | ||
| this.node.next = nowNode; | ||
| this.node = nowNode; | ||
|
|
||
| if (this.node.next === this.head) this.tail = nowNode; | ||
| } | ||
|
|
||
| this.cnt += 1; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| // [x] get를 통해 원하는 인덱스의 노드를 가져올 수 있어야 해요. | ||
| getNthNodeFromHead(order) { | ||
| let i = 0; | ||
| let now = this.head; | ||
| while (i < order) { | ||
| i += 1; | ||
| now = now.next; | ||
| } | ||
|
|
||
| return now; | ||
| } | ||
|
|
||
| getNthNode(order) { | ||
| let i = 0; | ||
| let now = this.node; | ||
| while (i < order) { | ||
| i += 1; | ||
| now = now.next; | ||
| } | ||
|
|
||
| return now; | ||
| } | ||
|
|
||
| get nowNode() { | ||
| return this.node; | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근자 getter를 사용하신 이유가 궁금합니다! doubleLinkedList.node를 해주어도 node의 값을 알 수 있지 않을까요??
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
아래는 도움이 되는 예시입니다. class A {
constructor() { this.node = 1 }
get Node() {
return this.Node
}
}
const a = new A();
a.node = 3;
console.log(a.node) // 3
a.Node = 5;
console.log(a.node) // 3 ; not updated |
||
|
|
||
| // [x] update를 통해 해당 인덱스의 노드를 수정할 수 있어야 해요. | ||
| update(val) { | ||
| this.node.val = val; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| // [x] remove를 통해 해당 인덱스의 노드를 삭제할 수 있어야 해요. | ||
| remove() { | ||
| if (!this.cnt) return this; | ||
|
|
||
| if (this.cnt === 1) { | ||
| this.node = null; | ||
|
|
||
| this.head = null; | ||
| this.tail = null; | ||
| } else { | ||
| this.nextNode.prev = this.node.prev; | ||
| this.prevNode.next = this.node.next; | ||
|
|
||
| if (this.head === this.node) this.head = this.node.next; | ||
| this.node = this.node.next; | ||
| } | ||
|
|
||
| this.cnt -= 1; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| goPrev() { | ||
| if (this.cnt === 0) return this; | ||
| if (this.cnt === 1) return this.node; | ||
|
|
||
| this.node = this.node.prev; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| goNext() { | ||
| if (this.cnt === 0) return this; | ||
| if (this.cnt === 1) return this.node; | ||
|
|
||
| this.node = this.node.next; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| get length() { | ||
| return this.cnt; | ||
| } | ||
| } | ||
|
|
||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "main": "index.js", | ||
| "type": "module", | ||
| "scripts": { | ||
| "test": "node ./test.js" | ||
| } | ||
| } | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EOL 지켜주세요 😖 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import { DoubleLinkedList } from "./index.js"; | ||
|
|
||
| function expect(string = "", cb) { | ||
| if (!new.target) { | ||
| return new expect(string, cb); | ||
| } | ||
|
|
||
| this.isNot = false; | ||
|
|
||
| Object.defineProperty(this, 'not', { | ||
| get() { | ||
| this.isNot = !this.isNot; | ||
| return this; | ||
| }, | ||
| }); | ||
|
|
||
| this.toBeEqual = (val) => { | ||
| const res = cb(); | ||
| const result = this.isNot ? res !== val : res === val; | ||
|
|
||
| const nowIsNot = this.isNot; | ||
|
|
||
| this.isNot = false; | ||
|
|
||
| console.log({ command: string, result, ...(!result ? { expectedValue: `${nowIsNot ? 'not ' : ''}${val}`, actualValue: res } : {}) }); | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
| function describe(title, tests) { | ||
| try { | ||
| console.log(`${title} 결과: `) | ||
| const res = tests.filter(v => v).length; | ||
| if (res === tests.length) { | ||
| console.log(`✅ CHECKED ${tests.length} TESTS SUCCEED!`) | ||
| } else { | ||
| throw new Error(`🔥 ${res}/${tests.length} TESTS SUCCEED. FAILED 😖`) | ||
| } | ||
| } catch(e) { | ||
| console.error("🤯 FAILED TEST!\n") | ||
| console.error(e) | ||
| } | ||
| } | ||
|
|
||
| describe("양방향 연결리스트", [ | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헷갈리게 한 원흉(?)을 '양방향 원형 연결리스트'로 수정합니다. |
||
| expect("3개를 추가한 후 현재를 반환하면, 길이가 3개가 나와야 한다.", () => { | ||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| return doubleLinkedList.insert(1).insert(2).insert(3).length; | ||
| }).toBeEqual(3), | ||
| expect("마지막을 삭제하면, 마지막은 첫번째 머리 노드가 나와야 한다.", () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 양방향 연결리스트를 잘못 이해한 것인지 모르겠는데 이 부분이 잘 이해가 안돼서요..! 마지막을 삭제하면 마지막이 왜 첫번째 머리 노드가 되나요??
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아, 저는 양방향 원형 연결 리스트(Circularly Doubly Linked List)를 만들었어요!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제 코드가 헷갈리게 한 것 같은데, 미네님께서 하신 것이 원래 양방향 연결 리스트가 맞다고 보시면 돼요 :)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 그렇군요! 양방향 원형 연결 리스트도 한번 공부해보고 다시 코드를 보겠습니당 😂 |
||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| return doubleLinkedList.insert(1).insert(2).insert(3).remove().nowNode.val; | ||
| }).toBeEqual(1), | ||
| expect("첫번째 머리 노드를 삭제하면, 추출 시 다음 노드의 앞은 꼬리 노드가 나와야 한다.", () => { | ||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().nowNode.val; | ||
| }).toBeEqual(2), | ||
| expect("첫번째 머리 노드를 삭제하면, 다음 머리는 다음 노드가 되어야 한다.", () => { | ||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| return doubleLinkedList.insert(1).insert(2).insert(3).goPrev().goPrev().remove().head.val; | ||
| }).toBeEqual(2), | ||
| expect("getNthNodeFromHead 메서드로 순회를 한다면 머리 노드를 기준으로 나와야 한다", () => { | ||
| const doubleLinkedList = new DoubleLinkedList(); | ||
| return doubleLinkedList | ||
| .insert(1) | ||
| .insert(2) | ||
| .insert(3) | ||
| .insert(4) | ||
| .insert(5) | ||
| .insert(6) | ||
| .insert(7) | ||
| .insert(8) | ||
| .insert(9) | ||
| .getNthNode(8) | ||
| .val | ||
| }).toBeEqual(8) | ||
| ]); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EOL 지켜주세요... 😭 |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constructor 인자로 val을 넣어준 이유가 궁금합니다!🙋🏻♀️ 테스트 코드에서 보면 insert로 값을 추가하는 것 같은데 val 값을 인자로 넘겨주어 인스턴스를 생성하는 경우도 고려하신건가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네 맞습니다 :)
사용자가 처음부터 초기값을 넣어줄 수도 있고, 넣지 않을 수도 있다고 생각했어요.
유동적으로 입맛에 맞게 쓸 수 있도록 했다고 보시면 될 것 같아요 :)