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

文档讲解:代码随想录(programmercarl.com)

视频讲解:手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibili

帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili

帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili

状态:理解大框架,需要扣小细节

 LeeCode 203 链表移除元素

力扣题目链接(opens new window)

题意:删除链表中等于给定值 val 的所有节点。

示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

示例 2: 输入:head = [], val = 1 输出:[]

示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]

单向链表进行移除元素操作,会有两种情形:移除头结点和移除非头结点

那么对于这两个情形都需要进行考虑:

首先判断移除元素是否是头结点,是的话则进行head=head->next操作,即头指针后移一位操作;非头结点的话将目标节点的上一节点指针指向目标节点的下一节点,即如下图所示:

删除头结点:

删除非头结点:

C++的代码如下:

/**
 * 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 != NULL && head->val == val){
            ListNode* tmp = head;   //临时指针
            head = head->next;
            delete tmp;
        }

        //删除非头结点
        ListNode* cur = head;
        while(cur !=NULL && cur->next != NULL){
            if(cur->next->val == val){
                ListNode* tmp = cur->next;
                cur->next = cur->next->next;
                delete tmp;
            } else {
                cur = cur->next;
            }
        }
        return 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) {
        //增加虚拟头结点
        ListNode* dummyhead = new ListNode(); 
        //自定义构造函数
        // struct ListNode{
        //     int val;  //节点上存储的元素
        //     ListNode *next; //指向下一个节点的指针
        //     ListNode(int x) : val(x),next(NULL) {}  //节点的构造函数
        // };
        dummyhead->next = head;

        //移除节点
        ListNode* cur = dummyhead;
        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 dummyhead->next;
    }
};

 LeeCode 707.设计链表

力扣题目链接(opens new window)

题意:

在链表类中实现这些功能:

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

在前面203 移除元素的题目中,已经理解的链表的基础概念和虚拟头结点的好处,那在这道题当中去进行很好的运用。

class MyLinkedList {
public:
    //定义链表节点结构体
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val),next(nullptr){}
    };

    //初始化链表
    MyLinkedList() {
        _dummyHead = new LinkedNode(0);
        _size = 0;
    }

    //获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
    int get(int index) {
        if( index > (_size - 1) || index < 0 ){
            return -1;
        }
        LinkedNode* cur = _dummyHead->next;
        while(index){   
        //while边界的条件:假设最极端条件下index=0时不执行循环的情况下,完成要求,即条件合格
        //假设index=0,即不进入循环,此时cur指向的是头结点,满足条件
            cur = cur->next;
            index--;   
        }
        return cur->val;
    }
    
    //void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);//创建一个新节点
        newNode->next = _dummyHead->next;  //先将新节点的指针指向_dummyHead->next 
        _dummyHead->next = newNode;
        _size++;
    }
    
    //void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
    void addAtTail(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while(cur->next != nullptr){
            cur = cur->next;
        }
        cur->next = newNode;
        _size++;
    }
    
    //void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。
    void addAtIndex(int index, int val) {
        if(index > _size) return;
        if(index < 0) index = 0;        
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while(index--) {
        //循环边界条件:假设最极端条件index=0,不进入循环,此时cur指向头结点,此时也知道了要插入新节点的位置的上一个节点指针,男足条件
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }
    
    //oid deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
    //删除一个链表元素,一定是找到它的前面的节点以及知道其指针,那么才能执行删除操作
    void deleteAtIndex(int index) {
        if (index >= _size || index < 0) {
            return;
        }
        LinkedNode* cur = _dummyHead;
        while(index--) {//同样考虑最极端条件
            cur = cur ->next;
        }
        LinkedNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        //delete命令指示释放了tmp指针原本所指的那部分内存,
        //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
        //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
        tmp=nullptr;
        _size--;
    }

    // 打印链表
    void printLinkedList() {
        LinkedNode* cur = _dummyHead;
        while (cur->next != nullptr) {
            cout << cur->next->val << " ";
            cur = cur->next;
        }
        cout << endl;
    }

private:
    int _size;
    LinkedNode* _dummyHead;

};

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

 LeeCode 206.反转链表 

力扣题目链接(opens new window)

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

这道题可以通过双指针和递归两种方法来解答

双指针法:先有一个指针cur=head,再在cur之前有一个指针pre,且pre->next  =  NULL

即以图中的遍历方式,实现翻转链表,代码如下:

/**
 * 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* cur = head;
        ListNode* pre = nullptr;
        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* reverse(ListNode* cur, ListNode* pre){
        if(cur == NULL){
            return pre;
        }
        listNode* tmp = cur->next;
        cur->next = pre;
        reverse(tmp,cur); //这里为什么选择这样递归,可以在双指针法中找到答案 
    }
    ListNode* reverseList(ListNode* head) {
        reverse(head, NULL); 
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值