【データ構造学習記録4】-二重リンクリスト

1.簡単な紹介

二重リンクリストの意味は、次のノードのアドレスに加えて、ノードが前のノードのアドレスを追加で格納する必要があるということです。
このように、ノードの1つのアドレスがわかっていると仮定すると、単一リンクリストの「後方」トラバーサルだけでなく、「前方」と「後方」を簡単にトラバースできます。
したがって、二重リンクリストは単一リンクリストよりも柔軟性があり、トラバースにかかる時間はわずかに短くなりますが、追加のストレージスペースが必要になります。一部の質問は法外で「前進および後退」する可能性があるため、単一リンクリストではこれらの機能を完了できません。二重リンクリストは、非常に典型的な「時間の空間」です。

2.アイデア図

2.1ノード

ノードを作成するには、prev data next3つの部分が含まれている必要があるため、単一リンクリストと同様である必要があります。prev前のノードのアドレスをポイントし、next次のノードのアドレスをポイントします。
ここに画像の説明を挿入

2.2リンクリスト構造

二重リンクリストの構造のため、1つだけを格納することはできず头地址、1つを宣言する必要があり尾地址ます。順方向トラバーサルの場合、トラバーサルが終了する境界は*next = tail;逆方向トラバーサルの場合、トラバーサルが終了する境界は*prev = headです。さらに、スペースを節約するために、カウントを追加できます(リンクリストの長さをカウントします)。index識別子として挿入するとindexリンクリストの長さと比較を通じてトラバース方向取得できます。
もちろん、面倒な場合は、リンクリストを作成する必要はなくnode最初のノードのデータを直接使用して、リンクリストの長さを記録することができます。この記事では、最初の方法を使用します。

ここに画像の説明を挿入

2.3リンクリストの初期化

head *tail len = 03つの部分を含む構造を個別に作成し、初期値を割り当てることをお勧めします。
ここに画像の説明を挿入

2.4リンクリストのトラバーサル

正の順序または逆の順序で出力できますが、トラバーサルの「開始」と「プロセスの方向」が異なります。
次の操作で任意のノードのアドレスを取り出して、このコードを再利用できるように、方向を自動的に決定できるトラバーサル関数を作成することをお勧めします。

2.5リンクリストの挿入

二重リンクリストの挿入は非常に簡単です。必要なのは次のことだけです。

  1. 挿入するノードがNewNodeされる*prev変更NextNode*prev*next変更LastNode*next
  2. その後、変更の合計前のノードをするのアドレス挿入されたノード、および設定は完了です!LastNode*nextprevNewNode
    ここに画像の説明を挿入

2.6リンクリストの削除

その周りの2つのノードを変更する必要があります。

  1. 変更LastNode*next*next
  2. それは変更ので、あなたがそれを通過することができないことを、そのノードが削除されましたNextNode*prev*prev

    ここに画像の説明を挿入

2.7リンクリストの変更/クエリ

この部分はトラバーサルと同等であり、値を変更するだけで済みます。

3.コードの実装

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

#define OK      1
#define ERROR   0

typedef struct dataType{
    
    
    int data1;
    int data2;
}dataType;  // 数据类

typedef struct nodeType{
    
    
    struct nodeType *prev;
    struct nodeType *next;
    dataType data;
}nodeType; // 节点类

typedef struct listType{
    
    
    nodeType *head;
    nodeType *tail;
    int len;
}listType;  // 链表类

dataType DEFAULTDATA={
    
    0,0}; // 创建一个默认的类,作为数据结构

nodeType* NewNode(dataType data);
listType* ListInit(void);
nodeType* ListGetNode(listType *list, int index);
int ListShow(listType *list, int way);
int ListInsert(listType *list, dataType data, int index);
int ListDelete(listType *list, int index);
int ListModify(listType* list, dataType data, int index);
int ListQuery(listType* list, dataType data, int way);

int main()
{
    
    
    listType* list = ListInit();
    dataType test = {
    
    1,2};

    //主函数可以自己修改
    ListInsert(list, test, 0);
    test.data1 = 2;
    ListInsert(list, test, 1);
    test.data1 = 3;
    ListInsert(list,test,2);
    ListShow(list, 0);
    ListModify(list, test, 0);
    ListShow(list, 0);
    printf("i find in %d\n", ListQuery(list, test, 0));
    printf("i find in %d\n", ListQuery(list, test, 1));
    return 0;
}

nodeType* NewNode(dataType data)
{
    
    
    nodeType* node = (nodeType*)malloc(sizeof(nodeType));

    if(node != NULL)
    {
    
    
        node->data = data;  // 把数据的值赋值给节点
        node->prev = NULL;  // 并初始化节点的信息
        node->next = NULL;
        return node;
    }
    exit(1);
}

listType* ListInit(void)
{
    
    
    listType* list = (listType*)malloc(sizeof(listType));

    if (list != NULL)
    {
    
    
        list->head = NewNode(DEFAULTDATA); //   创建两个节点给头尾
        list->tail = NewNode(DEFAULTDATA);
        list->head->next = list->tail;  // 并将头的地址指向尾
        list->tail->prev = list->head;  // 尾的地址指向头
        list->len = 0;  // 初始化长度
        return list;
    }
    exit(1);
}

nodeType* ListGetNode(listType *list, int index)
{
    
    
    nodeType *begin, *end;
    int cont = 0;

    if (list->len == 0)
    {
    
    
        return list->tail; // 如果链表为空,那么就返回尾节点
    }
    else
    {
    
    
        if (index > list->len / 2)  // 通过1/2判断从前还是后遍历
        {
    
    
            begin = list->tail;
            end = list->head;
            cont = list->len;   
            // 因为len刚好比index序号大一,所以尾节点的序号刚好等于len
            // 反之,如果是头节点,那么序号就是 -1
            while(begin != end && cont > index) // 遍历的边界就是链表的末尾,或者cont= index
            {
    
    
                begin = begin->prev;
                --cont; // 遍历节点与更新当前index
            }
            return begin;  // 此时的链表节点,就是index指向的节点,或者表头
        }
        else
        {
    
    
            begin = list->head;
            end = list->tail;
            cont = -1;
            // 头节点的序号就是-1
            while(begin != end && cont < index) // 遍历的边界就是链表末尾或者cont=index
            {
    
    
                begin = begin->next;
                ++cont; // 这部分和上面一样
            }
            return begin;
        }
    }
}

int ListShow(listType *list, int way)
{
    
    
    nodeType* begin;
    nodeType* end;
    int cont = 0;

    if (way == 1)   //way=1时,从后往前遍历,否则都是从前开始遍历
    {
    
    
        begin = list->tail->prev;
        end = list->head;
        cont = list->len - 1;
        // 此时的开始节点是尾节点的前一个,要么是有效的,要么就是头节点
        // 所以开始的节点序号是len-1
        while (begin != end)
        {
    
    
            printf("No.%d is %d %d\n", cont,begin->data.data1, begin->data.data2);
            begin = begin->prev;
            --cont;
        } // 遍历,并输出信息
        printf("----------------\n");
        return OK;
    }
    else
    {
    
    
        begin = list->head->next;
        end = list->tail;
        cont = 0;
        // 从头节点的后一个节点开始,所以序号因该是0
        while (begin != end)
        {
    
    
            printf("No.%d is %d %d\n", cont,begin->data.data1, begin->data.data2);
            begin = begin->next;
            ++cont;
        } // 同理 都是遍历
        printf("----------------\n");
        return OK;
    }
}


int ListInsert(listType *list, dataType data, int index)
{
    
    
    nodeType *newNode = NewNode(data);  // 先创建一个节点
    nodeType *nextNode = ListGetNode(list, index);  // 获得index节点的地址 
    nodeType *lastNode = nextNode->prev;    // 插入节点前的节点地址

    if (nextNode->prev != NULL)
    {
    
    
        newNode->prev = nextNode->prev;     // 修改插入节点的信息
        newNode->next = nextNode;
        lastNode->next = newNode;   // 更新插入节点的前节点信息
        nextNode->prev = newNode;   // 更新插入节点的后节点信息
        list->len += 1; // 一定不要忘记把长度加1
        return OK;
    }
    else
    {
    
    
        return ERROR;
    }

}

int ListDelete(listType* list, int index)
{
    
    
    nodeType *dNode;
    nodeType *nextNode;
    nodeType *lastNode;
    if (list->len == 0)
    {
    
    
        return ERROR;
    }
    else
    {
    
    
        dNode = ListGetNode(list, index);   // 找到要删除的节点
        if (dNode->next == NULL || dNode->prev == NULL)
        {
    
    
            return ERROR;   // 确保不是头节点或者尾节点
        }
        nextNode = dNode->next; //  更新前后节点的信息
        lastNode = dNode->prev;
        lastNode->next = nextNode;
        nextNode->prev = lastNode;
        list->len -= 1; // 一定不要忘记要修改链表的长度
        return OK;
    }
}

int ListModify(listType* list, dataType data, int index)
{
    
    
    nodeType *thisNode = ListGetNode(list, index);  //查找节点

    if (thisNode->next != NULL || thisNode->prev != NULL) // 确保不是首尾节点
    {
    
    
        thisNode->data = data;
        return OK;
    }
    else
    {
    
    
        return ERROR;
    }
}

int ListQuery(listType* list, dataType data, int way)
{
    
    
    nodeType *begin;
    nodeType *end;
    int cont = 0;
    
    if (way == 0)
    {
    
    
        begin = list->head->next; // 创建开始与结束节点
        end = list->tail;
        cont = 0;   // index计数
        while(begin != end)
        {
    
    
            if (begin->data.data1 == data.data1 && begin->data.data2 == data.data2)
            {
    
    
                return cont;    //找到了
            }
            else
            {
    
    
                ++cont; // 没找到,更新节点信息
                begin = begin->next;
            }
        }
        return -1;  // 没找到
    }
    else
    {
    
    
        begin = list->tail->prev;
        end = list->head;
        cont = list->len - 1;
        while(begin != end)
        {
    
    
            if (begin->data.data1 == data.data1 && begin->data.data2 == data.data2)
            {
    
    
                return cont;
            }
            else
            {
    
    
                --cont;
                begin = begin->prev;
            }
        }
        return -1;  // 反之同上,只是顺序变化了
    }
}

おすすめ

転載: blog.csdn.net/u011017694/article/details/109318559