LeetCode Day3

516 阅读6分钟

203.移除链表元素

题意:删除链表中等于给定值 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 输出:[]

思路

  1. 虚拟头结点: 在开始时,创建一个新的虚拟头结点dummyHead。这个节点不包含任何实际的数据,但它的next指针指向原始链表的头节点。使用虚拟头结点可以简化边界条件的处理,特别是当需要删除的节点是原始链表的头节点时。
  2. 初始化指针: 初始化一个指针p,并将其指向虚拟头结点。这个指针将用于遍历链表。
  3. 遍历链表: 使用p指针遍历链表。在每一步中,检查pnext节点的值是否等于val
  • 如果等于val,则pnext节点需要被删除。为此,首先保存p->next节点的地址到一个临时指针temp。然后,更新p->next的值为p->next->next,这样就跳过了p->next节点,从而删除了它。最后,释放temp指针所指向的内存。
  • 如果不等于val,则将p指针移动到下一个节点。
  1. 更新头结点: 遍历完成后,将head指针更新为dummyHeadnext指针,因为dummyHead是虚拟头结点,所以真正的链表从dummyHead->next开始。
  2. 释放虚拟头结点的内存: 释放dummyHead所占用的内存。
  3. 返回结果: 返回更新后的head指针。

这种方法的时间复杂度为O(n),其中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* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* p = dummyHead;
        while(p->next!=NULL){
            if(p->next->val==val){
                ListNode *temp=p->next;
                p->next=p->next->next;
                delete temp;
            }
            else p=p->next;
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

707.设计链表

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

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

思路

  1. 节点定义:定义了一个内部结构体LinkedNode,它包含一个整数值val和一个指向下一个节点的指针next
  2. 链表属性:链表有一个虚拟头节点dHead和一个整数size来存储链表的长度。
  3. 初始化:在构造函数中,初始化虚拟头节点和链表长度为0。
  4. get操作:从虚拟头节点的下一个节点开始,遍历链表直到达到指定的index,然后返回该节点的值。
  5. addAtHead操作:创建一个新节点,并将其next指针指向当前的头节点。然后更新虚拟头节点的next指针指向新节点。
  6. addAtTail操作:从虚拟头节点开始,遍历链表直到达到尾节点,然后在尾部添加新节点。
  7. addAtIndex操作:首先检查index是否有效。然后从虚拟头节点开始,遍历链表直到达到指定的index,在该位置插入新节点。
  8. deleteAtIndex操作:首先检查index是否有效。然后从虚拟头节点开始,遍历链表直到达到指定的index,删除该位置的节点。

这种设计使用了虚拟头节点来简化边界条件的处理,特别是当需要操作的节点是原始链表的头节点时。虚拟头节点不存储任何实际的数据,但它的next指针指向原始链表的头节点。

题解

class MyLinkedList {
public:
struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val):val(val), next(nullptr){}
    };
    MyLinkedList() {
        dHead=new LinkedNode(0);
        size=0;
    }   
    int get(int index) {

        if(index<0||index>(size-1)){
            return -1;
        }
        LinkedNode *p=dHead->next;
        for(int i=0;i<index;i++){
            p=p->next;
        }
        return p->val;
    }
    
    void addAtHead(int val) {
        LinkedNode *L=new LinkedNode(val);
        L->next=dHead->next;
        dHead->next=L;
        size++;
    }
    
    void addAtTail(int val) {
        LinkedNode *T=new LinkedNode(val);
        LinkedNode *p=dHead;//dhead->next会出现空指针错误
        while(p->next!=nullptr){
            p=p->next;
        }
        p->next=T;
        size++;
    }
    
    void addAtIndex(int index, int val) {
        if(index>size)return;
        if(index<0)index=0;//如果index小于0,则在头部插入节点
        LinkedNode *I=new LinkedNode(val);
        LinkedNode *p=dHead;
        for(int i=0;i<index;i++){
            p=p->next;
        }
        I->next=p->next;
        p->next=I;
        size++;
    }
    
    void deleteAtIndex(int index) {
        if(index>(size-1)||index<0)return;
        LinkedNode *p=dHead;
        while(index--){
            p=p->next;
        }
        LinkedNode *q=p->next;
        p->next=p->next->next;
        delete q;
        size--;
    }
    private:
    LinkedNode *dHead;
    int size;
};

/**
 * 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->5->NULL 输出: 5->4->3->2->1->NULL

思路

我使用迭代方法反转链表的解法。以下是该解法的简短思路:

  1. 初始化两个指针p指向当前要反转的节点,初始时指向headq指向已经反转的部分的头节点,初始时为nullptr
  2. 遍历链表:当p不为nullptr时,继续执行以下操作:
  • 先保存p的下一个节点到temp,这样我们就不会在反转后丢失对链表的其余部分的引用。
  • 更新pnext指针,使其指向q,从而实现节点的反转。
  • 移动qp的位置,然后移动ptemp的位置,为下一次迭代做准备。
  1. 返回结果:遍历完成后,q指向新的头节点,因此返回q

这种方法的时间复杂度是O(n),其中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 *q=NULL,*p=head;
        while(p){
            ListNode *temp=p->next;
            p->next=q;
            q=p;
            p=temp;
        }
        return q;
    }
};