代码随想录day5-链表(1)

代码随想录-day5-链表(1)

从今天开始,我们将进入链表的学习,里面的知识很多。

1、LeetCode 203 移除链表元素

题目分析:
移除元素(删除结点)是链表基本操作中最常见的一种操作。
一般流程就是:定位到需要删除的结点的上一个结点,这里为了表示方便,我们记作pre,删除的操作就是一句话pre->next = pre->next->next。当然,对于c++而言,我们还需要将需要删除的结点删除进行内存的释放。
这就牵涉到一个麻烦的点:如果删除的是头结点,但是头结点之前没有结点,怎么办?
方案1:单独处理头结点
方案2:设置虚拟头结点dummyHead,让dummyHead的下一个结点指向head,就可以对左右元素进行统一的操作了。 这种需要处理某个结点,需要前面结点进行辅助的情况,都可以设置dummyHead来进行操作。特别是题目中涉及删除的情况。本题就是一道典型的设计删除,可以使用虚拟头结点的情况的题目。

题目解答:

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead =  new ListNode(0, head);  // 设置虚拟头结点,并且下一个结点时head
        ListNode* cur = dummyHead;  // 当前的工作结点
        while (cur->next) {
            if (cur->next->val == val) {
                ListNode* temp = cur->next;  // 存起来,等会儿释放
                cur->next = cur->next->next;  // 删除操作
                delete temp;
            } 
            else {
                cur = cur->next;
            }
        }
        ListNode* ans = dummyHead->next;
        delete dummyHead;  // 虚拟的头结点也需要删除
        return ans;  
    }
};

解答如上,非常简单。
有一个问题,这个循环里里面的条件,到底是cur != nullptr 还是cur->next != nullptr呢?
我们只要关注我们需要操作的就行了!
本题,一开始cur是虚拟结点,那么cur->next就是我们需要操作的结点,所以while中的条件就是cur->next。
如果我们为了计算链表的长度:

int calculateLinkLen(ListNode* head) {
	ListNode* cur = head;
	int len = 0;
	while (cur) {
		cur = cur->next;
		len++;
	}
	return len;
}

这里cur一开始指向的是head,也就是我们需要操作的数,那么循环的判断条件就是cur。
另外,我们有时候指针需要移动两位,一样的道理,此时循环里面的就是
cur && cur->next, 注意这里顺序不能颠倒,否则当cur为nullptr,cur->next就会报错。

2、LeetCode 707 设计链表

题目分析:
本题非常重要,它考察了我们对链表最最最基本的几种操作,需要反复写,反复熟悉,这是基础中的基础。

题目解答:

class myListNode {
public:
    int val;
    myListNode* next;
    myListNode(int x): val(x), next(nullptr) {}; 
};

class MyLinkedList {
public:
    MyLinkedList() {
        size = 0;
        head = nullptr;
    }
    
    int get(int index) {
        if (index < 0 || index > size - 1) {
            return -1;  // 如果索引无效,返回-1
        }
        int pos = 0;
        myListNode* cur = head;
        while (cur) {
            if (pos == index) {
                return cur->val;
            }
            else {
                cur = cur->next;
            }
        }
        return -1;
    }
    
    void addAtHead(int val) {
        myListNode* newNode = new myListNode(val);
        myListNode* oldNode = head;
        newNode->next = oldNode;
        head = newNode;
        size++;
    }
    
    void addAtTail(int val) {
        myListNode* newTail = new myListNode(val);
        myListNode* dummyHead = new myListNode(0);
        dummyHead->next = head;  // 设置一个虚拟头结点
        myListNode* cur = dummyHead;
        while (cur->next) {
            cur = cur->next;
        }
        // 此时的cur指向的是最后一个结点
        cur->next = newTail;
        newTail->next = nullptr;
        head = dummyHead->next;
        delete dummyHead;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index < 0) {
            addAtHead(val);  // 小于0,插入头部结点
        }
        else if (index > size - 1) {
            return;  // 大于链表长度,不会插入结点
        }
        else {
            int pos = 0;
            // 设置虚拟头结点
            myListNode* dummyHead = new myListNode(0);
            dummyHead->next = head;
            myListNode* cur = dummyHead;
            myListNode* newNode = new myListNode(val);
            while (cur->next) {
                if (pos == index) {
                    // 此时cur就是需要插入的元素的上一个元素
                    myListNode* node = cur->next;
                    cur->next = newNode;
                    newNode->next = node;
                    size++;
                    // 虚拟结点的删除
                    head = dummyHead->next;
                    delete dummyHead;
                    break;
                }
                else {
                    cur = cur->next;
                    pos++;
                }
            }
        }
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1) {
            return;  // 索引无效
        }
        myListNode* dummyHead = new myListNode(0);  // 设置虚拟头结点
        dummyHead->next = head;
        int pos = 0;
        myListNode* cur = dummyHead;
        while (cur->next) {
            if (pos == index) {
                // 此时cur指向的是需要删除的结点的上一个结点
                myListNode* temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
                head = dummyHead->next;
                delete dummyHead;
                size--;
                break;  // 满足条件跳出循环
            }
            else {
                cur = cur->next;
                pos++;
            }
        }
    }

private:
    int size;  // 链表的长度
    myListNode* head;  //  链表的头结点
};

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

3、LeetCode 206 反转链表

题目分析:
反转链表是典型的使用双指针进行操作的题目,就是那种需要遍历整个链表的题目就是很多时候用到的都是双指针。
反转过后,以前的头结点肯定作为最后一个结点,指向nullptr,所以我们要一开始就弄一个虚拟结点,让它指向nullptr。
我们定义两个指针,fast指针负责遍历链表,slow负责存结果,最终返回slow即可。

题目解答:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* fast = head;
        ListNode* slow = nullptr;  // 因为反转后的最后一个结点是nullptr,所以开始就让slow指向nullptr
        while (fast) {
            ListNode* temp = fast->next;  // 保存fast的下一个结点
            fast->next = slow;  // 反转过后,slow就是fast的next
            slow = fast;  // 结点更新
            fast = temp;
        }
        return slow;
    }
};

4、LeetCode 24 反转链表

题目分析:
本题一开始思考还是有点儿困难,本题使用了虚拟头结点的方法后简单很多了。
本题还是满足虚拟头结点的使用原则,需要操作cur的next以及next->next。如果没有虚拟头结点的话,这里的交换不太容易实现。

题目解答:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0, head);  // 虚拟头结点
        ListNode* cur = dummyHead;
        while (cur->next && cur->next->next ) {  // 由于需要考虑后面的两个结点,那么需要进行这样的判定
            ListNode* temp1 = cur->next;  // 存储cur的next
            ListNode* temp2 = cur->next->next->next;  // 存储下下下个结点
            cur->next = cur->next->next;  // 结点更新,这里一定要想明白
            cur->next->next = temp1;
            temp1->next = temp2;
            cur = temp1;  // 当前工作结点的更新
        }
        ListNode* ans = dummyHead->next;  // 虚拟头结点释放
        delete dummyHead;
        return ans;
    }
};

注意虚拟头结点的使用,当遇到困难了,想想双指针法,以及虚拟头结点的方法,可以解决很多问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值