算法训练 day03 | 203.移除链表元素 707.设计链表 206.反转链表

203.移除链表元素

题目链接:移除链表元素

视频讲解:手把手带你学会操作链表

此题就是熟悉链表的操作,主要学会了使用虚拟头结点的方法,方便对链表进行增、删。

首先要熟悉定义链表的方式:

struct Listnode {
     int val; // 节点存储的值
     Listnode *next; // 指向下一个节点的指针
     Listnode(int x) : val(x), next(NULL) {} // 节点的构造函数
}

C++是有默认的构造函数的,但用默认构造函数初始化时,不能直接给变量赋值。

通过自己定义构造函数初始化节点:

ListNode* head = new ListNode(5);

使用默认构造函数初始化节点:

ListNode* head = new ListNode();
head->val = 5;

直接使用原来的链表来进行移除节点操作

直接使用原来链表操作,需要把头结点单独考虑,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点。若要移除头结点,只需把头结点向后移动一位即可,不过C++移动后要自己删除。把head赋给一个临时指针,在进行移位,最后删除临时指针。如果不创建临时指针,head移位后指向的位置变了,不能找到原来的头结点。

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 删除头结点
        while (head != NULL && head->val == val)  //用while,可能head一直等于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 //不等于val,cur向后移一位继续判断
            {
                cur = cur->next;
            }
        }
        return head;
    }
};

设置一个虚拟头结点在进行移除节点操作

虚拟头结点就是在head前面再加一个节点,这样就不用单独考虑head结点了,此时head不再是链表的头结点。

//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 虚拟头结点指向头结点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;
    }
};

707.设计链表

题目链接:设计链表

视频讲解:手把手带你学会操作链表

这题做的时间有点长,主要还是对构造函数不够了解。

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

    // LinkNode* dummyHead = new LinkNode(0);
    // int m_size = 0;

    MyLinkedList()
    {
        dummyHead = new LinkNode(0);
        m_size = 0;
    }
    
    int get(int index) {
        if (index < 0 || index > (m_size - 1))
        {
            return -1;
        }
        LinkNode* cur = dummyHead->next;
        while (index--) // 让cur定位到index的位置,才能取值
        {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {  // 没有改变原来链表的节点的值,不用定义一个cur
        LinkNode* newNode = new LinkNode(val);
        newNode->next = dummyHead->next;
        dummyHead->next = newNode;
        m_size++;
    }
    
    void addAtTail(int val) {
        LinkNode* newNode = new LinkNode(val);
        LinkNode* cur = dummyHead;
        while (cur->next != NULL) // 自己想用size--判断,有点儿麻烦
        {
            cur = cur->next;
        }
        cur->next = newNode; // newNode->next本身就为空
        m_size++;
    }
    
    void addAtIndex(int index, int val) {
        //先判断index是否有效
        if (index < 0) index = 0; // index小于0,则在头部插入节点
        if (index > m_size) return; // index大于链表长度,返回空
        // index等于链表长度,表示在链表尾部插入一个节点,下面可以满足,不用单独讨论 

        LinkNode* newNode = new LinkNode(val);
        LinkNode* cur = dummyHead;
        while (index--) // val插入下标为index节点之前,就要让cur->next指向index
        {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode; //这两步不能顺序不能弄反了,如果先给cur->next赋值,再给newNode->next赋值时cur->next的值就变了
        m_size++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index >= m_size) return;

        LinkNode* cur = dummyHead;
        while(index--) // 删除index,就要找到index前面的节点
        {
            cur = cur->next;
        }
        LinkNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        // delete命令只是释放了tmp原本所指向的那部分内存,delete后tmp地址并非就是空的,而是随机的
        // 要把地址置空,不然tmp会成为野指针,之后可能被其他程序使用
        tmp = nullptr;
        m_size--;
    }

private:
    int m_size;
    LinkNode* 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);
 */

206.反转链表

题目链接:反转链表

视频讲解:帮你拿下反转链表

双指针法

主要理解双指针法,每次只改变两个指针之间的指向,让一个箭头反转后,两个指针同时向前移动一位,反转下一个。

初始链表:1→2→3→4→NULL

NULL1→2→3→4→NULL (链表头节点前面加空指针pre,加粗表示pre和cur的位置,pre始终在cur前一位)

NULL1→2→3→4→NULL

②NULL←12→3→4→NULL

③NULL←1←23→4→NULL

④NULL←1←2→34→NULL

⑤NULL←1←2←3←4NULL (循环的终止条件是cur = NULL,才能保证返回的pre是反转后的头节点)

(又把tmp声明在while循环里导致不能更新)

//时间复杂度:O(n)
//空间复杂度:O(1)
/**
 * 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 = NULL; //为了让head变成尾节点后head->next指向空
        ListNode* cur = head;
        ListNode* tmp;
        while (cur) //移动到pre是原来尾节点,cur是空的时候结束
        {
            tmp = cur->next; // 保存cur下一个节点,不然接下来改变cur->next后找不到原来的值了
            cur->next = pre; //把两个指针的箭头翻转
            pre = cur;
            cur = tmp; //反转后两指针都向前移动一位,下一步循环继续翻转
        }
        return pre; //此时pre是头结点
    }
};

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值