Javaによる単方向・双方向・循環リストの実装と操作

リスト構造は順序を持ち、ノード単位で連結されるが、メモリ上では必ずしも連続していない。

  • 各ノードはデータ領域と次のノードへの参照を持つ
  • 先頭ノード(ヘッド)は実データを持たず、リストの起点として機能する
  • ノードの配置は物理的に連続している必要はない
  • ヘッドノード有り/無しの設計は用途に応じて選択可能

基本操作:追加・更新・削除・逆転

public class NodeManager {
    private ListNode head = new ListNode(0, "", "");

    static class ListNode {
        int id;
        String title;
        String alias;
        ListNode next;

        ListNode(int id, String title, String alias) {
            this.id = id;
            this.title = title;
            this.alias = alias;
        }

        @Override
        public String toString() {
            return "Node{id=" + id + ", title='" + title + "', alias='" + alias + "'}";
        }
    }

    public void append(ListNode node) {
        ListNode cursor = head;
        while (cursor.next != null) cursor = cursor.next;
        cursor.next = node;
    }

    public void modify(ListNode updated) {
        ListNode cursor = head.next;
        while (cursor != null && cursor.id != updated.id) cursor = cursor.next;
        if (cursor != null) {
            cursor.title = updated.title;
            cursor.alias = updated.alias;
        } else {
            System.out.printf("ID %d のノードが見つかりません%n", updated.id);
        }
    }

    public void remove(int targetId) {
        ListNode cursor = head;
        while (cursor.next != null && cursor.next.id != targetId) cursor = cursor.next;
        if (cursor.next != null) {
            cursor.next = cursor.next.next;
        } else {
            System.out.printf("ID %d のノードは存在しません%n", targetId);
        }
    }

    public void display() {
        ListNode cursor = head.next;
        while (cursor != null) {
            System.out.println(cursor);
            cursor = cursor.next;
        }
    }

    public static void reverse(ListNode start) {
        if (start == null || start.next == null) return;
        
        ListNode current = start.next;
        ListNode reversedHead = new ListNode(0, "", "");
        
        while (current != null) {
            ListNode nextTemp = current.next;
            current.next = reversedHead.next;
            reversedHead.next = current;
            current = nextTemp;
        }
        start.next = reversedHead.next;
    }
}

双方向リストの特徴と操作

双方向リストは前後両方向へのトラバースが可能で、自己削除が実現できる点が単方向リストとの大きな違いです。

class BidirectionalList {
    private DListNode root = new DListNode(0, "", "");

    static class DListNode {
        int id;
        String label;
        String codename;
        DListNode next;
        DListNode prev;

        DListNode(int id, String label, String codename) {
            this.id = id;
            this.label = label;
            this.codename = codename;
        }

        @Override
        public String toString() {
            return "DNode{id=" + id + ", label='" + label + "', codename='" + codename + "'}";
        }
    }

    public void insert(DListNode node) {
        DListNode tail = root;
        while (tail.next != null) tail = tail.next;
        tail.next = node;
        node.prev = tail;
    }

    public void erase(int targetId) {
        DListNode cursor = root.next;
        while (cursor != null && cursor.id != targetId) cursor = cursor.next;
        if (cursor != null) {
            cursor.prev.next = cursor.next;
            if (cursor.next != null) cursor.next.prev = cursor.prev;
        } else {
            System.out.printf("削除対象 ID %d が見つかりません%n", targetId);
        }
    }
}

循環リストとヨセフス問題

n人が円形に並び、k番目からm人ごとに除外していくという古典的アルゴリズム問題。循環リストで自然にモデル化できる。

実装のポイント:

  • 最後のノードが最初のノードを指すように接続
  • カウンタを用いてmステップごとにノードを削除
  • 削除後は次のノードから再カウントを開始

タグ: Java LinkedList DataStructure

5月20日 23:19 投稿