단일 연결 리스트(Singly Linked List)
직접 구현
노드
class Node<E>
{
E data;
Node<E> next;
Node(E data)
{
this.data = data;
this.next = null;
}
}
노드 추가
//리스트의 가장 뒷쪽에 데이터 추가
public void add(E data)
{
Node<E> newNode = new Node<>(data);
if (head == null) head = newNode;
else
{
Node<E> currentHead = head;
while (currentHead.next != null) currentHead = currentHead.next;
currentHead.next = newNode;
}
}
노드 삽입
//리스트의 원하는 인덱스에 데이터 삽입
public void insert(E data, int idx)
{
if (head == null)
{
add(data);
return;
}
Node<E> newNode = new Node<>(data);
Node<E> currentNode = head;
if (currentNode != null && idx == 0)
{
newNode.next = head;
head = newNode;
return;
}
for (int i = 0; i < idx - 1; ++i)
{
if (currentNode == null)
{
System.out.println("Error");
return;
}
currentNode = currentNode.next;
}
if (currentNode == null)
{
System.out.println("Invalid index");
return;
}
newNode.next = currentNode.next;
currentNode.next = newNode;
}
노드 삭제
//앞쪽의 데이터 삭제
public void delete()
{
if (head == null) System.out.println("Empty");
else head = head.next;
}
//특정 데이터의 내용을 포함하는 노드 삭제
public void delete(E data)
{
if (head == null) System.out.println("Empty");
else if (head.data.equals(data)) head = head.next;
else
{
Node<E> currentHead = head;
while (currentHead.next != null && !currentHead.next.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead.next != null) currentHead.next = currentHead.next.next;
else currentHead.next = null;
}
}
노드 검색
//해당 데이터가 존재하는지 아닌지 확인
public void search(E data)
{
if (head == null)
{
System.out.println("Invalid");
return;
}
else
{
Node<E> currentHead = head;
while (currentHead != null && !currentHead.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead == null) System.out.println("Invalid");
else System.out.println("Valid");
}
}
노드 출력
public void printList()
{
Node<E> currentNode = head;
if (currentNode == null)
{
System.out.println("Empty");
return;
}
while (currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.next;
}
System.out.println();
}
전체 소스코드
public class SinglyLinkedList<E>
{
class Node<E>
{
E data;
Node<E> next;
Node(E data)
{
this.data = data;
this.next = null;
}
}
private Node<E> head;
SinglyLinkedList() { this.head = null; }
public void printList()
{
Node<E> currentNode = head;
if (currentNode == null)
{
System.out.println("Empty");
return;
}
while (currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.next;
}
System.out.println();
}
//리스트의 가장 뒷쪽에 데이터 추가
public void add(E data)
{
Node<E> newNode = new Node<>(data);
if (head == null) head = newNode;
else
{
Node<E> currentHead = head;
while (currentHead.next != null) currentHead = currentHead.next;
currentHead.next = newNode;
}
}
//리스트의 원하는 인덱스에 데이터 삽입
public void insert(E data, int idx)
{
if (head == null)
{
add(data);
return;
}
Node<E> newNode = new Node<>(data);
Node<E> currentNode = head;
if (currentNode != null && idx == 0)
{
newNode.next = head;
head = newNode;
return;
}
for (int i = 0; i < idx - 1; ++i)
{
if (currentNode == null)
{
System.out.println("Error");
return;
}
currentNode = currentNode.next;
}
if (currentNode == null)
{
System.out.println("Invalid index");
return;
}
newNode.next = currentNode.next;
currentNode.next = newNode;
}
//앞쪽의 데이터 삭제
public void delete()
{
if (head == null) System.out.println("Empty");
else head = head.next;
}
//특정 데이터의 내용을 포함하는 노드 삭제
public void delete(E data)
{
if (head == null) System.out.println("Empty");
else if (head.data.equals(data)) head = head.next;
else
{
Node<E> currentHead = head;
while (currentHead.next != null && !currentHead.next.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead.next != null) currentHead.next = currentHead.next.next;
else currentHead.next = null;
}
}
//해당 데이터가 존재하는지 아닌지 확인
public void search(E data)
{
if (head == null)
{
System.out.println("Invalid");
return;
}
else
{
Node<E> currentHead = head;
while (currentHead != null && !currentHead.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead == null) System.out.println("Invalid");
else System.out.println("Valid");
}
}
public static void main(String args[])
{
SinglyLinkedList sll = new SinglyLinkedList();
sll.add(1);
sll.add(2);
sll.add(3);
sll.add(4);
sll.insert(0, 0);
sll.insert(0, 2);
sll.insert(0, 4);
sll.delete();
sll.delete(1);
sll.delete(0);
sll.printList();
}
}
책 코드
책 : Do it! 자료구조와 알고리즘 입문 자바편
ArrayLinkedList.java
// 연결 리스트 클래스(배열 커서 버전)
import java.util.Comparator;
public class ArrayLinkedList<E> {
//--- 노드 ---//
class Node<E> {
private E data; // 데이터
private int next; // 리스트의 뒤쪽포인터
private int dnext; // 프리 리스트의 뒤쪽포인터
//--- data와 next를 설정 ---//
void set(E data, int next) {
this.data = data;
this.next = next;
}
}
private Node<E>[] n; // 리스트 본체
private int size; // 리스트 크기(최대 데이터 개수)
private int max; // 사용 중인 꼬리 record
private int head; // 머리노드
private int crnt; // 선택 노드
private int deleted; // 프리 리스트의 머리노드
private static final int NULL = -1; // 뒤쪽노드가 없음 / 리스트가 가득 참
//--- 생성자(constructor) ---//
public ArrayLinkedList(int capacity) {
head = crnt = max = deleted = NULL;
try {
n = new Node[capacity];
for (int i = 0; i < capacity; i++)
n[i] = new Node<E>();
size = capacity;
}
catch (OutOfMemoryError e) { // 배열 생성에 실패
size = 0;
}
}
//--- 다음에 삽입하는 record의 인덱스를 구함 ---//
private int getInsertIndex() {
if (deleted == NULL) { // 삭제 record가 존재하지 않음
if (max < size)
return ++max; // 새 record를 사용
else
return NULL; // 크기 넘침(over)
} else {
int rec = deleted; // 프리 리스트에서
deleted = n[rec].dnext; // 머리 rec을 꺼냄
return rec;
}
}
//--- record idx를 프리 리스트에 등록 ---//
private void deleteIndex(int idx) {
if (deleted == NULL) { // 삭제 record가 존재하지 않음
deleted = idx; // idx를 프리 리스트의
n[idx].dnext = NULL; // 머리에 등록
} else {
int rec = deleted; // idx를 프리 리스트의
deleted = idx; // 머리에 삽입
n[rec].dnext = rec;
}
}
//--- 노드를 검색 ---//
public E search(E obj, Comparator<? super E> c) {
int ptr = head; // 현재 스캔 중인 노드
while (ptr != NULL) {
if (c.compare(obj, n[ptr].data) == 0) {
crnt = ptr;
return n[ptr].data; // 검색 성공
}
ptr = n[ptr].next; // 뒤쪽 노드 선택
}
return null; // 검색 실패
}
//--- 머리노드 삽입 ---//
public void addFirst(E obj) {
int ptr = head; // 삽입 전의 머리노드
int rec = getInsertIndex();
if (rec != NULL) {
head = crnt = rec; // 제 rec 번째 레코드에 삽입
n[head].set(obj, ptr);
}
}
//--- 꼬리노드 삽입 ---//
public void addLast(E obj) {
if (head == NULL) // 리스트가 비어있으면
addFirst(obj); // 머리에 삽입
else {
int ptr = head;
while (n[ptr].next != NULL)
ptr = n[ptr].next;
int rec = getInsertIndex();
if (rec != NULL) { // 제 rec 번째 레코드에 삽입
n[ptr].next = crnt = rec;
n[rec].set(obj, NULL);
}
}
}
//--- 머리노드 삭제 ---//
public void removeFirst() {
if (head != NULL) { // 리스트가 비어있지 않으면
int ptr = n[head].next;
deleteIndex(head);
head = crnt = ptr;
}
}
//--- 꼬리노드 삭제 ---//
public void removeLast() {
if (head != NULL) {
if (n[head].next == NULL) // 노드가 하나만 있으면
removeFirst(); // 머리노드 삭제
else {
int ptr = head; // 스캔 중인 노드
int pre = head; // 스캔 중인 노드의 앞쪽노드
while (n[ptr].next != NULL) {
pre = ptr;
ptr = n[ptr].next;
}
n[pre].next = NULL; // pre는 삭제 뒤의 꼬리 노드
deleteIndex(pre);
crnt = pre;
}
}
}
//--- 레코드 p를 삭제 ---//
public void remove(int p) {
if (head != NULL) {
if (p == head) // p가 머리 노드이면
removeFirst(); // 머리노드 삭제
else {
int ptr = head;
while (n[ptr].next != p) {
ptr = n[ptr].next;
if (ptr == NULL) return; // p가 리스트에 없음
}
n[ptr].next = NULL;
deleteIndex(ptr);
n[ptr].next = n[p].next;
crnt = ptr;
}
}
}
//--- 선택 노드 삭제 ---//
public void removeCurrentNode() {
remove(crnt);
}
//--- 전체 노드 삭제 ---//
public void clear() {
while (head != NULL) // 비게 될 때까지
removeFirst(); // 머리 노드 삭제
crnt = NULL;
}
//--- 선택 노드를 하나 뒤쪽으로 진행 ---//
public boolean next() {
if (crnt == NULL || n[crnt].next == NULL)
return false; // 나아갈 수 없음
crnt = n[crnt].next;
return true;
}
//--- 선택 노드 표시 ---//
public void printCurrentNode() {
if (crnt == NULL)
System.out.println("선택 노드가 없습니다.");
else
System.out.println(n[crnt].data);
}
//--- 전체 노드 표시 ---//
public void dump() {
int ptr = head;
while (ptr != NULL) {
System.out.println(n[ptr].data);
ptr = n[ptr].next;
}
}
}
ArrayLinkedListTester.java
// 선형리스트 클래스 ArrayLinkedList<E>의 사용 예
import java.util.Scanner;
import java.util.Comparator;
class ArrayLinkedListTester {
static Scanner stdIn = new Scanner(System.in);
//--- 데이터(회원번호+이름) ---//
static class Data {
static final int NO = 1; // 번호를 읽어 들일까요?
static final int NAME = 2; // 이름을 읽어 들일까요?
private Integer no; // 회원번호
private String name; // 이름
//--- 문자열 표현을 반환 ---//
public String toString() {
return "(" + no + ") " + name;
}
//--- 데이터를 읽어 들임 ---//
void scanData(String guide, int sw) {
System.out.println(guide + "할 데이터를 입력하세요.");
if ((sw & NO) == NO) {
System.out.print("번호: ");
no = stdIn.nextInt();
}
if ((sw & NAME) == NAME) {
System.out.print("이름: ");
name = stdIn.next();
}
}
//--- 회원번호로 순서를 매기는 comparator ---//
public static final Comparator<Data> NO_ORDER =
new NoOrderComparator();
private static class NoOrderComparator implements Comparator<Data> {
public int compare(Data d1, Data d2) {
return (d1.no > d2.no) ? 1 : (d1.no < d2.no) ? -1 : 0;
}
}
//--- 이름으로 순서를 매기는 comparator ---//
public static final Comparator<Data> NAME_ORDER =
new NameOrderComparator();
private static class NameOrderComparator implements Comparator<Data> {
public int compare(Data d1, Data d2) {
return d1.name.compareTo(d2.name);
}
}
}
//--- 메뉴 열거형 ---//
enum Menu {
ADD_FIRST( "머리 노드 삽입"),
ADD_LAST( "꼬리 노드 삽입"),
RMV_FIRST( "머리 노드 삭제"),
RMV_LAST( "꼬리 노드 삭제"),
RMV_CRNT( "선택 노드 삭제"),
CLEAR( "전체 노드 삭제"),
SEARCH_NO( "번호 검색"),
SEARCH_NAME("이름 검색"),
NEXT( "선택 노드 진행"),
PRINT_CRNT( "선택 노드 표시"),
DUMP( "전체 노드 표시"),
TERMINATE( "종료");
private final String message; // 표시할 문자열
static Menu MenuAt(int idx) { // 순서가 idx번째인 열거를 반환
for (Menu m : Menu.values())
if (m.ordinal() == idx)
return m;
return null;
}
Menu(String string) { // 생성자(constructor)
message = string;
}
String getMessage() { // 표시할 문자열을 반환
return message;
}
}
//--- 메뉴 선택 ---//
static Menu SelectMenu() {
int key;
do {
for (Menu m : Menu.values()) {
System.out.printf("(%d) %s ", m.ordinal(), m.getMessage());
if ((m.ordinal() % 3) == 2 &&
m.ordinal() != Menu.TERMINATE.ordinal())
System.out.println();
}
System.out.print(" : ");
key = stdIn.nextInt();
} while (key < Menu.ADD_FIRST.ordinal() ||
key > Menu.TERMINATE.ordinal());
return Menu.MenuAt(key);
}
public static void main(String[] args) {
Menu menu; // 메뉴
Data data; // 추가용 데이터 참조
Data ptr; // 검색용 데이터 참조
Data temp = new Data(); // 읽어 들일 데이터
ArrayLinkedList<Data> list = new ArrayLinkedList<Data>(100);
do {
switch (menu = SelectMenu()) {
case ADD_FIRST : // 머리 노드 삽입
data = new Data();
data.scanData("머리에 삽입", Data.NO | Data.NAME);
list.addFirst(data);
break;
case ADD_LAST : // 꼬리 노드 삽입
data = new Data();
data.scanData("꼬리에 삽입", Data.NO | Data.NAME);
list.addLast(data);
break;
case RMV_FIRST : // 머리 노드 삭제
list.removeFirst();
break;
case RMV_LAST : // 꼬리 노드 삭제
list.removeLast();
break;
case RMV_CRNT : // 선택 노드 삭제
list.removeCurrentNode();
break;
case SEARCH_NO : // 회원번호 검색
temp.scanData("검색", Data.NO);
ptr = list.search(temp, Data.NO_ORDER);
if (ptr == null)
System.out.println("그 번호의 데이터가 없습니다.");
else
System.out.println("검색 성공 : " + ptr);
break;
case SEARCH_NAME : // 이름 검색
temp.scanData("검색", Data.NAME);
ptr = list.search(temp, Data.NAME_ORDER);
if (ptr == null)
System.out.println("그 이름의 데이터가 없습니다.");
else
System.out.println("검색 성공 : " + ptr);
break;
case NEXT : // 선택 노드를 뒤쪽으로 진행
list.next();
break;
case PRINT_CRNT : // 선택 노드의 데이터를 표시
list.printCurrentNode();
break;
case DUMP : // 모든 데이터를 리스트 순서로 표시
list.dump();
break;
case CLEAR : // 전체노드 삭제
list.clear();
break;
}
} while (menu != Menu.TERMINATE);
}
}
이중 연결 리스트(Doubly Linked List)
노드
class Node<E>
{
E data;
Node<E> prev;
Node<E> next;
Node(E data)
{
this.data = data;
this.prev = null;
this.next = null;
}
}
노드 추가
//리스트 앞쪽에 추가
public void add(E data)
{
Node<E> newNode = new Node(data);
Node<E> currentNode = head;
if(currentNode == null)
{
head = newNode;
tail = newNode;
}
else
{
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
}
//리스트 뒷쪽에 추가
public void add(E data, boolean back)
{
if(back == false) add(data);
else
{
Node<E> newNode = new Node(data);
Node<E> currentNode = tail;
if(currentNode == null)
{
head = newNode;
tail = newNode;
}
else
{
head.prev = newNode;
newNode.prev = null;
head = newNode;
}
}
}
노드 삽입
//원하는 인덱스에 노드 삽입
public void insert(E data, int idx)
{
Node<E> newNode = new Node(data);
if(head == null) add(data);
if (head == null || idx == 0)
{
newNode.next = head;
if (head != null) head.prev = newNode;
head = newNode;
if (tail == null) tail = newNode;
}
else
{
Node<E> currentNode = head;
for(int i = 0; i < idx - 1; ++i)
{
if(currentNode == null)
{
System.out.println("Error");
return;
}
currentNode = currentNode.next;
}
if (currentNode == null)
{
System.out.println("Invalid index");
return;
}
newNode.next = currentNode.next;
if (currentNode.next != null) currentNode.next.prev = newNode;
newNode.prev = currentNode;
currentNode.next = newNode;
if(newNode.next == null) tail = newNode;
}
}
노드 삭제
//노드를 앞 또는 뒷부분 삭제
public void delete(boolean back)
{
if (head == null) System.out.println("Empty");
else if(back == false) //앞부분 삭제
{
head = head.next;
if (head != null) head.prev = null;
else tail = null; // 리스트에 하나의 노드만 있었고 삭제한 경우
}
else //뒷부분 삭제
{
tail = tail.prev;
if (tail != null) tail.next = null;
else head = null; // 리스트에 하나의 노드만 있었고 삭제한 경우
}
}
//특정 데이터의 내용을 포함하는 노드 삭제
public void delete(E data)
{
if (head == null) System.out.println("Empty");
else
{
Node<E> currentNode = head;
while(currentNode.next != null && !currentNode.next.data.equals(data))
{
currentNode = currentNode.next;
}
if(currentNode.next != null)
{
currentNode.next = currentNode.next.next;
currentNode.next.next.prev = currentNode;
}
}
}
노드 검색
//해당 데이터가 존재하는지 아닌지 확인
public void search(E data)
{
if (head == null)
{
System.out.println("Invalid");
return;
}
else
{
Node<E> currentHead = head;
while (currentHead != null && !currentHead.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead == null) System.out.println("Invalid");
else System.out.println("Valid");
}
}
노드 출력
//리스트를 앞쪽부터 출력
public void printList()
{
Node<E> currentNode = head;
while(currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.next;
}
System.out.println();
}
//리스트를 뒷쪽부터 출력
public void printList(boolean back)
{
if(back == false) printList();
else
{
Node<E> currentNode = tail;
while(currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.prev;
}
System.out.println();
}
}
전체 코드
public class DoublyLinkedList<E>
{
class Node<E>
{
E data;
Node<E> prev;
Node<E> next;
Node(E data)
{
this.data = data;
this.prev = null;
this.next = null;
}
}
private Node<E> head;
private Node<E> tail;
DoublyLinkedList()
{
this.head = null;
this.tail = null;
}
//리스트를 앞쪽부터 출력
public void printList()
{
Node<E> currentNode = head;
while(currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.next;
}
System.out.println();
}
//리스트를 뒷쪽부터 출력
public void printList(boolean back)
{
if(back == false) printList();
else
{
Node<E> currentNode = tail;
while(currentNode != null)
{
System.out.print(currentNode.data + " ");
currentNode = currentNode.prev;
}
System.out.println();
}
}
//리스트 앞쪽에 추가
public void add(E data)
{
Node<E> newNode = new Node(data);
Node<E> currentNode = head;
if(currentNode == null)
{
head = newNode;
tail = newNode;
}
else
{
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
}
//리스트 뒷쪽에 추가
public void add(E data, boolean back)
{
if(back == false) add(data);
else
{
Node<E> newNode = new Node(data);
Node<E> currentNode = tail;
if(currentNode == null)
{
head = newNode;
tail = newNode;
}
else
{
head.prev = newNode;
newNode.prev = null;
head = newNode;
}
}
}
//원하는 인덱스에 노드 삽입
public void insert(E data, int idx)
{
Node<E> newNode = new Node(data);
if(head == null) add(data);
if (head == null || idx == 0)
{
newNode.next = head;
if (head != null) head.prev = newNode;
head = newNode;
if (tail == null) tail = newNode;
}
else
{
Node<E> currentNode = head;
for(int i = 0; i < idx - 1; ++i)
{
if(currentNode == null)
{
System.out.println("Error");
return;
}
currentNode = currentNode.next;
}
if (currentNode == null)
{
System.out.println("Invalid index");
return;
}
newNode.next = currentNode.next;
if (currentNode.next != null) currentNode.next.prev = newNode;
newNode.prev = currentNode;
currentNode.next = newNode;
if(newNode.next == null) tail = newNode;
}
}
//노드를 앞 또는 뒷부분 삭제
public void delete(boolean back)
{
if (head == null) System.out.println("Empty");
else if(back == false) //앞부분 삭제
{
head = head.next;
if (head != null) head.prev = null;
else tail = null; // 리스트에 하나의 노드만 있었고 삭제한 경우
}
else //뒷부분 삭제
{
tail = tail.prev;
if (tail != null) tail.next = null;
else head = null; // 리스트에 하나의 노드만 있었고 삭제한 경우
}
}
//특정 데이터의 내용을 포함하는 노드 삭제
public void delete(E data)
{
if (head == null) System.out.println("Empty");
else
{
Node<E> currentNode = head;
while(currentNode.next != null && !currentNode.next.data.equals(data))
{
currentNode = currentNode.next;
}
if(currentNode.next != null)
{
currentNode.next = currentNode.next.next;
currentNode.next.next.prev = currentNode;
}
}
}
//해당 데이터가 존재하는지 아닌지 확인
public void search(E data)
{
if (head == null)
{
System.out.println("Invalid");
return;
}
else
{
Node<E> currentHead = head;
while (currentHead != null && !currentHead.data.equals(data))
{
currentHead = currentHead.next;
}
if (currentHead == null) System.out.println("Invalid");
else System.out.println("Valid");
}
}
public static void main(String args[])
{
DoublyLinkedList dll = new DoublyLinkedList<>();
dll.add(1);
dll.add(2);
dll.add(3);
dll.add(4);
dll.insert(0,0);
dll.insert(0,2);
dll.insert(0,4);
dll.insert(0,6);
dll.printList();
dll.delete(false);
dll.printList();
dll.delete(true);
dll.printList();
dll.search(9);
}
}
'자료구조 & 알고리즘 > 알고리즘' 카테고리의 다른 글
[Java] 문자열 탐색(브루트 포스, KMP, 보이어 무어) (0) | 2024.01.15 |
---|---|
[Java] 정렬 알고리즘(Sorting Algorithm) (1) | 2024.01.15 |
[알고리즘] 다익스트라(Dijkstra) 알고리즘 (1) | 2023.12.19 |
[알고리즘] 최소 신장 트리(Minimum Spanning Tree) (1) | 2023.12.18 |
[알고리즘] 그래프 위상 정렬(Topological sorting) (1) | 2023.12.17 |