首页 > 编程语言 >代码随想录算法训练营第三天 链表 | 203.移除链表元素 | 707.设计链表 | 206.反转链表

代码随想录算法训练营第三天 链表 | 203.移除链表元素 | 707.设计链表 | 206.反转链表

时间:2023-01-14 01:55:25浏览次数:37  
标签:ListNode cur val int 随想录 next 链表 移除

链表

链表是一种线性结构,不同于使用连续空间的储存结构(vector,数组,string等),链表在内存中的储存方式并不是连续分布的,分配散乱,分配机制取决于操作系统的内存管理。

C++中定义链表节点的方式

// 单链表
struct ListNode {
    int val;  // 节点上存储的元素
    ListNode *next;  // 指向下一个节点的指针
    ListNode(int x) : val(x), next(NULL) {}  // 节点的构造函数
};

链表中删除和添加元素都是通过改变相邻节点指针指向的节点来完成,时间复杂度是\(O(1)\)。但是链表并不支持随机访问,查询复杂度是\(O(n)\)。所有链表适用于数据量不固定,频繁增删,较少查询的情况。

链表操作 lc203 移除链表元素

使用虚拟头指针来统一删除操作,避免对头指针为目标值的情况特殊处理

/**
 * Definition for singly-linked list.
 * 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 Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummy_head = new ListNode(0,head);
        ListNode* cur = dummy_head;
        while(cur->next != NULL){
            if (cur->next->val == val){
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            }
            else{
                cur = cur->next;
            }
        }
        return dummy_head->next;
    }
};

链表操作 lc707 设计链表

这道题基本包含了所有链表的操作。难点在于对链表增删查的理解,删改时操作的顺序,cur当前指针应该从虚拟头指针开始还是从头指针开始。

目前总结下来,使用虚拟头指针时

  1. 增删时,先将新节点的next指好,再将目标位置前节点的next指向新节点,以防找不到新节点指向的位置
  2. 增删时,需要保留目标位置前一个位置的节点信息,所以cur当前指针应该从虚拟头指针开始,cur->next 为我们想要删除或增加的位置
class MyLinkedList {
public:
    //链表节点结构体
    struct LinkedNode{
        int val;
        LinkedNode* next;
        LinkedNode(int val) : val(val), next(nullptr){}
    };
    MyLinkedList() {
        _size = 0;
        _dummy_head = new LinkedNode(0);
    }
    
    int get(int index) {
        if (index < 0 || index > _size-1){
            return -1;
        }
        LinkedNode* cur = _dummy_head->next;
        while(index--){
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        LinkedNode* newnode = new LinkedNode(val);
        newnode->next = _dummy_head->next;
        _dummy_head->next = newnode;
        _size++;
    }
    
    void addAtTail(int val) {
        LinkedNode* newnode = new LinkedNode(val);
        LinkedNode* cur = _dummy_head;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        cur->next = newnode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > _size) return;
        if (index < 0) index = 0; 
        LinkedNode* cur = _dummy_head;
        LinkedNode* newnode = new LinkedNode(val);
        while(index--){
            cur = cur->next;
        }
        newnode->next = cur->next;
        cur->next = newnode;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index > _size-1){
            return;
        }
        LinkedNode* cur = _dummy_head;
        while(index--){
            cur = cur->next;
        }
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        _size--;
    }
private: //private 中的数据成员命名时前面加了'_',用来与其他变量进行区分。
    int _size;
    LinkedNode* _dummy_head;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

双指针、递归 lc206 反转链表

双指针

需要一个临时指针来存放原本的cur->next,迭代赋值时要注意先给pre赋值,不然会丢失pre应该迭代到的位置

/**
 * Definition for singly-linked list.
 * 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 Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur){
            ListNode* tmp =  cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
};

递归

就是双指针直接改的

/**
 * Definition for singly-linked list.
 * 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 Solution {
public:
    ListNode* reverseList(ListNode* head) {
        return reverse(nullptr,head);
    }
    ListNode* reverse(ListNode* pre, ListNode* cur){
        if (cur == nullptr) return pre;
        ListNode* tmp = cur->next;
        cur->next = pre;
        return reverse(cur,tmp);
    }
};

Extra

leetcode评论区里的写法,时间空间复杂度都是\(O(n)\),单纯感觉思路有点妙

/**
 * Definition for singly-linked list.
 * 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 Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* ans = nullptr;
        for (ListNode* i=head;i!=nullptr;i=i->next){
            ans = new ListNode(i->val,ans);
        }
        return ans;
    }
};

标签:ListNode,cur,val,int,随想录,next,链表,移除
From: https://www.cnblogs.com/frozenwaxberry/p/17051082.html

相关文章