Data Structure Series Learning (4) - Circular Linked List

Table of contents

introduction:

study:

Code:

Header file (Circular_Linked_List):

Structure design:

Function function declaration: 

code:

Source file (Circular_Linked_List):

Initialization function (Init_clist):

Empty judgment function (IsEmpty):

Find function (Search):

Get effective length function (Get_length):

Clear function (Clear):

Destroy function (Destroy): 

Print function (Show): 

Head insertion function (Insert_head):

Tail insertion function (Insert_tail):

Insert function by position (Insert_pos): 

Head delete function (Delete_head): 

Tail delete function (Delete_tail): 

Delete function by position (Delete_pos):

Delete function by value (Delete_val):

test: 

Test initialization functions, positional interpolation functions, and printing functions:

Test header plug-in function:

 Test tail interpolation function:

Test the positional interpolation function: 

Number of test head deletions: 

Test tail delete function:

Test the delete function by position: 

Test the delete-by-value function:

Test destroy function:

Test lookup function:

Summarize:

think:


introduction:

Data structure learning directory:

Data Structure Series Learning (1) - An Introduction to Data Structure

Data structure series learning (2) - sequence table (Contiguous_List) 

Data Structure Series Learning (3) - Singly Linked List (Linked_List)

In the previous article, we learned and used C language to realize the single-linked list without the head node. In this article, we will implement another chained storage structure - the one-way circular linked list in the circular linked list. Learn and implement with code.

study:

As another storage structure of linked storage - circular linked list, we should first be clear about the difference between a one-way circular linked list and an ordinary single linked list?

We use two diagrams to distinguish these two storage structures:

The structure of a single linked list without a head node is shown in the figure below. No data is stored in the data field of the head node of the linked list, and the address of the next node is saved in the pointer field, and so on. Each node saves the address of the next node and saves data. Until the last node pointer field is empty.

The structure of the one-way circular linked list is shown in the figure below. The data field in the head node does not store data, and each node stores the address of the next node. Unlike the singly linked list without the head node, the last node in the circular linked list stores What is the address of the head node, the entire linked list forms a ring structure.

 

We have roughly understood the structural distinction between the one-way circular linked list and the single-linked list, so what is the significance of the existence of the circular linked list?

Now that a ring structure is formed, we can start from any node in the linked list and traverse to all nodes in the entire linked list.

Of course, if the structure of the linked list changes, the corresponding functions will also change randomly. Because the pointer field of the end node in the one-way circular linked list is no longer empty, but to store the address of the head node, so when we are initializing the linked list, inserting data at the end, inserting data by position, deleting data at the head, and When deleting data, deleting data by position, deleting data by value, printing data, etc., the implementation of these functions should be modified accordingly.

Code:

Header file (Circular_Linked_List):

Structure design:

The structure design of the one-way circular linked list is the same as that of the single-linked list. We rename the type of int to Elem_type, define a structure variable named CNode, and its members are the data field of Elem_type type and the next of the structure pointer type. area.

typedef int Elem_type;//范型定义
typedef struct CNode
{
    Elem_type data;
    struct CNode *next;
}CNode, *PCnode;//结构体别名

Function function declaration: 

We need to implement the function in a one-way circular linked list:

initialization function (Init_clist);

Empty judgment function (IsEmpty);

Find function (Search);

Get the effective length (Get_length);

Clear function (Clear);

Destroy function (Destroy);

Destroy function (Destroy1);

print function (Show);

Head insert function (Insert_head);

Tail insertion function (Insert_tail);

Interpolate function by position (Insert_pos);

Head delete function (Delete_head);

Tail delete function (Delete_tail);

Delete function by position (Delete_pos);

Delete function by value (Delete_val);

code:

void Init_clist(PCnode cplist);
bool IsEmpty(PCnode cplist);
PCnode Search(PCnode cplist,Elem_type val);
int Get_length(PCnode cplist);
bool Clear(PCnode cplist);
bool Destroy(PCnode cplist);//无限头删
bool Destroy2(PCnode cplist);//不借助头节点,有两个辅助指针
void Show(PCnode cplist);
bool Insert_head(PCnode cplist,Elem_type val);
bool Insert_tail(PCnode cplist,Elem_type val);
bool Insert_pos(PCnode cplist,int pos,Elem_type val);
bool Delete_head(PCnode cplist);
bool Delete_tail(PCnode cplist);
bool Delete_pos(PCnode cplist,int pos);
bool Delete_val(PCnode cplist,Elem_type val);

Source file (Circular_Linked_List):

Initialization function (Init_clist):

When we introduced the one-way circular linked list, we said that the end node of the one-way circular linked list stores the address of the head node, so when we initialize, the next field of the initial node actually stores the address of the initial node itself.

void Init_clist(PCnode cplist)
{
    //问题:如果没有一个有效节点,那么头节点的next域应该指向谁? 答:指向自己
    assert(cplist != nullptr);
    cplist->next = cplist;
}

Empty judgment function (IsEmpty):

If the one-way circular linked list is empty, it means that no valid data is saved in the linked list, that is to say, only the head node exists, and there is no data in the head node. We directly set the return value as the address of the head node to save It can be in the next field of the head node.

bool IsEmpty(PCnode cplist)
{
    return cplist->next == cplist;
}

Find function (Search):

We set the return value of the lookup function as the address of the value to be looked up. The first thing we need to do is to perform a null operation on the linked list. If the linked list is empty, then we directly set the return value to the empty address. Newly define a pointer variable p of structure type to point to the first valid node after the head node. The termination condition of the loop is that p points to the head node (because the head node does not store data), if the value to be searched is equal to the data of the node pointed to by p The value stored in the field returns the address of the node. If not found, an empty address is returned.

PCnode Search(PCnode cplist,Elem_type val)
{
    assert(cplist != nullptr);
    if(IsEmpty(cplist)){
        return nullptr;
    }
    PCnode p = cplist->next;
    for(;p != cplist;p = p->next){
        if(val == p->data){
            return p;
        }
    }
    return nullptr;
}

Get effective length function (Get_length):

Define a pointer variable p of structure type to point to the first valid node after the head node, and then define an integer value count to count the number of valid nodes, define a loop, the termination condition of the loop is not pointing to the head node, and loop every Add 1 to the value of count once executed, and set the final return value to count.

int Get_length(PCnode cplist)
{
    assert(cplist != nullptr);
    PCnode p = cplist->next;
    int count = 0;
    for(;p != cplist;p = p->next){
        count++;
    }
    return count;
}

Clear function (Clear):

Empty and destroy in the linked list are similar, so we can directly call the destroy function in the clear function.

bool Clear(PCnode cplist)
{
    Destroy(cplist);
    return true;
}

Destroy function (Destroy): 

When cp is not empty, execute the head delete function in a loop until the linked list becomes completely empty.

bool Destroy(PCnode cplist)//无限头删
{
    while(!IsEmpty(cplist)){
        Delete_head(cplist);
    }
    return true;
}

Print function (Show): 

Define the pointer variable p of the structure type to point to the first valid node after the head node, define a loop about the pointer p, the loop termination condition is not pointing to the head node, and print the data field of the node pointed to by p every time the loop is executed. until the loop is complete.

void Show(PCnode cplist)
{
    //判定:使用不需要前驱的for循环
    assert(cplist != nullptr);
    PCnode p = cplist->next;
    for(;p != cplist;p = p->next){
        printf("%5d",p->data);
    }
    printf("\n");
}

Head insertion function (Insert_head):

Apply for a new node in the heap area through the malloc function to allocate memory space for the new node, assign the value to be inserted to the data field of the new node, then replace the next field, and assign the address of the first valid node after the original head node to In the next field of pnewnode, replace the address of the first valid node stored in the original head node with the address of pnewnode, as shown in the figure:

 

bool Insert_head(PCnode cplist,Elem_type val)
{
    assert(cplist != nullptr);
    PCnode pnewnode = (PCnode) malloc(1 * sizeof(CNode));
    assert(pnewnode != nullptr);
    pnewnode->data = val;
    pnewnode->next = cplist->next;
    cplist->next = pnewnode;
    return true;
}

Tail insertion function (Insert_tail):

Apply for a new node in the heap area through the malloc function to allocate memory space for the new node, assign the value to be inserted to the data field of the new node, define the pointer p of the structure type to point to the head node, define a loop, and the termination condition of the loop is the p pointer The next field is not equal to the head node (that is, traverse to the end node), and then replace the next field. We assign the address of the head node originally stored in the next field of the end node to the next field of pnewnode, and then assign the address of pnewnode Assign the value to the next field of the original end node, as shown in the figure:

bool Insert_tail(PCnode cplist,Elem_type val)
{
    assert(cplist != nullptr);
    PCnode pnewnode = (PCnode) malloc(1 * sizeof(CNode));
    assert(pnewnode != nullptr);
    pnewnode->data = val;
    PCnode p = cplist;
    for(;p->next != cplist;p = p->next);
    pnewnode->next = p->next;
    p->next = pnewnode;
    return true;
}

Insert function by position (Insert_pos): 

Apply for a new node in the heap area through the malloc function to allocate memory space for the new node, assign the value to be inserted to the data field of the new node, define the pointer p of the structure type to point to the head node, define a loop, and the termination condition is i < pos, where When p points to the previous node of the position to be inserted, then the next field of p stores the address of the node to be inserted, and then the next field is replaced, and the address of the node behind the position to be inserted is assigned to the next field of pnewnode, and then Just assign the address of pnewnode itself to the next field of the pointer p pointing to the node before the position to be inserted.

  

bool Insert_pos(PCnode cplist,int pos,Elem_type val)
{
    assert(cplist != nullptr);
    PCnode pnewnode = (PCnode) malloc(1 * sizeof(CNode));
    assert(pnewnode != nullptr);
    pnewnode->data = val;
    PCnode p = cplist;
    for(int i = 0;i < pos;i++){
        p = p->next;
    }
    pnewnode->next = p->next;
    p->next = pnewnode;
    return true;
}

Head delete function (Delete_head): 

First, judge the linked list as empty. If the linked list is empty, it will return false directly. Define a pointer variable p of structure type to point to the first valid node after the head node (that is, our node to be deleted), and replace the original head node Replace the address of the first valid node saved in , with the address of the second valid node, and then release the p pointer, as shown in the figure:

 

bool Delete_head(PCnode cplist)
{
    assert(cplist != nullptr);//安全性处理
    if(IsEmpty(cplist)){
        return false;
    }
    PCnode p = cplist->next;//找到待删除节点
    cplist->next = p->next;//跨越指向
    free(p);//释放删除节点
    return true;
}

Tail delete function (Delete_tail): 

First, judge the linked list as empty. If the linked list is empty, it will return false directly. Define a pointer variable p of structure type to point to the head node, define a loop about p, and the loop termination condition is that the next field of p is not the head node (also is to traverse to the last node), and then define a pointer variable q of structure type to point to the head node, define a loop about q, and the loop termination condition is that the next field of q is not p (that is, traverse to the node pointed to by p A node), copy the head node address originally stored in the next field of the node pointed to by p to the next field of the node pointed to by q, so that we have completed the tail deletion operation, as shown in the figure:

bool Delete_tail(PCnode cplist)
{
    assert(cplist != nullptr);
    if(IsEmpty(cplist)){
        return false;
    }
    PCnode p = cplist;
    for(;p->next != cplist;p = p->next);
    PCnode q = cplist;
    for(;q->next!=p;q = q->next);
    q->next = p->next;
    free(p);
    return true;
}

Delete function by position (Delete_pos):

First, judge the linked list to be empty. If the linked list is empty, it will directly return false. Define a pointer variable q of structure type to point to the head node, define a loop, and the termination condition of the loop is i < pos. At this time, what q points to is to be Delete the previous node of the node, the next field of q is the node to be deleted, and then define a pointer variable p of structure type to point to the next field of q, then the next field of p is the next node of the node to be deleted, and then We perform a cross-pointing operation, copying the next field of p to the next field of q that originally stored the address of the node to be deleted, and then release the p pointer.

bool Delete_pos(PCnode cplist,int pos)
{
    assert(cplist != nullptr);
    if(IsEmpty(cplist)){
        return false;
    }
    PCnode q = cplist;
    for(int i = 0;i < pos;i++){
        q = q->next;
    }
    PCnode p = q->next;
    q->next = p->next;
    free(p);
    return true;
}

Delete function by value (Delete_val):

First judge the linked list as empty, if the linked list is empty, it will return false directly, define a pointer variable p of structure type to store the return value of the search function, if the p pointer is empty, return false, and then define a structure type The pointer variable q points to the head node, defines a loop, and the loop termination condition is that the next field of q is not p (that is, it traverses to the previous node of the value node to be deleted), and then performs a cross-pointing operation, and the original next field of q Store the next field in which the address of the node to be deleted is replaced with p, and then release the pointer p.

bool Delete_val(PCnode cplist,Elem_type val)
{
    assert(cplist != nullptr);
    if(IsEmpty(cplist)){
        return false;
    }
    PCnode p = Search(cplist,val);
    if(p == nullptr){
        return false;
    }
    PCnode q = cplist;
    for(;q->next != p;q = q->next);
    q->next = p->next;
    free(p);
    return true;
}

test: 

Test initialization functions, positional interpolation functions, and printing functions:

#include<cstdio>
#include<cassert>
#include<cstdlib>
#include "Circular_Linked_List.h"
int main()
{
    CNode circle;
    Init_clist(&circle);
    for(int i = 0;i < 10;i++){
        Insert_pos(&circle,i,i + 1);
    }
    printf("原始数据为:\n");
    Show(&circle);
/*
    其他测试函数添加位置
*/
    return 0;
}

operation result:

 

Test header plug-in function:

Here we want to insert 100 into the head of the linked list:

    Insert_head(&circle,100);
    printf("\n头插后的数据为:\n");
    Show(&circle);

operation result: 

 Test tail interpolation function:

Here we want to insert 100 at the end of the linked list:

    Insert_tail(&circle,100);
    printf("\n头删后的数据为:\n");
    Show(&circle);

operation result:

 

Test the positional interpolation function: 

Here we want to insert 100 after the second valid node after the head node:

    Insert_pos(&circle,2,100);
    printf("\n头删后的数据为:\n");
    Show(&circle);

operation result:

 

Number of test head deletions: 

Here we want to delete the first valid node after the head node of the linked list:

    Delete_head(&circle);
    printf("\n头删后的数据为:\n");
    Show(&circle);
    return 0;

operation result:

 

Test tail delete function:

Here we want to delete the end node of the linked list:

    Delete_tail(&circle);
    printf("\n尾删后的数据为:\n");
    Show(&circle);
    return 0;

operation result:

 

Test the delete function by position: 

Here we want to delete the node after the fourth valid node after the head node in the linked list:

    Delete_pos(&circle,4);
    printf("\n按位置删后的数据为:\n");
    Show(&circle);
    return 0;

operation result:

 

Test the delete-by-value function:

Here we want to delete the value of 3 in the linked list:

    Delete_val(&circle,3);
    printf("\n按位置删后的数据为:\n");
    Show(&circle);
    return 0;

operation result:

 

Test destroy function:

    Destroy(&circle);
    printf("\n头删后的数据为:\n");
    Show(&circle);

 operation result:

Test lookup function:

    PCnode p = Search(&circle,5);
    printf("%p",p);
    return 0;

operation result:

  

If we replace the value we are looking for with a value that does not exist in the linked list, this returns an empty address, which is nullptr:

Summarize:

The one-way circular linked list is generally very similar to the single-linked list without a head node. Before we write the code, we should sort out the structure of the one-way circular linked list and clarify the difference between the two. In this way, we It is more efficient when writing code.

think:

/*面试笔试喜欢问的问题:
 * 第一题:单链表怎么进行逆置?
 * 第二题:单链表如何获取倒着打印的数据?
 * 第三题:如何判断两个单链表是否存在交点?如果存在相交点则找到第一个相交点?
 * 第四题:如果判断一个单链表是否存在环?如果存在环,则找到入环点。
 * 第五题:给一个随机节点的地址,如何在O(1)时间内删除这个节点?(这个随机节点不能是尾节点)
 * 第六题:如何判断一个单链表是否是回文链表
 */

November 7, 2022 at 23:00 

Guess you like

Origin blog.csdn.net/weixin_45571585/article/details/127724891