LeetCode | C++ 203.移除链表元素 206.反转链表 707.设计链表

目录

  • 203.移除链表元素
    • 第一种方法不设置虚拟头结点
    • 第二种方法设置虚拟头结点
  • 206.反转链表
    • 第一种方法双指针法
    • 第二种方法递归法
  • 707.设计链表

203.移除链表元素

203题目链接

  • 对于单链表来说,想要删除某个节点,需要通过指向它的上一个节点,这是单链表的特性。
  • 为啥有了虚拟头结点,还需要再定义一个 current 节点指向虚拟头结点,是因为虚拟头结点是不能改变的,对于本题来说,返回的是它的下一个节点。current 临时指针 是用来遍历整个链表的,如果使用头结点进行遍历的话,头结点是在不断发生改变的,最后就无法返回原先列表的头结点,所以说头结点的指针是不能改的。
  • 此外,对比了是否设置虚拟头结点两种方法。可以发现,设置虚拟头结点,只需要考虑一种情况,更易分析理解。一般建议设置虚拟头节点。

第一种方法不设置虚拟头结点

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 首先针对头结点情况
        while (head != NULL && head->val == val) {
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
        }
        // 针对后面的一般情况
        ListNode* current = head;
        // 注意,这里需要加上current != NULL 这种情况
        // 因为有可能链表里面所有值都等于 val,head 为 NULL
        while (current != NULL && current->next != NULL) {
            if (current->next->val == val) {
                ListNode* tmp = current->next;
                current->next = current->next->next;
                delete tmp;
            } else {
                current = current->next;
            }
        }
        return head;
    }
};

第二种方法设置虚拟头结点

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 设置虚拟头结点,统一处理格式
        ListNode* virtualHead = new ListNode(0);
        virtualHead->next = head;
        // 定义current 为遍历指针
        ListNode* current = virtualHead;
        while (current->next != NULL) {
            if (current->next->val == val) {
                ListNode* tmp = current->next;
                current->next = current->next->next;
                delete tmp;
            } else {
                current = current->next;
            }
        }
        head = virtualHead->next;
        delete virtualHead;
        return head;
    }
};

206.反转链表

206题目链接

  • 对于单链表,想要访问里面的元素,只能通过上一个节点。所以要想实现链表内元素之间的交换,则需要在靠后的一个节点交换之前,把该节点的next 值提前保存下来,思路类似与交换两个数的值。
  • 对于递归写法的话,尽量先把双指针法理解清楚,然后在写。递归方法可以理解为双指针法循环的另一种写法。首先需要把current 的下一个存下来,然后再去交换。

第一种方法双指针法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 双指针解法
        ListNode* before = NULL;
        ListNode* current = head;
        ListNode* tmp;
        while (current != NULL) {
            tmp = current->next;
            current->next = before;
            before = current;
            current = tmp;     
        }
        return before;
    }
};

第二种方法递归法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 递归解法
        ListNode* before = NULL;
        ListNode* current = head;
        return reverse(before, current);
    }
    ListNode* reverse(ListNode* before, ListNode* current) {
        if (current == NULL) return before;
        ListNode* tmp = current->next;
        current->next = before;
        // 递归 只是循环赋值 的另一种写法
        // before = current;
        // current = tmp;
        return reverse(current, tmp);
    }
};

707.设计链表

707题目链接

  • 在处理链表时,如果比较容易乱,可以通过画草图的方式来梳理思路。
 class MyLinkedList {
public:
    // 首先定义链表节点结构体
    struct ListNode {
        int val;
        ListNode *next;
        ListNode(int x) : val(x), next(nullptr) {}
    };

    MyLinkedList() {
        _virtualHead = new ListNode(0);
        _size = 0;
    }
    
    int get(int index) {
        if (index > (_size - 1) || index < 0) {
            return -1;
        }
        ListNode* current = _virtualHead->next;
        while (index-- != 0) {
            current = current->next;
        }
        return current->val;

    }
    
    void addAtHead(int val) {
        ListNode* addHead = new ListNode(val);
        addHead->next = _virtualHead->next;
        _virtualHead->next = addHead;
        _size++;
    }
    
    void addAtTail(int val) {
        ListNode* addTail = new ListNode(val);
        ListNode* current = _virtualHead;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = addTail;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > _size) return;
        if (index < 0) index = 0;
        ListNode* current = _virtualHead;
        ListNode* addIndex = new ListNode(val);
        while (index-- != 0) {
            current = current->next;
        }  
        addIndex->next = current->next;
        current->next = addIndex;
        _size++;   
    }
    
    void deleteAtIndex(int index) {
        // 在这里需要注意的是 链表的 index 是从0开始的
        if (index >= _size || index < 0) return;
        ListNode* current = _virtualHead;
        while (index-- != 0) {
            current = current->next;
        }
      
        ListNode* tmp = current->next;
        current->next = current->next->next;
        delete tmp;
        //delete命令指示释放了tmp指针原本所指的那部分内存,
        //被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
        //如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
        //如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
        tmp=nullptr;
        _size--;
    }

private:
    int _size;
    ListNode* _virtualHead;
};

你可能感兴趣的:(#,C++,链表,leetcode,c++)