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

203.移除链表元素

思路:

因为列表为单向列表,在做删除操作时需要向后看,否则删除该节点后无法访问前一个节点进行链表的重新连接。遍历到a时判断是否产出b, 如果产出b:
a->b->c : a->c
a->next = b->next

如果不删除b,则进行简单遍历
a = a->next

问题:

自己写的话要注意删除节点内存空间的释放。刚开始的时候以为head是一个指向第一个节点的不包含数据的节点指针,后面才发现题目中head本身也得是一个节点,所以得将头节点分开处理。因为头节点没有前驱节点,对头节点我们需要决定当前节点是否删除,而对于非头节点,我们要决定下一个节点是否删除。

/**
 * 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) {
        //先确定头节点
        while(head != nullptr && head->val == val){
            //delete head;
            head = head->next;
        }

        ListNode* ptr = head;
        //遍历所有有效结点
        //当前ptr决定是否删除后一个节点
        while(ptr != nullptr && ptr->next != nullptr) {
            if (ptr->next->val == val){
                ListNode* remove = ptr->next;
                ptr->next = remove->next;
                //delete remove;
            }
            else {
                ptr = ptr->next;
            }
        }
        return head;
    }
};

#707.设计链表

单链表版本

思路:

单链表只有后驱方向,所以将head设计为Node0的前驱节点(head->next 指向Node0,而不是head指向Node0)比较方便插入和删除,尾部插入时将新节点置于最后一个节点,所以tail指向最后一个实体节点即可。此外,需要注意哪些操作会改变head和tail。每次遍历都是从head开始,所以每次head都会被考虑到,只需要考虑哪些情况需要改变tail:

  1. add_at_head时候链表为空,此时需要更新tail为插入的Node0
  2. add_at_tail
  3. add_at_index or delete_at_index 操作最后一个Node

问题:

1.如何对head这种不指向实体节点的指针进行初始化?
2. 在类定义中定义了NodeList* head,又在类的初始化中定义了NodeList* head = new NodeList()导致内存不对齐
3.Add_at_tail 中只记得将新节点接入链表:
tail->next = newNode; 忘记了tail自身的更新:
tail = tail->next;

易忘知识点总结:

结构体初始化格式:

struct node{
    int data;
    string str;
    char x;
    //注意构造函数最后这里没有分号!
  node() :x(), str(), data(){} //无参数的构造函数数组初始化时调用
  node(int a, string b, char c) :data(a), str(b), x(c){}
}N[10];
class MyLinkedList {
private: 
    struct NodeList{
        int val;
        NodeList* next;
        // NodeList(NodeList* ptr): next(ptr){}
        NodeList(int v, NodeList* ptr): val(v), next(ptr) {}
    };
    NodeList* head;
    NodeList* tail;
    int size;
public:
    MyLinkedList() {
        //head:头节点的前驱指针
        //head->next : Node0
        //tail: 指向最后一个节点
        //add at tail 新节点插入tail->next位置
        //已经初始化过不要再初始化
        // NodeList* head = new NodeList(-1, nullptr);
        // NodeList* tail = head;
        head = new NodeList(-1, nullptr);
        tail = head;
        size = 0;
    }
    
    int get(int index) {
        if (index < 0 || index >= size) return -1;
        NodeList* ptr = head->next;
        for(int i = 0; i < index; ++i){
            //if (ptr == nullptr) return -1;
            ptr = ptr->next;
        }
        return ptr->val;
    }
    
    void addAtHead(int val) {
        NodeList* newNode = new NodeList(val,head->next);
        head->next = newNode;
        //链表为空时既影响head又影响tail
        if (size == 0) {
            tail = newNode;
        }
        ++size;
    }
    
    void addAtTail(int val) {
        NodeList* newNode = new NodeList(val,nullptr);
        tail->next = newNode;
        //tail的移动给忘了
        tail = tail->next;
        ++size;
    }
    
    void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;
        NodeList* ptr = head;
        for (int i = 0; i < index; ++i){
            ptr = ptr->next;
        }
        NodeList* newNode = new NodeList(val, ptr->next);
        ptr->next = newNode;
        //影响head,为第一个节点,ptr从head遍历起包括该情况
        //影响tail,尾部插入,当index == size时
        if(index == size){
            tail = newNode;
        }
        ++ size;
    }
    
    void deleteAtIndex(int index) {
        if (index< 0 || index >= size) return;
        NodeList* ptr = head;
        for (int i = 0; i < index; ++i){
            ptr = ptr->next;
        }
        NodeList* remove = ptr->next;
        ptr->next = remove->next;
        // delete remove;
        if (size == index + 1) {
            tail  = ptr;
        }
        --size;
    }
};

/**
 * 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);
 */


双向链表

思路

跟单链表类似,不过链接链表时要同时考虑前驱和后驱的关系,只需要注意连接顺序,比如在先赋值head->next->prev再赋值head->next

问题

head,tail初始化过程中:
head = new NodeList(-1, nullptr, tail);
tail = new NodeList(-1, head, nullptr);
head初始化时用了初始化的变量tail,这种“小错误”很容易被忽略,而且很难在自己debug过程发现,需要多加注意。

class MyLinkedList {
private: 
    struct NodeList{
        int val;
        NodeList* prev;
        NodeList* next;
        NodeList(NodeList* ptr_prev, NodeList* ptr_next): prev(ptr_prev), next(ptr_next){}
        NodeList(int v, NodeList* ptr_prev, NodeList* ptr_next): val(v), prev(ptr_prev), next(ptr_next) {}
    };
    NodeList* head;
    NodeList* tail;
    int size;
public:
    MyLinkedList() {
        //head:头节点的前驱指针
        //tail: 尾节点的后驱节点
        // tail 还没初始化!!!!!!!!
        // head = new NodeList(-1, nullptr, tail);
        // tail = new NodeList(-1, head, nullptr);
        // head = new NodeList(-1, nullptr, nullptr);
        // tail = new NodeList(-1, nullptr, nullptr);
        head = new NodeList(nullptr, nullptr);
        tail = new NodeList(nullptr, nullptr);
        head->next = tail;
        tail->prev = head;
        size = 0;
    }
    
    int get(int index) {
        if (index < 0 || index >= size) return -1;
        NodeList* ptr = head->next;
        for(int i = 0; i < index; ++i){
            //if (ptr == nullptr) return -1;
            ptr = ptr->next;
        }
        return ptr->val;
    }
    
    void addAtHead(int val) {
        NodeList* newNode = new NodeList(val, head, head->next);
        head->next->prev = newNode;
        head->next = newNode;
        ++size;
    }
    
    void addAtTail(int val) {
        NodeList* newNode = new NodeList(val, tail->prev, tail);
        tail->prev->next = newNode;
        tail->prev = newNode;
        //tail的移动给忘了
        ++size;
    }
    
    void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;
        NodeList* ptr = head;
        for (int i = 0; i < index; ++i){
            ptr = ptr->next;
        }
        NodeList* newNode = new NodeList(val, ptr, ptr->next);
        ptr->next->prev = newNode;
        ptr->next = newNode;
        ++ size;
    }
    
    void deleteAtIndex(int index) {
        if (index< 0 || index >= size) return;
        NodeList* ptr = head;
        for (int i = 0; i < index; ++i){
            ptr = ptr->next;
        }
        NodeList* remove = ptr->next;
        remove->prev->next = remove->next;
        remove->next->prev = remove->prev;
        // delete remove;
        --size;
    }
};

206. 反转链表

思路

思路非常简单,反转链表的过程其实就是模拟了一个入栈出栈的过程,直接调用STL的stack即可完成

问题

  1. 反转完之后的链表的头节点发生了变化,我们需要重新赋值,这里我们用counter来判断哪个哪个是头节点。
  2. stack.pop()是个void返回类型的函数,需要栈顶元素要用stack.top()
class Solution {
public:
    //可以用stack实现
    //是一个栈的过程,后进先出
    ListNode* reverseList(ListNode* head) {
        stack<ListNode*> s;
        ListNode* ptr = head;
        int count = 0;
        while (ptr != nullptr){
            s.push(ptr);
            ptr = ptr->next;
        }
        while(!s.empty()){
            ListNode* current = s.top();
            //头节点
            if (++count == 1) head = s.top();
            s.pop();
            //尾节点指向nullptr
            current->next = s.empty() ? nullptr : s.top();
        }
        return head;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值