サーチ…


備考

C言語はリンクリストデータ構造を定義していません。 Cを使用していてリンクリストが必要な場合は、既存のライブラリ(GLibなど)のリンクリストを使用するか、独自のリンクリストインターフェイスを作成する必要があります。このトピックでは、リンクリストとリンクリストを作成するための出発点として使用できるリンクリストと二重リンクリストの例を示します。

単独リンクリスト

リストには、nextという1つのリンクで構成されるノードが含まれています。

データ構造

struct singly_node
{
  struct singly_node * next;
};

二重リンクリスト

このリストには、前と次の2つのリンクで構成されるノードが含まれています。リンクは、通常、同じ構造を持つノードを参照しています。

データ構造

struct doubly_node
{
  struct doubly_node * prev;
  struct doubly_node * next;
};

Topoliges

リニアまたはオープン

ここに画像の説明を入力

円形またはリング

ここに画像の説明を入力

手続き

バインド

2つのノードを結合する。 ここに画像の説明を入力

void doubly_node_bind (struct doubly_node * prev, struct doubly_node * next)
{
  prev->next = next;
  next->prev = prev;
}

循環リンクリストを作る

ここに画像の説明を入力

void doubly_node_make_empty_circularly_list (struct doubly_node * head)
{
  doubly_node_bind (head, head);
}

線形リンクリストを作る

ここに画像の説明を入力

void doubly_node_make_empty_linear_list (struct doubly_node * head, struct doubly_node * tail)
{
  head->prev = NULL;
  tail->next = NULL;
  doubly_node_bind (head, tail);
}

挿入

空リストには常にNULLの代わりに1つのノードが含まれると仮定します。その後、挿入プロシージャはNULLを考慮する必要はありません。

void doubly_node_insert_between
(struct doubly_node * prev, struct doubly_node * next, struct doubly_node * insertion)
{
  doubly_node_bind (prev, insertion);
  doubly_node_bind (insertion, next);
}

void doubly_node_insert_before
(struct doubly_node * tail, struct doubly_node * insertion)
{
  doubly_node_insert_between (tail->prev, tail, insertion);
}

void doubly_node_insert_after
(struct doubly_node * head, struct doubly_node * insertion)
{
  doubly_node_insert_between (head, head->next, insertion);
}

単一リンクリストの先頭にノードを挿入する

以下のコードは数字の入力を求め、リンクリストの先頭にそれらを追加し続けます。

/* This program will demonstrate inserting a node at the beginning of a linked list */

#include <stdio.h>
#include <stdlib.h>

struct Node {
  int data;
  struct Node* next;
};


void insert_node (struct Node **head, int nodeValue);
void print_list (struct Node *head);

int main(int argc, char *argv[]) {
  struct Node* headNode;
  headNode = NULL; /* Initialize our first node pointer to be NULL. */
  size_t listSize, i;
  do {
    printf("How many numbers would you like to input?\n");
  } while(1 != scanf("%zu", &listSize));

  for (i = 0; i < listSize; i++) {
    int numToAdd;
    do {
      printf("Enter a number:\n");
    } while (1 != scanf("%d", &numToAdd));

    insert_node (&headNode, numToAdd);
    printf("Current list after your inserted node: \n");
    print_list(headNode);
  }

  return 0;
}

void print_list (struct Node *head) {
  struct node* currentNode = head;

  /* Iterate through each link. */
  while (currentNode != NULL) {
      printf("Value: %d\n", currentNode->data);
      currentNode = currentNode -> next;
  }
}

void insert_node (struct Node **head, int nodeValue) {
  struct Node *currentNode = malloc(sizeof *currentNode);
  currentNode->data = nodeValue;
  currentNode->next = (*head);

  *head = currentNode;
}

ノードの挿入の説明

最初にノードを追加する方法を理解するために、可能なシナリオを見てみましょう:

  1. リストは空ですので、新しいノードを追加する必要があります。この場合、 HEADは最初のノードへのポインタで、次のようになります。
| HEAD | --> NULL

line currentNode->next = *headNode;値が割り当てられますcurrentNode->nextであることがNULLため、 headNode元々の値から始まりNULL

今、頭のノードのポインタを現在のノードを指すように設定します。

  -----      -------------
 |HEAD | --> |CURRENTNODE| --> NULL /* The head node points to the current node */
  -----      -------------

これは*headNode = currentNode;行われ*headNode = currentNode;

  1. リストにはすでに値が設定されています。最初に新しいノードを追加する必要があります。簡単にするために、まず1つのノードから始めましょう:
 -----    -----------
 HEAD --> FIRST NODE --> NULL
 -----    -----------

currentNode->next = *headNode場合、データ構造は次のようになります。

 ---------        -----    ---------------------
 currentNode -->  HEAD --> POINTER TO FIRST NODE --> NULL
 ---------        -----    ---------------------

これは、以来、明らかに変更する必要があります*headNodeを指している必要がありcurrentNode

 ----    -----------    ---------------
 HEAD -> currentNode -->     NODE       -> NULL
 ----    -----------    ---------------

これは*headNode = currentNode;行われ*headNode = currentNode;

n番目のノードにノードを挿入する

これまでは、単一リンクリストの先頭にノードを挿入する方法を検討しました 。しかし、ほとんどの場合、他の場所にもノードを挿入できるようにしたいと思うでしょう。次のコードは、リンクされたリストのどこにでもノードを挿入するためのinsert()関数の記述方法を示しています。

#include <stdio.h>
#include <stdlib.h>

struct Node {
  int data;
  struct Node* next;
};

struct Node* insert(struct Node* head, int value, size_t position);
void print_list (struct Node* head);

int main(int argc, char *argv[]) {
  struct Node *head = NULL; /* Initialize the list to be empty */

  /* Insert nodes at positions with values: */
  head = insert(head, 1, 0);
  head = insert(head, 100, 1);
  head = insert(head, 21, 2);
  head = insert(head, 2, 3);
  head = insert(head, 5, 4);
  head = insert(head, 42, 2);

  print_list(head);
  return 0;
}

struct Node* insert(struct Node* head, int value, size_t position) {
  size_t i = 0;
  struct Node *currentNode;

  /* Create our node */
  currentNode = malloc(sizeof *currentNode);
  /* Check for success of malloc() here! */

  /* Assign data */
  currentNode->data = value;

  /* Holds a pointer to the 'next' field that we have to link to the new node.
     By initializing it to &head we handle the case of insertion at the beginning. */
  struct Node **nextForPosition = &head;
  /* Iterate to get the 'next' field we are looking for.
     Note: Insert at the end if position is larger than current number of elements. */
  for (i = 0; i < position && *nextForPosition != NULL; i++) {
      /* nextForPosition is pointing to the 'next' field of the node.
         So *nextForPosition is a pointer to the next node.
         Update it with a pointer to the 'next' field of the next node. */
      nextForPosition = &(*nextForPosition)->next;
  }

  /* Here, we are taking the link to the next node (the one our newly inserted node should
  point to) by dereferencing nextForPosition, which points to the 'next' field of the node
  that is in the position we want to insert our node at.
  We assign this link to our next value. */
  currentNode->next = *nextForPosition;

  /* Now, we want to correct the link of the node before the position of our
  new node: it will be changed to be a pointer to our new node. */
  *nextForPosition = currentNode;

  return head;
}

void print_list (struct Node* head) {
  /* Go through the list of nodes and print out the data in each node */
  struct Node* i = head;
  while (i != NULL) {
    printf("%d\n", i->data);
    i = i->next;
  }
}

リンクされたリストを逆転する

このタスクを再帰的に実行することもできますが、この例では反復アプローチを使用することを選択しました。このタスクは、リンクされたリストの先頭にすべてのノードを挿入する場合に便利です。次に例を示します。

#include <stdio.h>
#include <stdlib.h>

#define NUM_ITEMS 10

struct Node {
  int data;
  struct Node *next;
};

void insert_node(struct Node **headNode, int nodeValue, int position);
void print_list(struct Node *headNode);
void reverse_list(struct Node **headNode);


int main(void) {
  int i;
  struct Node *head = NULL;

  for(i = 1; i <= NUM_ITEMS; i++) {
    insert_node(&head, i, i);
  }
  print_list(head);

  printf("I will now reverse the linked list\n");
  reverse_list(&head);
  print_list(head);
  return 0;
}

void print_list(struct Node *headNode) {
  struct Node *iterator;

  for(iterator = headNode; iterator != NULL; iterator = iterator->next) {
    printf("Value: %d\n", iterator->data);
  }
}

void insert_node(struct Node **headNode, int nodeValue, int position) {
  int i;
  struct Node *currentNode = (struct Node *)malloc(sizeof(struct Node));
  struct Node *nodeBeforePosition = *headNode;

  currentNode->data = nodeValue;

  if(position == 1) {
    currentNode->next = *headNode;
    *headNode = currentNode;
    return;
  }

  for (i = 0; i < position - 2; i++) {
    nodeBeforePosition = nodeBeforePosition->next;
  }

  currentNode->next = nodeBeforePosition->next;
  nodeBeforePosition->next = currentNode;
}

void reverse_list(struct Node **headNode) {
  struct Node *iterator = *headNode;
  struct Node *previousNode = NULL;
  struct Node *nextNode = NULL;

  while (iterator != NULL) {
    nextNode = iterator->next;
    iterator->next = previousNode;
    previousNode = iterator;
    iterator = nextNode;
  }

  /* Iterator will be NULL by the end, so the last node will be stored in
  previousNode.  We will set the last node to be the headNode */
  *headNode = previousNode;
}

リバースリストメソッドの説明

私たちは、開始previousNode通りにNULL我々は、ループの最初の反復に知っているので、我々は最初のヘッドノードの前にノードを探しているなら、それはなりますNULL 。最初のノードはリストの最後のノードになり、次の変数は当然NULLになりNULL

基本的に、ここでリンクされたリストを逆転させるという概念は、リンク自体を実際に逆転させることです。各ノードの次のメンバーは、その前のノードになります。

Head -> 1 -> 2 -> 3 -> 4 -> 5

各数字はノードを表します。このリストは次のようになります。

1 <- 2 <- 3 <- 4 <- 5 <- Head

最後に、頭部は代わりに5番目のノードを指し、各ノードはその前のノードを指していなければなりません。

その前に何もなかったので、ノード1はNULLを指すべきです。ノード2はノード1を指し、ノード3はノード2を指すはずである。

しかし、この方法には小さな問題1つあります。次のノードへのリンクを切断してそれを前のノードに変更すると、リンク先のノードがリスト内の次のノードに移動することはできなくなります。

この問題を解決するには、リンクを変更する前に変数( nextNode )に次の要素を格納するだけです。

二重リンクリスト

二重リンクリストにノードを挿入する方法、リストを簡単に元に戻す方法、逆に印刷する方法を示すコードの例

#include <stdio.h>
#include <stdlib.h>

/* This data is not always stored in a structure, but it is sometimes for ease of use */
struct Node {
  /* Sometimes a key is also stored and used in the functions */
  int data;
  struct Node* next;
  struct Node* previous;
};

void insert_at_beginning(struct Node **pheadNode, int value);
void insert_at_end(struct Node **pheadNode, int value);

void print_list(struct Node *headNode);
void print_list_backwards(struct Node *headNode);

void free_list(struct Node *headNode);

int main(void) {
  /* Sometimes in a doubly linked list the last node is also stored */
  struct Node *head = NULL;

  printf("Insert a node at the beginning of the list.\n");
  insert_at_beginning(&head, 5);
  print_list(head);

  printf("Insert a node at the beginning, and then print the list backwards\n");
  insert_at_beginning(&head, 10);
  print_list_backwards(head);

  printf("Insert a node at the end, and then print the list forwards.\n");

  insert_at_end(&head, 15);
  print_list(head);

  free_list(head);

  return 0;
}

void print_list_backwards(struct Node *headNode) {
  if (NULL == headNode)
  {
    return;
  }
  /*
  Iterate through the list, and once we get to the end, iterate backwards to print 
  out the items in reverse order (this is done with the pointer to the previous node).
  This can be done even more easily if a pointer to the last node is stored.
  */
  struct Node *i = headNode;
  while (i->next != NULL) {
    i = i->next; /* Move to the end of the list */
  }

  while (i != NULL) {
    printf("Value: %d\n", i->data);
    i = i->previous;
  }
}

void print_list(struct Node *headNode) {
  /* Iterate through the list and print out the data member of each node */
  struct Node *i;
  for (i = headNode; i != NULL; i = i->next) {
    printf("Value: %d\n", i->data);
  }
}

void insert_at_beginning(struct Node **pheadNode, int value) {
  struct Node *currentNode;

  if (NULL == pheadNode)
  {
    return;
  }
  /* 
  This is done similarly to how we insert a node at the beginning of a singly linked
  list, instead we set the previous member of the structure as well
  */
  currentNode = malloc(sizeof *currentNode);

  currentNode->next = NULL;
  currentNode->previous = NULL;
  currentNode->data = value;

  if (*pheadNode == NULL) { /* The list is empty */
    *pheadNode = currentNode;
    return;
  }

  currentNode->next = *pheadNode;
  (*pheadNode)->previous = currentNode;
  *pheadNode = currentNode;
}

void insert_at_end(struct Node **pheadNode, int value) {
  struct Node *currentNode;

  if (NULL == pheadNode)
  {
    return;
  }

  /*
  This can, again be done easily by being able to have the previous element.  It 
  would also be even more useful to have a pointer to the last node, which is commonly
  used.
  */
  
  currentNode = malloc(sizeof *currentNode);
  struct Node *i = *pheadNode;

  currentNode->data = value;
  currentNode->next = NULL;
  currentNode->previous = NULL;

  if (*pheadNode == NULL) {
    *pheadNode = currentNode;
    return;
  }

  while (i->next != NULL) { /* Go to the end of the list */
    i = i->next;
  }

  i->next = currentNode;
  currentNode->previous = i;
}

void free_list(struct Node *node) {
  while (node != NULL) {
    struct Node *next = node->next;
    free(node);
    node = next;
  }
}

最後のノードへのポインタを格納すると便利な場合があることに注意してください(リストの最後まで単純にジャンプすることは、最後まで反復処理するよりも効率的です)。

struct Node *lastNode = NULL;

この場合、リストの変更時に更新する必要があります。

キーを使用して要素を識別することもあります。これは単純にNode構造体のメンバです。

struct Node {
  int data;
  int key;
  struct Node* next;
  struct Node* previous;
};

このキーは、要素の削除など、特定の要素に対してタスクが実行されたときに使用されます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow