【算法与数据结构】707、LeetCode设计链表

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解

一、题目

在这里插入图片描述

在这里插入图片描述

二、设计链表

  思路分析:这里我将的成员函数放在类外实现了,这样链表类看起来更加简洁,方便大家学习链表的结构,主要包含:节点类结构体,构造函数(构造函数也可以放在类外实现),成员函数和成员变量类的成员函数实现主要有两种,一种是类内实现,一种是类外实现,类外实现需要在类内写声明,然后在类外写实现。构造函数初始化一个虚假头结点和链表大小。关于虚假头结点,可以参考【算法与数据结构】203、LeetCode移除链表元素
  链表类如下

// 链表类
class MyLinkedList {
public:   
    // 定义链表节点类结构体
    struct LinkNode {
        int val;
        LinkNode* next;
        LinkNode() : val(0), next(NULL) {};
        LinkNode(int input_val) : val(input_val), next(NULL) {};
        LinkNode(int input_val, LinkNode* input_next) : val(input_val), next(input_next) {};
    };
    // 构造函数
    MyLinkedList() {
        _FakeNode = new LinkNode(0);    // 虚假头结点
        _size = 0;
    }
// 成员函数
    int get(int index);
    void addAtHead(int val);
    void addAtTail(int val);
    void addAtIndex(int index, int val);
    void deleteAtIndex(int index);
    void ChainGenerator(int arr[], int len);
    void LinkListPrint(string str);
// 成员变量
private:
    int _size;
    LinkNode* _FakeNode;
};

  讲完整体构造,然后将各个部分。
  首先是get函数:进入链表首先判断index是否合法,然后链表从虚假头结点出发,利用index,当找到目标索引时,cur就指向这个值,index自减归于0退出循环

int MyLinkedList::get(int index) { 
    if (index > (_size - 1) || index < 0) return -1;
    LinkNode* cur = _FakeNode->next; // 头结点
    while (index--) {
        cur = cur->next;
    }
    return cur->val;
}

  addAtHead函数:这个函数比较简单,我们新建一个节点,指向虚节点的下一个节点(也就是插入之前的头结点),然后让虚节点指向新建节点,最后链表大小+1。

void MyLinkedList::addAtHead(int val) {
    LinkNode* pNewNode = new LinkNode(val, _FakeNode->next);
    _FakeNode->next = pNewNode;
    _size++;
}

  addAtTail函数:addTail函数需要遍历这个链表,找到尾节点,复杂度是 O ( n ) O(n) O(n),让尾节点指向新建节点即可。

void MyLinkedList::addAtTail(int val) {
    LinkNode* pNewNode = new LinkNode(val);
    LinkNode* cur = _FakeNode;
    while (cur->next != NULL) {
        cur = cur->next;
    }
    cur->next = pNewNode;
    _size++;
}

  addAtIndex函数:前两个函数都可以用这个函数实现,在这个函数中,首先根据题目条件判断index是否合法,令cur指针等于虚节点,同样使用index找到插入位置的上一个节点,然后就是链表的插入操作。

void MyLinkedList::addAtIndex(int index, int val) {   
    if (index > _size) return;
    if (index < 0) index = 0;
    LinkNode* cur = _FakeNode; // 虚结点 
    // 需要断开上一一个阶段的链接,从插入位置的上一个索引开始处理
    while (index--) {
        cur = cur->next;
    }
    LinkNode* pNewNode = new LinkNode(val, cur->next);
    cur->next = pNewNode;
    _size++;
}

  deleteAtIndex函数:这个函数可以参考【算法与数据结构】203、LeetCode移除链表元素

void MyLinkedList::deleteAtIndex(int index) {     
    if (index > (_size - 1) || index < 0) return;
    LinkNode* cur = _FakeNode; // 头结点
    // 需要断开上一一个阶段的链接
    while (index--) {
        cur = cur->next;
    }
    LinkNode* tmp = cur->next;
    cur->next = cur->next->next;
    delete tmp;
    tmp = NULL;
    _size--;
}

  ChainGenerator函数和LinkListPrint函数:除了题目要求的函数以外,我还写了数组生成链表函数,以及链表打印函数。这两个函数在main函数中调用,方便调试。

void MyLinkedList::ChainGenerator(int arr[], int len) {
    if (_FakeNode->next != NULL) return;
    LinkNode* head = new LinkNode(arr[0], NULL);
    LinkNode* p = head;
    for (int i = 1; i < len; i++) {
        LinkNode* pNewNode = new LinkNode(arr[i], NULL);
        p->next = pNewNode; // 上一个节点指向这个新建立的节点
        p = pNewNode; // p节点指向这个新的节点
    }
    _FakeNode->next = head;
    _size = len;
}
void MyLinkedList::LinkListPrint(string str) {
    cout << str << endl;
    LinkNode* cur = _FakeNode;
    while (cur->next != NULL) {
        cout << cur->next->val << ' ';
        cur = cur->next;
    }
    cout << endl;
}

三、完整代码

# include <iostream>
using namespace std;

// 链表类
class MyLinkedList {
public:   
    // 定义链表节点类结构体
    struct LinkNode {
        int val;
        LinkNode* next;
        LinkNode() : val(0), next(NULL) {};
        LinkNode(int input_val) : val(input_val), next(NULL) {};
        LinkNode(int input_val, LinkNode* input_next) : val(input_val), next(input_next) {};
    };
    // 构造函数
    MyLinkedList() {
        _FakeNode = new LinkNode(0,NULL);    // 虚假头结点
        _size = 0;
    }
// 成员函数
    int get(int index);
    void addAtHead(int val);
    void addAtTail(int val);
    void addAtIndex(int index, int val);
    void deleteAtIndex(int index);
    void ChainGenerator(int arr[], int len);
    void LinkListPrint(string str);
// 成员变量
private:
    int _size;
    LinkNode* _FakeNode;
};

// 类外实现链表的成员函数
int MyLinkedList::get(int index) { 
    if (index > (_size - 1) || index < 0) return -1;
    LinkNode* cur = _FakeNode->next; // 头结点
    while (index--) {
        cur = cur->next;
    }
    return cur->val;
}

void MyLinkedList::addAtHead(int val) {
    LinkNode* pNewNode = new LinkNode(val, _FakeNode->next);
    _FakeNode->next = pNewNode;
    _size++;
}

void MyLinkedList::addAtTail(int val) {
    LinkNode* pNewNode = new LinkNode(val);
    LinkNode* cur = _FakeNode;
    while (cur->next != NULL) {
        cur = cur->next;
    }
    cur->next = pNewNode;
    _size++;
}

void MyLinkedList::addAtIndex(int index, int val) {   
    if (index > _size) return;
    if (index < 0) index = 0;
    LinkNode* cur = _FakeNode; // 虚结点 
    // 需要断开上一一个阶段的链接,从插入位置的上一个索引开始处理
    while (index--) {
        cur = cur->next;
    }
    LinkNode* pNewNode = new LinkNode(val, cur->next);
    cur->next = pNewNode;
    _size++;
}

void MyLinkedList::deleteAtIndex(int index) {     
    if (index > (_size - 1) || index < 0) return;
    LinkNode* cur = _FakeNode; // 头结点
    // 需要断开上一一个阶段的链接
    while (index--) {
        cur = cur->next;
    }
    LinkNode* tmp = cur->next;
    cur->next = cur->next->next;
    delete tmp;
    tmp = NULL;
    _size--;
}

void MyLinkedList::ChainGenerator(int arr[], int len) {
    if (_FakeNode->next != NULL) return;
    LinkNode* head = new LinkNode(arr[0], NULL);
    LinkNode* p = head;
    for (int i = 1; i < len; i++) {
        LinkNode* pNewNode = new LinkNode(arr[i], NULL);
        p->next = pNewNode; // 上一个节点指向这个新建立的节点
        p = pNewNode; // p节点指向这个新的节点
    }
    _FakeNode->next = head;
    _size = len;
}

void MyLinkedList::LinkListPrint(string str) {
    cout << str << endl;
    LinkNode* cur = _FakeNode;
    while (cur->next != NULL) {
        cout << cur->next->val << ' ';
        cur = cur->next;
    }
    cout << endl;
}

int main()
{
    int arr[] = { 2, 3};
    int len = sizeof(arr) / sizeof(int);
    MyLinkedList m1;
    m1.ChainGenerator(arr, len);
    m1.LinkListPrint("打印链表:");
    m1.addAtHead(1);
    m1.addAtTail(5);
    m1.LinkListPrint("打印链表:");
    m1.addAtIndex(3, 4);
    m1.addAtIndex(5, 666);
    m1.LinkListPrint("打印链表:");
    m1.deleteAtIndex(5);
    m1.LinkListPrint("打印链表:");
    int val1 = m1.get(0);
    cout << "目标索引的值:" << val1 << endl;

    system("pause");
    return 0;
}

end

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1. 二分法 5 1.1. 什么是二分查找 5 1.2. 如何识别二分法 5 1.3. 二分法模板 6 1.3.1. 模板一 6 1.3.1.1. 模板代码 6 1.3.1.2. 关键属性 7 1.3.1.3. 语法说明 7 1.3.1.4. Lc69:x的平方根 8 1.3.1.5. Lc374:猜数大小 9 1.3.1.6. Lc33:搜索旋转数组 11 1.3.2. 模板二 13 1.3.2.1. 模板代码 13 1.3.2.2. 关键属性 14 1.3.2.3. 语法说明 14 1.3.2.4. Lc278:第一个错误版本 14 1.3.2.5. Lc162:寻找峰值 16 1.3.2.6. Lc153:寻找旋转排序数组最小值 19 1.3.2.7. Lc154:寻找旋转排序数组最小值II 20 1.3.3. 模板三 22 1.3.3.1. 模板代码 22 1.3.3.2. 关键属性 23 1.3.3.3. 语法说明 23 1.3.3.4. LC-34:在排序数组中查找元素的第一个和最后一个 23 1.3.3.5. LC-658:找到K个最接近的元素 25 1.3.4. 小结 28 1.4. LeetCode中二分查找题目 29 2. 双指针 30 2.1. 快慢指针 31 2.1.1. 什么是快慢指针 31 2.1.2. 快慢指针模板 31 2.1.3. 快慢指针相关题目 32 2.1.3.1. LC-141:链表是否有环 32 2.1.3.2. LC-142:环形链表入口 34 2.1.3.3. LC-876:链表的中间节点 37 2.1.3.4. LC-287:寻找重复数 40 2.2. 滑动窗口 43 2.2.1. 什么是滑动窗口 43 2.1.4. 常见题型 44 2.1.5. 注意事项 45 2.1.6. 滑动窗口模板 45 2.1.7. 滑动窗口相关题目 46 2.1.7.1. LC-3:无重复字符的最长子串 47 2.1.7.2. LC-76:最小覆盖子串 49 2.1.7.3. LC-209:长度最小的子数组 54 2.1.7.4. LC-239:滑动窗口最大值 57 2.1.7.5. LC-395:至少有K个重复字符的最长子串 60 2.1.7.6. LC-567:字符串排列 62 2.1.7.7. LC-904:水果成篮 64 2.1.7.8. LC-424:替换后的最长重复字符 66 2.1.7.9. LC-713:乘积小于K的子数组 67 2.1.7.10. LC-992:K个不同整数的子数组 70 2.3. 左右指针 73 2.3.1. 模板 73 2.3.2. 相关题目 73 2.3.2.1. LC-76:删除倒数第N个节点 74 2.3.2.2. LC-61:旋转链表 76 2.3.2.3. LC-80:删除有序数组中的重复项 79 2.3.2.4. LC-86:分割链表 80 2.3.2.5. LC-438:找到字符串中所有字母的异位词 82 3. 模板 85 2.3.2.6. LC-76:删除倒数第N个节点 85
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚安66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值