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 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C++实现单链表 -> 正文阅读

[数据结构与算法]C++实现单链表

实现链表的基本功能

链表的特点就不赘述了,这类的文章特别多,这里以简单的方式手动实现链表的各种操作。

结点结构

链表中的每个结点,包括一个结点值val和指向下一个结点的指针next。

struct ListNode {
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* next) : val(x), next(next) {}
};

实现功能:

  1. get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  2. addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  3. addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  4. addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  5. deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

设计

维护一个链表元素个数size。
所有元素结点index的范围是[0…size-1],第一个结点的index是0,第二个是1,以此类推。

获取前置结点

因为单链表不能反向遍历,而且插入,删除操作都要对目标结点的前一个结点进行操作,所以特地编写一个获取目标序号之前结点的方法:
如果index=0,就返回头结点;如果index=1,就返回第一个结点;以此类推。

//查找index位置的前一个结点,index范围[0...size-1]
ListNode* findBefore(int index) {
    ListNode* p = head;
    for (int i = 0; p != nullptr && i < index; i++) {
        p = p->next;
    }
    return p;
}

获取第index个结点的值

借助于前面写的findBefore方法,可以不用另外写一个for循环了。
index超出范围的话,返回-1

//获取链表中第 index 个节点的值。如果索引无效,则返回-1。index范围[0...size-1]
int get(int index) {
    if (index < 0 || index >= size) {
        return -1;
    }
    return findBefore(index)->next->val;
}

头插

在链表最前面插入新节点,值val,这个结点在head和第一个结点之间。

//头插
void addAtHead(int val) {
    ListNode* node = new ListNode(val);
    node->next = head->next;
    head->next = node;
    size++;
    
	//对一个结点的链表进行头插会导致尾结点发生变化
	if (size == 1) {
    	tail = node;
    }
}

尾插

在链表尾部插入新结点,值val。
因为尾插是比较常用的操作,如果每次尾插都遍历链表查找尾结点,性能会比较差,所以这里维护了一个尾结点指针tail
尾插、删除最后一个结点、空表重复头插都会导致尾结点产生变化,具体维护方式在对应方法里。

//尾插
void addAtTail(int val) {
    ListNode* node = new ListNode(val);
    tail->next = node;
    tail = node;
    size++;
}

在指定index位置插入新结点

在链表中的第 index 个节点之前添加值为 val 的节点,插入的合法范围:index<=size。
如果index<=0则执行头插,index=size则执行尾插。

void addAtIndex(int index, int val) {
    if (index > size) {
        return;
    }
    if (index <= 0) {
        addAtHead(val);
    }
    else if (index == size) {
        addAtTail(val);
    }
    else {
        ListNode* before = findBefore(index);
        ListNode* node = new ListNode(val);
        node->next = before->next;
        before->next = node;
        size++;
    }
}

删除指定index位置的结点

index范围[0…size-1]

void deleteAtIndex(int index) {
    if (index < 0 || index >= size) {
        return;
    }
    ListNode* before = findBefore(index);
    //如果删除最后一个结点,需要处理尾结点
    if (index == size - 1) {
        tail = before;
    }
    ListNode* delNode = before->next;
    before->next = delNode->next;
    size--;
}

打印链表

每个结点之间用空格分隔。

//输出链表
void printList() {
    //p指向头结点后面的首个数据结点
    ListNode* p = head->next;
    while (p != nullptr) {
        cout << p->val << " ";
        p = p->next;
    }
    cout << endl;
}

完整代码

用一个链表类实现链表的初始化和各种功能。

#include <iostream>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* next) : val(x), next(next) {}
};

class MyLinkedList {
private:
    ListNode* head;
    ListNode* tail;
    //数据结点的数量
    int size;

public:
    //构造为只有一个头结点,大小为0的初始链表
    MyLinkedList() {
        size = 0;
        head = new ListNode(0);
        tail = head;
    }

    int getSize() {
        return size;
    }

    //查找index位置的前一个结点,index范围[0...size-1]
    ListNode* findBefore(int index) {
        ListNode* p = head;
        for (int i = 0; p != nullptr && i < index; i++) {
            p = p->next;
        }
        return p;
    }

    //获取链表中第 index 个节点的值。如果索引无效,则返回-1。index范围[0...size-1]
    int get(int index) {
        if (index < 0 || index >= size) {
            return -1;
        }
        return findBefore(index)->next->val;
    }

    //头插
    void addAtHead(int val) {
        ListNode* node = new ListNode(val);
        node->next = head->next;
        head->next = node;
        size++;
        if (size == 1) {
            tail = node;
        }
    }

    //尾插
    void addAtTail(int val) {
        ListNode* node = new ListNode(val);
        tail->next = node;
        tail = node;
        size++;
    }

    /*
    * 在链表中的第 index 个节点之前添加值为 val  的节点,插入的合法范围:index<=size。
    * 如果index<=0则执行头插,index=size则执行尾插
    */
    void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        if (index <= 0) {
            addAtHead(val);
        }
        else if (index == size) {
            addAtTail(val);
        }
        else {
            ListNode* before = findBefore(index);
            ListNode* node = new ListNode(val);
            node->next = before->next;
            before->next = node;
            size++;
        }
    }

    //index范围[0...size-1]
    void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        ListNode* before = findBefore(index);
        //如果删除最后一个结点,需要处理尾结点
        if (index == size - 1) {
            tail = before;
        }
        ListNode* delNode = before->next;
        before->next = delNode->next;
        size--;
    }

    //输出链表
    void printList() {
        //p指向头结点后面的首个数据结点
        ListNode* p = head->next;
        while (p != nullptr) {
            cout << p->val << " ";
            p = p->next;
        }
        cout << endl;
    }
};

int main() {
    MyLinkedList ml;
    ml.addAtHead(1);
    ml.addAtTail(3);
    ml.printList();

    ml.deleteAtIndex(0);
    ml.printList();
    cout << ml.get(0) << endl;
    
    //cout << ml.getSize();
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 21:19:25  更:2022-09-24 21:23:14 
 
开发: 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年5日历 -2024/5/19 19:21:28-

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