コンテンツにスキップ

連結リスト

出典: フリー百科事典『ウィキペディア(Wikipedia)』

: Linked list1使

12

LISP  Scheme Prolog

[]


19551956Cliff Shaw Information Processing Language (IPL) IPL General Problem Solver 使19572 "Proceedings of the Western Joint Computer Conference"  Shaw  "Programming the Logic Theory Machine" 使1975

 (MIT)  Victor Yngve  COMIT 使1958"Mechanical Translation"  "A programming language for mechanical translation" 

1960 MIT LISP "list processor" "Communications of the ACM"  "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I" [1]LISP [2]

1960MIT  Bert Green 19613"IRE Transactions on Human Factors in Electronics"  "Computer languages for symbol manipulation" 使19644Communications of the ACM  Bobrow  Raphael  "A Comparison of list-processing computer languages" 

Technical Systems Consultants (TSC)  FLEX辿FLEX  Smoke Signal Broadcasting使

IBM System/360System/370TSS使 UNIX  "flea" 

[]

[]


"O(1)"O(n)"O(n)"

[]


singly-linked list1Null

3つの整数値を格納した片方向リスト

双方向リスト[編集]

双方向リスト(doubly-linked list)は、より洗練された連結リスト。各ノードには2つのリンクがあり、1つが次のノード(前方リンク)、もう1つが後ろのノード(後方リンク)を指す。リストの先頭のノードには後ろのノードがないので、後方リンクにはヌル値を格納するか、空のリストを指す。リストの最後尾のノードには次のノードがないので、前方リンクにはヌル値を格納するか、空のリストを指す。


3つの整数値を格納した双方向リスト

XOR使21使

[]


circularly-linked list辿使1使便



3つの整数値を格納した循環リスト

片方向循環リスト[編集]


singly-circularly-linked list1辿 [3]

[]


doubly-circularly-linked list21使[3]

[]


; sentinel nodeLISPnil nil  CAR  CDR  nil  nil improper使

[]


使

LISPLISP 使

使association list2使

[]



連結リストと配列[編集]

配列 連結リスト
検索 O(1) O(n)
最後尾での挿入/削除 O(1) O(1) or O(n)[4]
途中での挿入/削除(位置指定あり) O(n) O(1)
永続性 なし 片方向で有り
局所性



1使

辿

1

Unrolled linked list CDR

1 nn  n1使 n辿n 

[]


1使

[]


1

[]


使使

[]


使 "null" 使

[]

[]


2List  "firstNode"  "null"
 record Node {
    data // ノードに格納されるデータ
    next // 次のノードへの参照(最後尾の場合は null)
 }
 record List {
     Node firstNode   // リストの先頭ノードを指す(空のリストの場合は null)
 }

辿 "next" 辿
 node := list.firstNode
 while node ≠ null {
     (node.data に何かをする)
     node := node.next
 }


 function insertAfter(Node node, Node newNode) { // newNode を node の次に挿入
     newNode.next := node.next
     node.next    := newNode
 }

firstNode 
 function insertBeginning(List list, Node newNode) { // 現在の先頭ノードの前にノードを挿入
     newNode.next   := list.firstNode
     list.firstNode := newNode
 }


 function removeAfter(Node node) { // このノードの次のノードを削除
     obsoleteNode := node.next
     node.next := node.next.next
     destroy obsoleteNode
 }
 function removeBeginning(List list) { // 先頭ノードを削除
     obsoleteNode := list.firstNode
     list.firstNode := list.firstNode.next          // 削除されるノードの次を指す
     destroy obsoleteNode
 }

removeBeginning() "list.firstNode"  "null" 

"insertBefore"  "removeBefore" 

22  LISP  append 使

"insertBeginning()"  "removeBeginning()"  "list.firstNode.next" 

[]


 "prev"   "lastNode" "list.firstNode"  "list.lastNode"  "null" 
 record Node {
    data // ノードに格納されるデータ
    next // 次のノードへの参照(最後尾の場合は null)
    prev // 前のノードへの参照(先頭の場合は null)
 }
 record List {
     Node firstNode   // リストの先頭ノードを指す(空のリストの場合は null)
     Node lastNode    // リストの最後尾ノードを指す(空のリストの場合は null)
 }




 node := list.firstNode
 while node ≠ null
     <node.data に対して何か行う>
     node := node.next


 node := list.lastNode
 while node ≠ null
     <node.data に対して何か行う>
     node := node.prev


 function insertAfter(List list, Node node, Node newNode)
     newNode.prev := node
     newNode.next := node.next
     if node.next = null
         list.lastNode := newNode
     else
         node.next.prev := newNode
     node.next := newNode
 function insertBefore(List list, Node node, Node newNode)
     newNode.prev := node.prev
     newNode.next := node
     if node.prev is null
         list.firstNode := newNode
     else
         node.prev.next := newNode
     node.prev    := newNode


 function insertBeginning(List list, Node newNode)
     if list.firstNode = null
         list.firstNode := newNode
         list.lastNode  := newNode
         newNode.prev := null
         newNode.next := null
     else
         insertBefore(list, list.firstNode, newNode)


 function insertEnd(List list, Node newNode)
     if list.lastNode = null
         insertBeginning(list, newNode)
     else
         insertAfter(list, list.lastNode, newNode)

firstNode  lastNode 
 function remove(List list, Node node)
   if node.prev = null
       list.firstNode := node.next
   else
       node.prev.next := node.next
   if node.next = null
       list.lastNode := node.prev
   else
       node.next.prev := node.prev
   destroy node

1"firstNode"  "lastNode"  "null" "removeBefore"  "removeAfter"  "remove(node.prev)"  "remove(node.next)" 

[]


"null" 使

"firstNode"  "lastNode"  "lastNode"  "null" 

[]


someNode  "someNode" 


 node := someNode
 do
     node.value について何か行う
     node := node.next
 while node ≠ someNode


 node := someNode
 do
     node.value について何か行う
     node := node.prev
 while node ≠ someNode

 "someNode" 1


 function insertAfter(Node node, Node newNode)
     newNode.next := node.next
     newNode.prev := node
     node.next.prev := newNode
     node.next      := newNode

"insertBefore"  "insertAfter(node.prev, newNode)" 
 function insertEnd(List list, Node node)
     if list.lastNode = null
         node.prev := node
         node.next := node
     else
         insertAfter(list.lastNode, node)
     list.lastNode := node

"insertAfter(list.lastNode, node)" 
 function remove(List list, Node node)
     if node.next = node
         list.lastNode := null
     else
         node.next.prev := node.prev
         node.prev.next := node.next
         if node = list.lastNode
             list.lastNode := node.prev;
     destroy node

"removeAfter"  "removeBefore"  "remove(list, node.prev)"  "remove(list, node.next)" 

使[]


使使使

使
 record Entry {
    integer next; // 次のエントリの配列インデックス
    integer prev; // 前のエントリ(双方向の場合)
    string name;
    real balance;
 }


integer listHead;
Entry Records[1000];

 Next  Prev 
インデックス Next Prev Name Balance
0 1 4 Jones, John 123.45
1 -1 0 Smith, Joseph 234.56
2 (listHead) 4 -1 Adams, Adam 0.00
3 Ignore, Ignatius 999.99
4 0 2 Another, Anita 876.54
5
6
7

ListHead 2357ListFree 使

辿name  balance 
i := listHead;
while i >= 0 { // リスト上をループする
     print i, Records[i].name, Records[i].balance // エントリの印字
     i = Records[i].next;
}



使













使

O(n)

使


[]


LISPSchemePrologconscons "car"  "cdr" "car" "cdr" cons使

使



C++STL - std::list<T><list>

Java - java.util.LinkedList

.NET - System.Collections.Generic.LinkedList<T>

C
#include <stdio.h>   /* for printf */
#include <stdlib.h>  /* for malloc */

typedef struct ns {
	int data;
	struct ns *next;
} node;

node *list_add(node **p, int i) { /* add head */
    node *n = malloc(sizeof(node)); /* you normally don't cast a return value for malloc */
    n->next = *p;
    *p = n;
    n->data = i;
    return n;
}

node *list_add_tail(node **p, int i) { /* add tail */ 
    node *n;
    node *ptr;
    if (*p == NULL) {
        n = list_add(p, i);
    } else {
        ptr = *p;
        while (ptr->next != NULL) {
            ptr = ptr->next;
        }
        n = malloc(sizeof(node));
        n->next = NULL;
        n->data = i;
        ptr->next = n;
    }
    return n;
}

void list_remove(node **p) { /* remove head */
    if (*p != NULL) {
        node *n = *p;
	*p = (*p)->next;
	free(n);
    }
}

void list_remove_tail(node **p) { /* remove tail */
    if (*p != NULL) {	
        if( (*p)->next == NULL ) {
            list_remove(p);
        } else {
            node *ptr = *p;	
            node *pre;
            while (ptr->next != NULL) {
                pre = ptr;
                ptr = ptr->next;
            }
            pre->next = NULL;
            free(ptr);
        }
    }
}

void list_remove_all(node **p) { /* remove all node */
    while (*p != NULL) {
        list_remove(p);
    }
}

node **list_search(node **n, int i) {
    while (*n != NULL) {
        if ((*n)->data == i) {
            return n;
        }
        n = &(*n)->next;
    }
    return NULL;
}

void list_print(node *n) {
    if (n == NULL) {
        printf("list is empty\n");
    }
    while (n != NULL) {
        printf("print %p %p %d\n", n, n->next, n->data);
        n = n->next;
    }
}

int main(void) {
    node *n = NULL;

    list_add(&n, 0); /* list: 0 */
    list_add(&n, 1); /* list: 1 0 */
    list_add(&n, 2); /* list: 2 1 0 */
    list_add(&n, 3); /* list: 3 2 1 0 */
    list_add(&n, 4); /* list: 4 3 2 1 0 */
    list_print(n);
    list_remove(&n);            /* remove first (4) */
    list_remove(&n->next);      /* remove new second (2) */
    list_remove(list_search(&n, 1)); /* remove cell containing 1 (first) */
    list_remove(&n->next);      /* remove second to last node (0) */
    list_remove(&n);            /* remove last (3) */
    list_print(n);

    return 0;
}

内部収納と外部収納[編集]


; internal storage[]; external storage[]使



1

 "next"  "prev" 使使

[]



 record member { // 家族のメンバー
     member next
     string firstName
     integer age
 }
 record family { // 家族
     family next
     string lastName
     string address
     member members // この家族のメンバーのリストの先頭
 }


 aFamily := Families // 家族リストの先頭から開始
 while aFamily ≠ null { // 家族リスト上でループ
     家族に関する情報を印字
     aMember := aFamily.members // その家族のメンバーのリストの先頭を得る
     while aMember ≠ null { // メンバーのリスト上でループ
         メンバーに関する情報を印字
         aMember := aMember.next
     }
     aFamily := aFamily.next
 }

使
 record node { // 汎用リンク構造体
     node next
     pointer data // ノードに付属するデータを指す汎用ポインタ
 }
 record member { // 家族構成員に関する構造体
     string firstName
     integer age
 }
 record family { // 家族に関する構造体
     string lastName
     string address
     node members // この家族のメンバーのリストの先頭
 }


 famNode := Families // 家族リストの先頭から開始
 while famNode ≠ null { // 家族リスト上でループ
     aFamily = (family)famNode.data // ノードから家族データ構造体を取り出す
     家族に関する情報を印字
     memNode := aFamily.members // その家族のメンバーのリストの先頭を得る
     while memNode ≠ null { // メンバーのリスト上でループ
         aMember := (member)memNode.data // ノードからメンバーデータ構造体を取り出す
         メンバーに関する情報を印字
         memNode := memNode.next
     }
     famNode := famNode.next
 }

使2node使

1

[]


 O(n) 

 "Move To Front" 1

1使1

[]


使



2

unrolled linked list 使

使

[]


Linked List Patented in 2006 (Slashdot)

2006 ( )

脚注[編集]

  1. ^ RECURSIVE FUNCTIONS OF SYMBOLIC EXPRESSIONS AND THEIR COMPUTATION BY MACHINE (Part I) (12-May-1998)”. www-formal.stanford.edu. 2024年4月7日閲覧。
  2. ^ McCarthy et al. LISP I Programmer's Manual. — Software Preservation Group”. softwarepreservation.org. 2024年4月7日閲覧。
  3. ^ a b Preiss, Bruno R. (1999年), Data Structures and Algorithms with Object-Oriented Design Patterns in Java, Wiley, p. page 97, 165, ISBN 0471-34613-6, http://www.brpreiss.com/books/opus5/html/page97.html 
  4. ^ 最後尾へのリンクを保持していれば O(1) だが、最後尾を探すために先頭から辿る必要がある場合は O(n)

参考文献[編集]

  • Antonakos, James L. and Mansfield, Kenneth C., Jr. Practical Data Structures Using C/C++ (1999). Prentice-Hall. ISBN 0-13-280843-9, pp. 165–190
  • Collins, William J. Data Structures and the Java Collections Framework (2002,2005) New York, NY: McGraw Hill. ISBN 0-07-282379-8, pp. 239–303
  • Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford Introductions to Algorithms (2003). MIT Press. ISBN 0-262-03293-7, pp. 205–213, 501–505
  • Green, Bert F. Jr. (1961). Computer Languages for Symbol Manipulation. IRE Transactions on Human Factors in Electronics. 2 pp. 3-8.
  • McCarthy, John (1960). Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I. Communications of the ACM. [1] HTML DVI PDF PostScript
  • Donald Knuth. Fundamental Algorithms, Third Edition. Addison-Wesley, 1997. ISBN 0-201-89683-4. Sections 2.2.3–2.2.5, pp.254–298.
  • Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Section 10.2: Linked lists, pp.204–209.
  • Newell, Allen and Shaw, F. C. (1957). Programming the Logic Theory Machine. Proceedings of the Western Joint Computer Conference. pp. 230-240.
  • Parlante, Nick (2001). Linked list basics. Stanford University. PDF
  • Sedgewick, Robert Algorithms in C (1998). Addison Wesley. ISBN 0-201-31452-5, pp. 90–109
  • Shaffer, Clifford A. A Practical Introduction to Data Structures and Algorithm Analysis (1998). NJ: Prentice Hall. ISBN 0-13-660911-2, pp. 77–102
  • Wilkes, Maurice Vincent (1964). An Experiment with a Self-compiling Compiler for a Simple List-Processing Language. Annual Review in Automatic Programming 4, 1. Published by Pergamon Press.
  • Wilkes, Maurice Vincent (1964). Lists and Why They are Useful. Proceeds of the ACM National Conference, Philadelphia 1964 (ACM Publication P-64 page F1-1); Also Computer Journal 7, 278 (1965).
  • Kulesh Shanmugasundaram (2005年4月4日). Linux Kernel Linked List Explained.

関連項目[編集]

外部リンク[編集]