代码随想录Day3

845 阅读4分钟

203.移除链表元素

题目链接: 203.移除链表元素

文章讲解:203.移除链表元素

视频讲解:链表基础操作| LeetCode:203.移除链表元素

思路:引入虚拟头结点便于统一计算步骤,简化算法结构。

不引入虚拟头结点

// 直接在原链表上操作,头结点和其他结点的操作不一致,需要单独处理
    // c++记得处理释放删除结点所占用内存,避免内存泄漏
    // 时间复杂度O(n)、空间复杂度O(1)
    ListNode* removeElements1(ListNode* head, int val) {
        // 处理头结点,注意用while而不是if(可能新头结点也符合删除条件)
        while (head != NULL && head->val == val) {
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
        }
        // 处理其他结点
        // 当前指针从head出发
        ListNode* cur = head;
        // 检查cur指针的下一个结点是否符合条件,符合则删除,否则向后移动
        // 末尾结点的判断条件为cur->next == NULL
        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;
    }

不引入虚拟头结点

// 设置虚拟头结点dummyHead,以此统一头结点和其他结点的操作方式,简化代码逻辑
    // 时间复杂度O(n)、空间复杂度O(1)
    ListNode* removeElements2(ListNode* head, int val) {
        // 用dummyHead替代原始头结点
        ListNode* dummyHead = new ListNode(0);
        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;
            }
        }
        // 原始的head可能被删除了
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }

707.设计链表

题目链接: 707.设计链表

文章讲解:707.设计链表

视频讲解:707.设计链表

思路:本题难点居然是在于index的取值问题,搞了半天很坑,题本身就是抄书级别。

class MyLinkedList {
public:
    // 定义内部类:链表结点结构体
    struct LinkedNode{
        int val;
        LinkedNode* next;
        // 构造函数
        LinkedNode(int x)
        : val(x)
        , next(nullptr){
            cout << "LinkedNode(int x)" << endl;
        }
    };
    //无参构造函数,初始化链表,创建虚拟头结点
    MyLinkedList() {
        _dummyHead = new LinkedNode(0);
        _size = 0;
    }
    // 获取下标为index的结点值,非法返回-1,头结点的index为0
    int get(int index) {
        // 先处理index的非法取值
        if (index > (_size - 1) || index < 0) {
            return -1;
        }
        LinkedNode* cur = _dummyHead->next;
        // 如果是--index 可能会出现负数,导致死循环
        while(index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    // 头插法
    void addAtHead(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }
    // 尾插法
    void addAtTail(int val) {
        LinkedNode* newNode = new LinkedNode(val);
        LinkedNode* cur = _dummyHead;
        while (cur->next != nullptr) {
            cur = cur->next;
        }
        cur->next = newNode;
        _size++;
    }
    // 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。
    // index为0,则变为新头结点
    // index大于链表长度,则不插入,返回空
    // index小于0,则在头部插入结点
    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--) {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }
    // 删除第index个结点
    // index大于链表的长度,返回空
    // index是从0开始的
    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;
        // 避免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;
};

206.反转链表

题目链接: 206.反转链表

文章讲解:206.反转链表

视频讲解:206.反转链表

思路:来到典中典之翻转链表,递归法确实难以直接想到,建议先写出双指针法,再根据其推出递归解法。建议观看carl的讲解视频,精髓! 好图直接引:

206.翻转链表.gif

双指针法

// 双指针法,pre指针和cur指针一前一后,
    // 在原链表上只改变next指针方向
    // 时间复杂度: O(n)
    // 空间复杂度: O(1)
    ListNode* reverseList1(ListNode* head) {
        ListNode* pre = NULL;
        ListNode* cur = head;
        // tmp记录当前结点的next,因为next换向后,会丢失下一个结点
        ListNode* tmp;
        // 循环到末尾结点
        while (cur) {
            tmp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }

递归解法

// 递归法,思想和双指针法相同
    // 时间复杂度: O(n), 要递归处理链表的每个节点
    // 空间复杂度: O(n), 递归调用了 n 层栈空间
    // 递归函数
    ListNode* reverse(ListNode* pre, ListNode* cur) {
        // 递归的出口:pre指向新头结点(原末尾结点),cur指向空
        if (cur == NULL) {
            // 返回新头结点
            return pre;
        }
        ListNode* tmp = cur->next;
        cur->next = pre;
        // 递归的入口:类比于双指针法的
        // pre = cur;
        // cur = temp;
        return reverse(cur, tmp);
    }
    ListNode* reverseList2(ListNode* head) {
        // reverse()返回的是翻转后的头结点
        return reverse(NULL, head);
    }

反向递归解法

// 从后向前递归
    ```
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 边缘条件判断
        if(head == NULL) return NULL;
        if (head->next == NULL) return head;
        
        // 递归调用,翻转第二个节点开始往后的链表
        ListNode *last = reverseList(head->next);
        // 翻转头节点与第二个节点的指向
        head->next->next = head;
        // 此时的 head 节点为尾节点,next 需要指向 NULL
        head->next = NULL;
        return last;
    }
};