๐Ÿ’ป Knowledge/์ž๋ฃŒ๊ตฌ์กฐ

๐Ÿง‘๐Ÿป‍๐Ÿ’ป Doubly Linked List ์ง์ ‘ ๊ตฌํ˜„ with dummy head & dummy tail (JAVA)

ming412 2023. 9. 25. 11:54

 

https://ming412.tistory.com/167

 

๐Ÿง‘๐Ÿป‍๐Ÿ’ป Doubly Linked List ์ง์ ‘ ๊ตฌํ˜„ (JAVA)

1. Doubly Linked List ์ž๋ฃŒ๊ตฌ์กฐ https://ming412.tistory.com/159 Singly Linked List vs Doubly Linked List vs Circular Linked List ์‹œ๊ฐ„๋ณต์žก๋„ & ์‚ฌ์šฉ ์‚ฌ๋ก€ Singly Linked List (๋‹จ์ผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ) ๊ฐ ์š”์†Œ๊ฐ€ ๋‹ค์Œ ์š”์†Œ๋งŒ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋‹จ

ming412.tistory.com

dummy node๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  Doubly Linked List๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์œ„ ๊ธ€์— ์ž์„ธํžˆ ์ ์–ด๋†“์•˜๋‹ค.

 

์ด๋ฒˆ์—๋Š” dummy node๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ„๋‹จํžˆ search, add, remove ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ ,

dummy node๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์˜ ์žฅ๋‹จ์ ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

 

1. Doubly Linked List ์ง์ ‘ ๊ตฌํ˜„ (with. dummy head & dummy tail)

1) ADT (Abstract Data Type) ์ •์˜

- ๋ฐ์ดํ„ฐ (Data)

    - Node

        - data: ๋ฐ์ดํ„ฐ

        - prev: ์ด์ „ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐ

        - next: ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐ

    - MyDoublyLinkedList

        - dHead: ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ์ฒ˜์Œ ์œ„์น˜์— dummy head ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐ

        - dTail: ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ๋งˆ์ง€๋ง‰ ์œ„์น˜์— dummy tail ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐ

        - size: ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ๊ธธ์ด

 

- ์ž‘์—… 

    - search(idx): ํŠน์ • ์œ„์น˜์˜ ๋…ธ๋“œ ๋ฐ˜ํ™˜

    - add(idx, e): ํŠน์ • ์œ„์น˜์— ์š”์†Œ ์ถ”๊ฐ€

    - remove(idx): ํŠน์ • ์œ„์น˜์˜ ์š”์†Œ ์‚ญ์ œ

 

2) Node ํด๋ž˜์Šค ์ •์˜

Node ํด๋ž˜์Šค๋Š” dummy node๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  Doubly Linked List๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ์™€ ๋™์ผํ•˜๋‹ค.

public class Node<E> {
	public E data;
	public Node<E> prev;
	public Node<E> next;

	public Node(E data) {
		this.data = data;
		this.prev = null;
		this.next = null;
	}
}

 

3) DoublyLinkedList ํด๋ž˜์Šค ์ •์˜

dummy node๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋Š” `head`์™€ `tail`์„ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ์ง€๋งŒ, ์ง€๊ธˆ์€ `dHead`์™€ `dTail`์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

`head`์™€ `dHead`, ๊ทธ๋ฆฌ๊ณ  `tail`๊ณผ `dTail`์˜ ์ฐจ์ด์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

`head`: ์‹ค์ œ ์ฒซ ๋…ธ๋“œ

`dHead`: ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ์ฒ˜์Œ ์œ„์น˜์— ์žˆ๋Š” ๋…ธ๋“œ๋กœ, ์œ ์˜๋ฏธํ•œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ฒซ ๋…ธ๋“œ๋Š” `dHead.next`

 

`tail`: ์‹ค์ œ ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ

`dTail`: ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ๋งˆ์ง€๋ง‰ ์œ„์น˜์— ์žˆ๋Š” ๋…ธ๋“œ๋กœ, ์œ ์˜๋ฏธํ•œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ๋Š” `dTail.prev`

public class DoublyLinkedList<E> {
	private Node<E> dHead; // dummyHead
	private Node<E> dTail; // dummyTail
	private int size;

	public DoublyLinkedList() {
		this.dHead = new Node<>();
		this.dTail = new Node<>();
		dHead.next = dTail;
		dTail.prev = dHead;
	}
}

 

4) search ๊ตฌํ˜„

์ฐพ๊ณ ์ž ํ•˜๋Š” ๋…ธ๋“œ(`idx`)๊ฐ€ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ์•ž ๋ถ€๋ถ„์— ์กด์žฌํ•˜๋Š”์ง€ or ๋’ท ๋ถ€๋ถ„์— ์กด์žฌํ•˜๋Š”์ง€๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค.

์•ž ๋ถ€๋ถ„์— ์กด์žฌํ•˜๋ฉด `dHead`๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ํƒ์ƒ‰ํ•˜๊ณ , ๋’ท ๋ถ€๋ถ„์— ์กด์žฌํ•˜๋ฉด `dTail`๋ถ€ํ„ฐ ์—ญ์ˆœ์œผ๋กœ ํƒ์ƒ‰ํ•œ๋‹ค.

public Node<E> search(int idx) {
    if (size < idx) {
        throw new IndexOutOfBoundsException();
    }
    Node<E> cur;
    if (idx < size/2) {
        cur = dHead;
        for (int i=-1; i<idx; i++) {
            cur = cur.next;
        }
    } else {
        cur = dTail;
        for (int i=size; i>idx; i--) {
            cur = cur.prev;
        }
    }
    return cur;
}

 

5) add ๊ตฌํ˜„

dummy node๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋Š”,

์ƒˆ๋กœ์šด ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ์ž ํ•˜๋Š” ์œ„์น˜(`idx`)๊ฐ€ 0์ผ ๋•Œ(=๋งจ ์ฒ˜์Œ), `size`์ผ ๋•Œ(=๋งจ ๋), ๋‘˜ ๋‹ค ์•„๋‹ ๋•Œ(=์ค‘๊ฐ„ ์œ„์น˜)๋กœ ๊ตฌ๋ถ„ํ–ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ dummy node๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ์— ํ•ญ์ƒ `dHead`์™€ `dTail` ๋…ธ๋“œ๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ ๋ณ„๋„์˜ ์กฐ๊ฑด๋ฌธ ์—†์ด ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

public void add(int idx, E data) {
    if (idx < 0 || idx > size) {
        throw new IndexOutOfBoundsException();
    }
    Node<E> newNode = new Node<>(data);
    Node<E> targetNode = search(idx);

    // targetNode์™€ targetNode์˜ ๋ฐ”๋กœ ์•ž ๋…ธ๋“œ ์‚ฌ์ด์— newNode๋ฅผ ๋ผ์›Œ๋„ฃ๋Š” ๊ฒƒ
    newNode.next = targetNode; // newNode ๋‹ค์Œ์— targetNode ์—ฐ๊ฒฐ
    newNode.prev = targetNode.prev; // newNode์™€ targetNode ์•ž ๋…ธ๋“œ ์—ฐ๊ฒฐ

    targetNode.prev.next = newNode; // targetNode์˜ ์•ž ๋…ธ๋“œ์™€ newNode ์—ฐ๊ฒฐ
    targetNode.prev = newNode; // targetNode ์•ž ๋…ธ๋“œ๋ฅผ newNode๋กœ ๊ฐฑ์‹ 

    size++;
}

 

6) remove ๊ตฌํ˜„

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋งจ ์ฒ˜์Œ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•˜๋Š”์ง€ ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•˜๋Š”์ง€ ์ค‘๊ฐ„ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

 

์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์—์„œ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์—ฐ๊ฒฐ์„ ๋Š์–ด ๊ณ ๋ฆฝ์‹œํ‚ค๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํ–ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์‚ญ์ œํ•  ๋…ธ๋“œ์˜ ์ด์ „ ๋…ธ๋“œ์™€ ์‚ญ์ œํ•  ๋…ธ๋“œ์˜ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ์„œ๋กœ ์—ฐ๊ฒฐ์‹œ์ผœ `targetNode`๋ฅผ ๊ณ ๋ฆฝ์‹œํ‚ค๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

public void remove(int idx) {
    if (idx < 0 || idx > size) {
        throw new IndexOutOfBoundsException();
    }
    Node<E> targetNode = search(idx); // ์‚ญ์ œํ•  ๋…ธ๋“œ
    // ์‚ญ์ œํ•  ๋…ธ๋“œ์˜ ์ด์ „ ๋…ธ๋“œ์™€ ์‚ญ์ œํ•  ๋…ธ๋“œ์˜ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ์„œ๋กœ ์—ฐ๊ฒฐ (์‚ญ์ œํ•  ๋…ธ๋“œ ๊ณ ๋ฆฝ)
    targetNode.prev.next = targetNode.next; 
    targetNode.next.prev = targetNode.prev;

    size--;
}

 

7) ์ „์ฒด ์ฝ”๋“œ

public class DoublyLinkedList<E> {
	private Node<E> dHead; // dummyHead
	private Node<E> dTail; // dummyTail
	private int size;

	public DoublyLinkedList() {
		this.dHead = new Node<>();
		this.dTail = new Node<>();
		dHead.next = dTail;
		dTail.prev = dHead;
	}

	public Node<E> search(int idx) {
		if (size < idx) {
			throw new IndexOutOfBoundsException();
		}
		Node<E> cur;
		if (idx < size/2) {
			cur = dHead;
			for (int i=-1; i<idx; i++) {
				cur = cur.next;
			}
		} else {
			cur = dTail;
			for (int i=size; i>idx; i--) {
				cur = cur.prev;
			}
		}
		return cur;
	}

	// dummy node๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด add ์‹œ idx๊ฐ€ ๋งจ ์ฒ˜์Œ/๋งˆ์ง€๋ง‰์ผ ๊ฒฝ์šฐ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
	public void add(int idx, E data) {
		if (idx < 0 || idx > size) {
			throw new IndexOutOfBoundsException();
		}
		Node<E> newNode = new Node<>(data);
		Node<E> targetNode = search(idx);

		// targetNode์™€ targetNode์˜ ๋ฐ”๋กœ ์•ž ๋…ธ๋“œ ์‚ฌ์ด์— newNode๋ฅผ ๋ผ์›Œ๋„ฃ๋Š” ๊ฒƒ
		newNode.next = targetNode; // newNode ๋‹ค์Œ์— targetNode ์—ฐ๊ฒฐ
		newNode.prev = targetNode.prev; // newNode์™€ targetNode ์•ž ๋…ธ๋“œ ์—ฐ๊ฒฐ

		targetNode.prev.next = newNode; // targetNode์˜ ์•ž ๋…ธ๋“œ์™€ newNode ์—ฐ๊ฒฐ
		targetNode.prev = newNode; // targetNode ์•ž ๋…ธ๋“œ๋ฅผ newNode๋กœ ๊ฐฑ์‹ 

		size++;
	}

	public void remove(int idx) {
		if (idx < 0 || idx > size) {
			throw new IndexOutOfBoundsException();
		}
		Node<E> targetNode = search(idx);
		targetNode.prev.next = targetNode.next;
		targetNode.next.prev = targetNode.prev;

		size--;
	}

	public void printAll() {
		Node<E> cur = dHead;
		System.out.print("dummy head - ");
		while (cur.next != dTail) {
			cur = cur.next;
			System.out.print(cur.data + " - ");
		}
		System.out.println("dummy tail");
	}
}
public class Main {
	public static void main(String[] args) {
		DoublyLinkedList<String> linkedList = new DoublyLinkedList<>();
		linkedList.add(0, "A");
		linkedList.add(0, "B");
		linkedList.printAll();
		linkedList.add(1, "C");
		linkedList.add(1, "D");
		linkedList.printAll();
		linkedList.add(4, "E");
		linkedList.printAll();

		System.out.println(linkedList.search(1).data);

		linkedList.remove(3);
		linkedList.printAll();
		linkedList.remove(0);
		linkedList.printAll();
	}
}

์‹คํ–‰ ๊ฒฐ๊ณผ

 

2. dummy node๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์˜ ์žฅ๋‹จ์ 

์žฅ์ 

- ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ๋•Œ ๋ณ„๋„์˜ ์กฐ๊ฑด๋ฌธ ์—†์ด ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

- ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ(ex. ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด์žˆ์„ ๋•Œ)๋ฅผ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฒ„๊ทธ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ์ค„์–ด๋“ ๋‹ค.

 

๋‹จ์ 

- ๋”๋ฏธ ๋…ธ๋“œ๋“ค๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

- ์˜๋ฏธ ์žˆ๋Š” ๊ฐ’์ด ์•„๋‹Œ ๋”๋ฏธ ๊ฐ’์ด ์‹ค์ œ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์— ์กด์žฌํ•˜๋ฏ€๋กœ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋‹ค.