IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构之链表(个人自用向) -> 正文阅读

[数据结构与算法]数据结构之链表(个人自用向)

目录

前言??

一、单链表

?二、双链表

三、循环链表

四、静态链表


前言??

链表是指采用链式存储的一种数据结构,其最小结构单元称为结点,一般包含两种信息,即数据域和指针域。数据域用于存储数据,也即是结点的值。指针域一般用于存储其指向的下一个结点的位置,即通过指针域来链接构成链表结构。

我目前所知道的链表大概有以下几种:

1、单链表(最简单的一种链表)

2、双链表(在单链表的基础上采用双向指针的方式实现的链表结构)

3、循环链表(在单链表的基础上把首尾指针相连构成可以循环的链表结构)

4、静态链表(采用数组方式实现的链表结构)

待补充...

下面开始逐一分析我所知道的这几种链表结构的具体实现方式

一、单链表

单链表应该是最基本最简单链表结构了,其具体实现方式可以用一个图来看更加形象

?用小方框来表示数据域,箭头来表示指针域,单链表也就可以形象的看作一个一个向下指的结构了。其中最后一个方框一般为空作为链表的结束位置。

定义讲完了,接下来就是具体的代码实现:

一般有两种方法:数组模拟和类模板

这里先讲数组模拟的方法

首先是采用头插法实现的模拟链表

int head, e[N], ne[N], idx, t;

void init()
{
    head = -1;
    idx = 0;
}
void add_to_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx ++;
}
void remove(int k)
{
    ne[k] = ne[ne[k]];
}
void add(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx ++;
}

作者:shengshu_
链接:https://www.acwing.com/activity/content/code/content/2708157/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

然后再来看用尾插法实现的模拟链表

#include <iostream>
using namespace std;
const int N = 10010;
int e[N], ne[N], tail, idx;
void init()
{
    tail = 0, idx = 1;
}
void add_to_tail(int x)
{
    e[idx] = x;
    ne[tail] = idx;
    ne[idx] = -1;
    tail = idx ++;
}
int main()
{
    int n, m;
    cin >> n;
    init();
    for (int i = 0; i < n; i ++) {
        cin >> m;
        add_to_tail(m);
    }
    for (int i = 1; i != -1; i = ne[i])
        cout << e[i] << " ";
    return 0;
}

细节会稍有不同,但具体逻辑是相通的。

其中e[N]数组存储值作为数据域,ne[N]数组存储下标也就是下一个指向的位置作为指针域,head或者tail作为头结点或尾结点,idx作为结点给每一个位置赋予一个独特的值。然后遍历链表的情况也大致相同,从首个位置出发,遍历到最后一个位置也就是-1处结束。

第二种就是用类模板的情况

这里我偷个懒没用模板,直接定义了一个int类型的链表

具体实现方法如下:

#include <iostream>
using namespace std;
struct Node
{
    int data;
    Node *next;
};
class LinkList
{
    Node *first;
public:
    LinkList();
    LinkList(int a[], int n, int op);//建表,两种方法
    ~LinkList();
    int Length();
    int Get(int i);//按位置查找
    int Locate(int x);//按值查找
    void Insert(int i, int x);//在第i个位置插入数值x
    int Delete(int i);//删除第i个结点并返回第i个结点的值
    int Empty();
    void PrintList();
};
LinkList::LinkList()
{
    first = new Node;
    first -> next = NULL;
}
LinkList::LinkList(int a[], int n, int op)
{
    //头插法
    if (op) {
            first = new Node;
            first -> next = NULL;
            Node *s = NULL;
            for (int i = 0; i < n; i ++) {
            s = new Node;
            s -> data = a[i];
            s -> next = first -> next;
            first -> next = s;
        }
    }
    //尾插法
    else {
        first = new Node;
        Node * tail = first, *s = NULL;
        for (int i = 0; i < n; i ++) {
            s = new Node;
            s -> data = a[i];
            tail -> next = s;
            tail = s;
        }
        tail -> next = NULL;
    }
}
LinkList::~LinkList()
{
    Node *p = NULL;
    while (first != NULL) {
        first = first -> next;
        delete p;
        p = first;
    }
}
int LinkList::Length()
{
    Node *p = first -> next;
    int cnt = 0;
    while (p != NULL) {
        p = p -> next;
        cnt ++;
    }
    return cnt;
}
int LinkList::Get(int i)
{
    Node *p = first -> next;
    int cnt = 1;
    while (p != NULL && cnt < i) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL) cerr << "查找位置错误";
    return p -> data;
}
int LinkList::Locate(int x)
{
    Node *p = first -> next;
    int cnt = 1;
    while (p != NULL) {
        if (p -> data == x)  return cnt;
        p = p -> next;
        cnt ++;
    }
    return 0;
}
void LinkList::Insert(int i, int x)
{
    Node *p = first, *s = NULL;
    int cnt = 0;
    while (p != NULL && cnt < i-1) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL) cerr << "插入位置错误";
    else {
        s = new Node;
        s -> data = x;
        s -> next = p -> next;
        p -> next = s;
    }
}
int LinkList::Delete(int i)
{
    int x;
    Node *p = first, *q = NULL;
    int cnt = 0;
    while (p != NULL && cnt < i-1) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL || p -> next == NULL) cerr << "删除位置错误";
    else {
        q = p -> next;
        x = q -> data;
        p -> next = q -> next;
        delete q;
        return x;
    }
}
int LinkList::Empty()
{
    if (first -> next == NULL) return 1;
    else return 0;
}
void LinkList::PrintList()
{
    Node *p = first -> next;
    while (p != NULL) {
        cout << p -> data << " ";
        p = p -> next;
    }
    cout << endl;
}
int main()
{
    int r[5] = {1,2,3,4,5}, i, x;
    LinkList L(r,5,0);
    cout << "当前线性表的数据为: ";
    L.PrintList();
    try
    {
        L.Insert(2,8);
        L.PrintList();
    } catch(string str) {cout << str << endl;}
    cout << "当前链表的长度为: " << L.Length() << endl;
    cout << "请输入查找的元素值: ";
    cin >> x;
    i = L.Locate(x);
    if (i > 0) cout << "元素" << x << "的位置为: " << i << endl;
    else cout << "单链表中没有元素" << x << endl;
    try
    {
        cout << "请输入要删除第几个元素: " ;
        cin >> i;
        x = L.Delete(i);
        cout << "删除的元素值是" << x << ",执行删除操作后数据为: ";
        L.PrintList();
    } catch(string str) {cout << str << endl;}
    return 0;
}

?二、双链表

与上面的单链表类似,不过指针不是用头指针或尾指针了,取而代之的是前驱和后继两个指针同时存储位置信息。对比单链表的优点:可以十分方便的查找出某个结点的前驱值。

具体实现方法如下:

1、数组模拟

int r[N], l[N], e[N], idx; 
右指针,左指针,数据域,结点
void init()
{
    //0表示左端点,1表示右端点
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}

//在第k个点的右边插入x
//如果是在k的左边插入x,直接调用add(l[k],x)
void add(int k, int x)
{
    e[idx] = x;
    r[idx] = r[k];
    l[idx] = k;
    l[r[k]] = idx;
    r[k] = idx;
    idx ++;
}

//删除第k个点
void remove(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}
//遍历链表,直接向右走就可以
void traverse()
{
    for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
}

作者:shengshu_
链接:https://www.acwing.com/activity/content/code/content/2727273/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2、类模板

跟单链表的类模板很相似,不过连指针的时候要注意前驱和后继可能都需要修改

#include <iostream>
using namespace std;
struct Node
{
    int data;
    Node *prior, *next;
};
class DLinkList
{
    Node *first;
public:
    DLinkList();
    DLinkList(int a[], int n, int op);
    ~DLinkList();
    int Length();
    int Get(int i);
    int Locate(int x);
    void Insert(int i, int x);
    int Delete(int i);
    int Empty();
    void PrintList();
};
DLinkList::DLinkList()
{
    first = new Node;
    first -> prior = NULL;
    first -> next = NULL;
}
DLinkList::DLinkList(int a[], int n, int op)
{
    if (op) {
            first = new Node;
            first -> prior = NULL;
            first -> next = NULL;
            Node *s = NULL;
            for (int i = 0; i < n; i ++) {
            s = new Node;
            s -> data = a[i];
            s -> prior = first;
            s -> next = first -> next;
            first -> next = s;
        }
    }
    else {
        first = new Node;
        Node *tail = first, *s = NULL;
        for (int i = 0; i < n; i ++) {
            s = new Node;
            s -> data = a[i];
            s -> prior = tail;
            tail -> next = s;
            tail = s;
        }
        tail -> next = NULL;
    }
}
DLinkList::~DLinkList()
{
    Node *p = NULL;
    while (p != NULL) {
        p =  p -> next;
        delete p;
        p = first;
    }
}
int DLinkList::Length()
{
    Node *p = first -> next;
    int cnt = 0;
    while (p != NULL) {
        p = p -> next;
        cnt ++;
    }
    return cnt;
}
int DLinkList::Get(int i)
{
    Node *p = first -> next;
    int cnt = 1;
    while (p != NULL && cnt < i) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL) cerr << "查找位置错误";
    return p -> data;
}
int DLinkList::Locate(int x)
{
    Node *p = first -> next;
    int cnt = 1;
    while (p != NULL) {
        if (p -> data == x)  return cnt;
        p = p -> next;
        cnt ++;
    }
    return 0;
}
void DLinkList::Insert(int i, int x)
{
    Node *p = first, *s = NULL;
    int cnt = 0;
    while (p != NULL && cnt < i-1) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL) cerr << "插入位置错误";
    else {
        s = new Node;
        s -> data = x;
        s -> prior = p;
        s -> next = p -> next;
        p -> next -> prior = s;
        p -> next = s;
    }
}
int DLinkList::Delete(int i)
{
    int x;
    Node *p = first, *q = NULL;
    int cnt = 0;
    while (p != NULL && cnt < i-1) {
        p = p -> next;
        cnt ++;
    }
    if (p == NULL || p -> next == NULL) cerr << "删除位置错误";
    else {
        q = p -> next;
        x = q -> data;
        p -> next = q -> next;
        q -> next -> prior = p;
        delete q;
        return x;
    }
}
int DLinkList::Empty()
{
    if (first -> next == NULL) return 1;
    return 0;
}
void DLinkList::PrintList()
{
    Node *p = first -> next;
    while (p != NULL) {
        cout << p -> data << " ";
        p = p -> next;
    }
    cout << endl;
}
int main()
{
    int r[5] = {1,2,3,4,5}, i, x;
    DLinkList L(r,5,0);
    cout << "当前线性表的数据为: ";
    L.PrintList();
    try
    {
        L.Insert(2,8);
        L.PrintList();
    } catch(string str) {cout << str << endl;}
    cout << "当前链表的长度为: " << L.Length() << endl;
    cout << "请输入查找的元素值: ";
    cin >> x;
    i = L.Locate(x);
    if (i > 0) cout << "元素" << x << "的位置为: " << i << endl;
    else cout << "单链表中没有元素" << x << endl;
    try
    {
        cout << "请输入要删除第几个元素: " ;
        cin >> i;
        x = L.Delete(i);
        cout << "删除的元素值是" << x << ",执行删除操作后数据为: ";
        L.PrintList();
    } catch(string str) {cout << str << endl;}

    return 0;
}

三、循环链表

循环链表是一种特殊的单链表,将链表中最后一个结点的指针指向头结点即可实现循环链表

具体实现方式就相当于单链表,但只需改变下最后一个结点的指向即可,这里就不再给出代码了。

若是在双链表中实现循环的话,只需将最后一个结点的后继改为头结点,头结点的前驱改为尾结点即可。

四、静态链表

静态链表使用数组来表示的链表结构,与前面用数组模拟的方法类似,但是采用类模板的方法,通用性更好。静态链表由于大小是固定的,所以指针域不再采用指针类型,取而代之以int类型的游标表示数组的下标。

具体实现原理同单链表,这里就不再给出代码了。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 21:07:03  更:2022-10-08 21:09:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:43:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码