【数据结构】链表

【数据结构】链表

1.什么是链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

在这里插入图片描述

2.链表的种类

实际应用中链表的结构非常多样,大致为以下几种:

  1. 单向或双向
  2. 带头节点或不带头节点
  3. 循环或不循环

本文将介绍几种常见链表

3.单链表

首先我们要知道,链表是由节点构成的,以下是一个单链表节点的构成

在这里插入图片描述

data为该节点所存的数据,next用于存放下一个节点的地址

因此一个节点可表示为:

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

数据域:data 指针域:next

而链表的创建,就是将一个一个节点连接起来的过程,以单链表的尾插法为例:

1.创建头节点(head)
在这里插入图片描述

2.创建下一个节点,其位置在上一个节点后,同时上一个节点的指针域(next)指向该节点

在这里插入图片描述

3.重复以上过程,直到创建该链表的最后一个节点

在这里插入图片描述

链表的初始化

Node* initial_link()
{
    Node* head = new Node;//分配内存
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    return head;
}

链表求长

int link_length(Node* head)
{
    Node* rear = head->next;
    int n = 0;
    while (rear != NULL)
    {
        n++;
        rear = rear->next;
    }
    return n;
}

链表尾插法

void backlink(Node* head,int n)//n为要传入链表数据的个数
{
	Node* rear = head;//定义一个rear(用于指向插入节点的前一个节点),方便后续插入操作
	int i;
	for (i = 0; i < n; i++)
	{
		Node* newnode = new Node;//为newnode申请空间
		newnode->next = NULL;
		cin >> newnode->data;//传入数据
		rear->next = newnode;
		rear = newnode;
	}
}

链表头插法

void headlink(Node* head,int n)//n为要传入链表数据的个数
{
	int i;
	for (i = 0; i < n; i++)
	{
		Node* newnode = new Node;//为newnode申请空间
		newnode->next = head->next;
		cin >> newnode->data;//传入数据
		head->next = newnode;
	}
}
//相比于尾插法,头插法由于直接插在head后面,因此省去定义rear的步骤

链表的遍历输出

void printlink(Node* head)
{
    Node* rear = head->next;
	while (rear != NULL)
	{
		cout << rear->data;
		rear = rear->next;
	}
}

链表是否为空的判断

void IsEmpty(Node* head)
{
    if (head->next == NULL)//判断第一个节点是否为NULL
    {
        cout << "true";
    }
    else
        cout << "false";
}

链表的查找

Node* findlink(Node *head,int g)//查找第g个节点
{
    if (g > link_length(head))//判断g是否超出链表长度
    {
        cout << "不存在";
        return NULL;
    }
    Node* rear = head;
    int i = 0;
    while (i != g)
    {
        rear = rear->next;
        i++;
    }
    return rear;//如果存在则返回该节点
}

链表的节点插入

void insertlink(Node* head,int g,int newdata)//在第g个节点后插入数据为newdata的新节点
{
    if (g > link_length(head))//判断g是否超出链表长度
    {
        cout << "ERROR";
        return;
    }
    Node* pre_newnode=findlink(head, g);//调用链表的查找
    Node *newnode = new Node;
    newnode->next =pre_newnode->next;
    newnode->data = newdata;
    pre_newnode->next = newnode;
}

链表的节点删除

void deletelink(Node* head, int g)//删除第g个节点
{
    if (g > link_length(head))//判断g是否超出链表长度
    {
        cout << "ERROR";
        return;
    }
    Node* pre_deletenode = findlink(head, g-1);//查找第g个节点的前一个节点
    Node* deletenode = pre_deletenode->next;
    pre_deletenode->next = deletenode->next;
    delete(deletenode);//释放内存
}

链表的销毁

void destorylink(Node *head)
{
    Node* rear1 = head->next;
    Node* rear2 = NULL;//定义两个rear,rear1负责向后遍历,rear2负责释放内存
    while (rear1 != NULL)
    {
        rear2 = rear1;
        rear1 = rear1->next;
        delete(rear2);
    }
    head->next = NULL;
}

最后完整代码

#include<bits/stdc++.h>//偷个懒,直接引万能头文件

using namespace std;

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

Node* initial_link()
{
    Node* head = new Node;
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    return head;
}

int link_length(Node* head)
{
    Node* rear = head->next;
    int n = 0;
    while (rear != NULL)
    {
        n++;
        rear = rear->next;
    }
    return n;
}

void backlink(Node* head, int n)
{
    Node* rear = head;
    int i;
    for (i = 0; i < n; i++)
    {
        Node* newnode = new Node;
        newnode->next = NULL;
        cin >> newnode->data;
        rear->next = newnode;
        rear = newnode;
    }
}

void printlink(Node* head)
{
    Node* rear = head->next;
    while (rear != NULL)
    {
        cout << rear->data;
        rear = rear->next;
    }
}

void IsEmpty(Node* head)
{
    if (head->next == NULL)
    {
        cout << "true";
    }
    else
        cout << "false";
}

Node* findlink(Node* head, int g)
{
    if (g > link_length(head))
    {
        cout << "不存在";
        return NULL;
    }
    Node* rear = head;
    int i = 0;
    while (i != g)
    {
        rear = rear->next;
        i++;
    }
    return rear;
}

void insertlink(Node* head, int g, int newdata)
{
    if (g > link_length(head))
    {
        cout << "ERROR";
        return;
    }
    Node* pre_newnode = findlink(head, g);
    Node* newnode = new Node;
    newnode->next = pre_newnode->next;
    newnode->data = newdata;
    pre_newnode->next = newnode;
}

void deletelink(Node* head, int g)
{
    if (g > link_length(head))
    {
        cout << "ERROR";
        return;
    }
    Node* pre_deletenode = findlink(head, g - 1);
    if (pre_deletenode == NULL)
    {
        cout << "ERROR";
        return;
    }
    Node* deletenode = pre_deletenode->next;
    pre_deletenode->next = deletenode->next;
    delete(deletenode);
    deletenode = NULL;
}

void destorylink(Node* head)
{
    Node* rear1 = head->next;
    Node* rear2 = NULL;
    while (rear1 != NULL)
    {
        rear2 = rear1;
        rear1 = rear1->next;
        delete(rear2);
    }
    delete(rear1);
}

int main()
{
    int n,a,b,c;
    cin >> n;
    Node* head = initial_link();
    backlink(head, n);
    cin >> a >> b >> c;
    IsEmpty(head);
    cout << endl;
    printlink(head);
    cout << endl;
    cout << findlink(head, n)->data << endl;
    insertlink(head, a, b);
    printlink(head);
    cout << endl;
    deletelink(head, c);
    printlink(head);
    cout << endl;
    destorylink(head);
    IsEmpty(head);
    return 0;
}

输入n,a,b,c,表示输入n个数,查找第n个数,在第a个数后插入b,删除第c个数

测试用例:n=5,a=4,b=9,c=1

结果:

在这里插入图片描述

4.双链表

双链表其实就是由有两个指针域的节点组成的链表,目的是为了能够访问前驱节点,以下是一个双链表节点的构成

在这里插入图片描述

data为该节点所存的数据,prior用于存放上一个节点的地址,next用于存放下一个节点的地址

因此一个节点可表示为:

struct DNode {
    int data;
    DNode* prior, * next;
};

数据域:data 指针域:prior,next

双链表的创建与单链表大体一致

在这里插入图片描述

双链表的初始化

DNode* initial_Dlink()
{
    DNode* head = new DNode;
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    head->prior = NULL;
    return head;
}

双链表求长

int Dlink_length(DNode* head)
{
    DNode* rear = head->next;
    int n = 0;
    while (rear != NULL)
    {
        n++;
        rear = rear->next;
    }
    return n;
}

双链表的尾插法

void backDlink(DNode* head, int n)//n为要传入链表数据的个数
{
    DNode* rear = head;//这里依旧定义一个rear(用于指向插入节点的前一个节点),方便后续插入操作
    int i;
    for (i = 0; i < n; i++)
    {
        DNode* newnode = new DNode;
        newnode->next = NULL;
        newnode->prior=rear;//先对newnode操作
        cin >> newnode->data;
        rear->next = newnode;
        rear = newnode;
    }
}

头插法可参照单链表的头插法创建,这里就不在过多赘述

双链表的遍历

void printDlink(DNode* head)
{
    DNode* rear = head->next;
    while (rear != NULL)
    {
        cout << rear->data;
        rear = rear->next;
    }
}
//与单链表的遍历操作一模一样(水长度+1)😎

双链表是否为空的判断

void IsEmpty(DNode* head)
{
    if (head->next == NULL)
    {
        cout << "true";
    }
    else
        cout << "false";
}

双链表的查找

DNode* findDlink(DNode* head, int g)//查找第g个节点
{
    if (g > Dlink_length(head))//判断g是否超出链表长度
    {
        cout << "不存在";
        return NULL;
    }
    DNode* rear = head;
    int i = 0;
    while (i != g)
    {
        rear = rear->next;
        i++;
    }
    return rear;
}
//还是和单链表的操作一模一样捏(水长度+2)😉

双链表由于prior的存在,因此还能采取二分的办法查找

双链表的查找(二分法)

DNode* findback(DNode* head)//在二分之前先找到尾节点
{
    DNode* rear = head->next;
    while (rear->next != NULL)
    {
        rear = rear->next;
    }
    return rear;
}

DNode* findDlink(DNode* head, DNode* back, int g)
{
    if (g > Dlink_length(head))
    {
        cout << "ERROR";
        return NULL;
    }
    else if (g > Dlink_length(head) / 2)//判断是否大于长度一半,大于则从尾节点开始查找
    {
        DNode* rear = back;
        int i = Dlink_length(head);
        while (i != g)
        {
            rear = rear->prior;
            i--;
        }
        return rear;
    }
    else//从头节点开始查找
    {
        DNode* rear = head;
        int i = 0;
        while (i != g)
        {
            rear = rear->next;
            i++;
        }
        return rear;
    }
}

双链表的节点插入

void insertDlink(DNode* head, int g, int newdata)//在第g个节点后插入数据为newdata的新节点
{
    if (g > Dlink_length(head))
    {
        cout << "ERROR";
        return;
    }
    DNode* pre_newnode = findDlink(head, g);//调用查找
    DNode* newnode = new DNode;
    newnode->prior = pre_newnode;
    newnode->next = pre_newnode->next;
    newnode->data = newdata;
    if (newnode->next != NULL)//这里要判断插入后的节点是否是最后一个节点
    {
        newnode->next->prior = newnode;
    }
    pre_newnode->next = newnode;
}

双链表的节点删除

void deleteDlink(Node* head, int g)
{
    if (g > Dlink_length(head))
    {
        cout << "ERROR";
        return;
    }
    DNode* deletenode = findlink(head, g);//由于prior的存在,因此不用定义pre_deletenode
    deletenode->prior->next = deletenode->next;
    if (deletenode->next != NULL)//判断删除的节点是否是最后一个节点
    {
        deletenode->next->prior = deletenode->prior;
    }
    delete(deletenode);
}

双链表的销毁同单链表的销毁,只不过更加简化,代码就不水了单独列出来了😎

最后完整代码

#include<bits/stdc++.h>

using namespace std;

struct DNode {
    int data;
    DNode* prior, * next;
};

DNode* initial_Dlink()
{
    DNode* head = new DNode;
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    head->prior = NULL;
    return head;
}

int Dlink_length(DNode* head)
{
    DNode* rear = head->next;
    int n = 0;
    while (rear != NULL)
    {
        n++;
        rear = rear->next;
    }
    return n;
}

void backDlink(DNode* head, int n)
{
    DNode* rear = head;
    int i;
    for (i = 0; i < n; i++)
    {
        DNode* newnode = new DNode;
        newnode->next = NULL;
        newnode->prior = rear;
        cin >> newnode->data;
        rear->next = newnode;
        rear = newnode;
    }
}

void printDlink(DNode* head)
{
    DNode* rear = head->next;
    while (rear != NULL)
    {
        cout << rear->data;
        rear = rear->next;
    }
}

void IsEmpty(DNode* head)
{
    if (head->next == NULL)
    {
        cout << "true";
    }
    else
        cout << "false";
}

DNode* findDlink(DNode* head, int g)
{
    if (g > Dlink_length(head))
    {
        cout << "不存在";
        return NULL;
    }
    DNode* rear = head;
    int i = 0;
    while (i != g)
    {
        rear = rear->next;
        i++;
    }
    return rear;
}

void insertDlink(DNode* head, int g, int newdata)
{
    if (g > Dlink_length(head))
    {
        cout << "ERROR";
        return;
    }
    DNode* pre_newnode = findDlink(head, g);
    DNode* newnode = new DNode;
    newnode->prior = pre_newnode;
    newnode->next = pre_newnode->next;
    newnode->data = newdata;
    if (newnode->next != NULL)
    {
        newnode->next->prior = newnode;
    }
    pre_newnode->next = newnode;
}

void deleteDlink(DNode* head, int g)
{
    if (g > Dlink_length(head))
    {
        cout << "ERROR";
        return;
    }
    DNode* deletenode = findDlink(head, g);
    deletenode->prior->next = deletenode->next;
    if (deletenode->next != NULL)
    {
        deletenode->next->prior = deletenode->prior;
    }
    delete(deletenode);
}

void destoryDlink(DNode* head)
{
    DNode* rear = head;//由于prior的存在,不需要定义rear2来辅助
    while (rear ->next != NULL)
    {
        rear = rear->next;
        delete(rear->prior);
    }
    delete(rear);
    head->next = NULL;
}

int main()
{
    int n, a, b, c;
    cin >> n;
    DNode* head = initial_Dlink();
    backDlink(head, n);
    cin >> a >> b >> c;
    IsEmpty(head);
    cout << endl;
    printDlink(head);
    cout << endl;
    cout << findDlink(head, n)->data << endl;
    insertDlink(head, a, b);
    printDlink(head);
    cout << endl;
    deleteDlink(head, c);
    printDlink(head);
    cout << endl;
    destoryDlink(head);
    IsEmpty(head);
    return 0;
}
//怎么有种套娃的感觉φ(* ̄0 ̄)

输入n,a,b,c,表示输入n个数,查找第n个数,在第a个数后插入b,删除第c个数

测试用例:n=6,a=3,b=1,c=2

结果:

在这里插入图片描述

5.循环链表

循环链表分为单向循环链表和双向循环链表,其实际上就是将最后一个节点的指针域和第一个节点相连,这样改造的好处是无论从任意节点进入链表,都能通过循环访问到任意一个节点

在这里插入图片描述

因此实现上并不困难,直接上代码(以循环单链表为例)

循环链表的初始化

Node* initial_looplink()
{
    Node* head = new Node;
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    return head;
}

循环链表求长

int looplink_length(Node* head)
{
    if (head->next == NULL)//判断链表是否为空
    {
        return 0;
    }
    Node* rear = head->next;
    int n = 0;
    do
    {
        rear = rear->next;
        n++;
    } while (rear != head->next);
    return n;
}

循环链表的尾插法

void backlooplink(Node* head, int n)
{
    Node* rear = head;
    int i;
    for (i = 0; i < n; i++)
    {
        Node* newnode = new Node;
        newnode->next = head->next;//将原本的NULL换成第一个节点
        cin >> newnode->data;
        rear->next = newnode;
        rear = newnode;
    }
}

循环链表的遍历

void printlooplink(Node* head)
{
    if (head->next == NULL)//判断链表是否为空
    {
        return;
    }
    Node* rear = head->next;
    do//用do...while确保第一个节点能够输出
    {
        cout << rear->data;
        rear = rear->next;
    } while (rear != head->next);//注意判断条件的改变
}

循环链表是否为空的判断

void IsEmpty(Node* head)
{
    if (head->next == NULL)//判断第一个节点是否为NULL
    {
        cout << "true";
    }
    else
        cout << "false";
}

循环链表的查找

Node* findlooplink(Node* head, int g_data)//因为是循环链表,所以没必要查找第几个节点,因此我们查找数据为g_data的节点
{
    Node* rear = head;
    do
    {
        rear = rear->next;
    } while (rear->data != g_data && rear->next != head->next);//防止陷入死循环
    if (rear->data == g_data)
    {
        return rear;
    }
    else
    {
        cout << "不存在";
        return NULL;
    }
}

循环链表的节点插入

void insertlooplink(Node* head,int g_data,int newdata)//在数据为g_data的节点后插入数据为newdata的新节点
{
    Node* pre_newnode=findlooplink(head, g);//调用链表的查找
    if (pre_newnode == NULL)
    {
        cout << "ERROR";
        return;
    }
    Node *newnode = new Node;
    newnode->next =pre_newnode->next;
    newnode->data = newdata;
    pre_newnode->next = newnode;
}
//代码基本直接copy单链表的就好😋

循环链表的节点删除

void deletelooplink(Node* head, int g_data)//删除数据为g_data的节点
{
    Node* deletenode = findlooplink(head, g_data);//查找该节点
    if (deletenode == NULL)
    {
        cout << "ERROR";
        return;
    }
    if (deletenode == head->next)//判断是否为第一个节点(因为第一个节点与尾节点相连)
    {
        if (looplink_length(head) == 1)//判断此时是否只剩下一个节点
        {
            delete(deletenode);
            head->next = NULL;//删除,标记此链表为空
            return;
        }
        Node* rear = head->next;
        while (rear->next != head->next)//查找尾节点
        {
            rear = rear->next;
        }
        head->next = deletenode->next;//头节点的next也要改变
        rear->next = deletenode->next;//改变尾节点next
        return;
    }
    Node* pre_deletenode = head;
    while (pre_deletenode->next != deletenode)
    {
        pre_deletenode = pre_deletenode->next;
    }
    pre_deletenode->next = deletenode->next;
    delete(deletenode);//释放内存
}

循环链表的销毁

void destorylooplink(Node* head)
{
    Node* rear1 = head->next;//这里让rear1先从第一个节点出发
    Node* rear2 = NULL;//依旧定义两个rear,rear1负责向后遍历,rear2负责释放内存
    do
    {
        rear2 = rear1;
        rear1 = rear1->next;
        delete(rear2);
    } while (rear1 != head->next);
    head->next = NULL;
}

完整代码

#include<bits/stdc++.h>

using namespace std;

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

Node* initial_looplink()
{
    Node* head = new Node;
    if (head == NULL)
    {
        cout << "分配失败,程序退出";
        exit(-1);
    }
    head->next = NULL;
    return head;
}

int looplink_length(Node* head)
{
    if (head->next == NULL)
    {
        return 0;
    }
    Node* rear = head->next;
    int n = 0;
    do
    {
        rear = rear->next;
        n++;
    } while (rear != head->next);
    return n;
}

void backlooplink(Node* head, int n)
{
    Node* rear = head;
    int i;
    for (i = 0; i < n; i++)
    {
        Node* newnode = new Node;
        newnode->next = head->next;
        cin >> newnode->data;
        rear->next = newnode;
        rear = newnode;
    }
    rear->next = head->next;
}

void printlooplink(Node* head)
{
    if (head->next == NULL)
    {
        return;
    }
    Node* rear = head->next;
    do
    {
        cout << rear->data;
        rear = rear->next;
    } while (rear != head->next);
}

void IsEmpty(Node* head)
{
    if (head->next == NULL)
    {
        cout << "true";
    }
    else
        cout << "false";
}

Node* findlooplink(Node* head, int g_data)
{
    Node* rear = head;
    do
    {
        rear = rear->next;
    } while (rear->data != g_data && rear->next != head->next);
    if (rear->data == g_data)
    {
        return rear;
    }
    else
    {
        cout << "不存在"<<endl;
        return NULL;
    }
}

void insertlooplink(Node* head, int g_data, int newdata)
{
    Node* pre_newnode = findlooplink(head, g_data);
    if (pre_newnode == NULL)
    {
        cout << "ERROR";
        return;
    }
    Node* newnode = new Node;
    newnode->next = pre_newnode->next;
    newnode->data = newdata;
    pre_newnode->next = newnode;
}

void deletelooplink(Node* head, int g_data)
{
    Node* deletenode = findlooplink(head, g_data);
    if (deletenode == NULL)
    {
        cout << "ERROR";
        return;
    }
    if (deletenode == head->next)
    {
        if (looplink_length(head) == 1)
        {
            delete(deletenode);
            head->next = NULL;
            return;
        }
        Node* rear = head->next;
        while (rear->next != head->next)
        {
            rear = rear->next;
        }
        head->next = deletenode->next;
        rear->next = deletenode->next;
        return;
    }
    Node* pre_deletenode = head;
    while (pre_deletenode->next != deletenode)
    {
        pre_deletenode = pre_deletenode->next;
    }
    pre_deletenode->next = deletenode->next;
    delete(deletenode);
}

void destorylooplink(Node* head)
{
    Node* rear1 = head->next;
    Node* rear2 = NULL;
    do
    {
        rear2 = rear1;
        rear1 = rear1->next;
        delete(rear2);
    } while (rear1 != head->next);
    head->next = NULL;
}

int main()
{
    int n, a, b, c;
    cin >> n;
    Node* head = initial_looplink();
    backlooplink(head, n);
    cin >> a >> b >> c;
    IsEmpty(head);
    cout << endl;
    printlooplink(head);
    cout << endl;
    insertlooplink(head, a, b);
    printlooplink(head);
    cout << endl;
    deletelooplink(head, c);
    printlooplink(head);
    cout << endl;
    destorylooplink(head);
    IsEmpty(head);
    return 0;
}

输入n,a,b,c,表示输入n个数,在数据为a的数后插入b,删除数据为c的数

测试用例:n=8,a=3,b=9,c=1

结果:

在这里插入图片描述

结语:

新手第一次写blog,写的不好的话还请见谅😭(;´д`)ゞ

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值