代码随想录Day3| 203.移除链表元素707.设计链表206.反转链表

代码随想录Day3| 203.移除链表元素707.设计链表206.反转链表

链表

链表的存储在内存中不是连续的
链表的定义
struct ListNode
{
//存储元素
int val;
//下一个节点位置
ListNode* next;
//构造函数
ListNode(int x, ListNode* next):val(x),next(next){}
};
链表节点的删除就和数组不一样了,数组的删除元素相当于覆盖元素,而链表的删除是直接将节点的指向改变,这样通过被删除的节点就不会通过这个链表寻找到,但他仍然存在在链表中,所以需要手动释放。

移除链表元素

文档讲解:代码随想录
视频讲解:手把手带你学会操作链表 | LeetCode:203.移除链表元素
状态:√

  1. 思路
    删除等于val的节点,相当于将指向这个节点的指针改变指向,指向这个节点后面第一个不等于val的节点。
    为了避免第一个节点就是val节点而带来的单独判断,所以采用一个虚拟头结点,即该节点指向原始链表的头部。
    循环终止条件:我们要判断的是当前节点的下一个节点值是否等于val,因为我们在循环中确定了当前节点的值不为val,所以循环终止条件就是val->next!=nullptr
    循环内部的处理逻辑是,当我们发现下一个节点的值为val时,进行删除操作,当不是val时,进行节点移动操作。
    注意由于我们的第一个节点的值可能是val所以,我们最后的返回结果不能是head,虽然我们返回的First->next在定义中是head。
/**
 * 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) {
        //使用虚拟头结点,处理第一个元素就是val的情况
        //用于返回数据
        ListNode* First = new ListNode(0);
        First->next = head;
        //用于处理数据
        ListNode* cur = First;

        //注意循环判断条件
        while(cur->next)
        {
            if(cur->next->val == val)
            {
                //存储要删除的节点
                ListNode* temp = cur->next;
                //改变指向
                cur->next = temp->next;
                //删除该节点
                delete temp;
            }
            //只有当我们next不等于val的时候才进行节点移动,而不是每个循环都要进行节点移动
            else
            {
                cur = cur->next;
            }
            //cout << head;          
        }
        return First->next;
    }
};

时间复杂度:对于每个节点只处理了1次,所以是O(n)

设计链表

文档讲解:代码随想录
视频讲解:帮你把链表操作学个通透!LeetCode:707.设计链表
状态:容易忘记每次增删之后长度要进行变化

  1. 思路
    本题就是对链表的一个模拟,
    1. 首先要定义一个结构体来表示一个节点单元
    2. 其次在类中要定义变量,即头结点和链表长度,在构造函数中初始化
    3. 增加需要定义一个节点用来接收值和处理其指针指向,增加后不要删除,删除节点过程需要定义一个临时变量来存储要删除的节点,使用完之后要删除
    4. 每次增加和删除之后,要记得对长度进行修改
    5. 构造函数中定义的是虚拟头结点,在对链表头部添加元素时效果明显
class MyLinkedList {

public:

    //一个链表节点
    struct ListNode
    {
        int val;
        ListNode* next;
        ListNode(int x):val(x),next(nullptr){}
        ListNode(int x , ListNode* n):val(x),next(n){}
    };

    MyLinkedList() {
        //虚拟头结点
        First = new ListNode(0);
        //此时链表长度
        size = 0;
    }
    
    int get(int index) {
        if(index<0 || index >= size)
        {
            return -1;
        }
        else
        {
            int i = index;
            ListNode* cur = First;
            while(i>=0)
            {
                cur = cur->next;
                i--;
                //cout << cur->val;
            }
            return cur->val;
        }
    }
    //void addAtHead(int val){}
    void addAtHead(int val) {
        ListNode* AddNew = new ListNode(val,First->next);
        First->next = AddNew;
        size++;
    }
    //void addAtTail(int val){}
    void addAtTail(int val) {
        ListNode* cur = First;
        while(cur->next)
        {
            cur = cur->next;
        }
        ListNode* AddNew = new ListNode(val);
        cur->next = AddNew;
        size++;
    }
    //void addAtIndex(int index, int val){}
    void addAtIndex(int index, int val) {
        if(index == size)
        {
            addAtTail(val);
        }
        else if(index > size)
        {
            return;
        }
        else if(index <= 0)
        {
            addAtHead(val);
        }
        else
        {
            ListNode* cur = First;
            int i = index;
            while(i>0)
            {
                cur = cur->next;
                i--;
            }
            ListNode* AddNew = new ListNode(val,cur->next);
            cur->next = AddNew;
            size++;
        }
    }
 //   void deleteAtIndex(int index){}
    void deleteAtIndex(int index) {
        if(index<0 || index >=size)
        {
            return ;
        }
        else
        {
            ListNode* cur = First;
            int i = index;
            while(i> 0)
            {
                cur = cur->next;
                i--;
            }
            ListNode* temp = cur->next;
            cur->next = temp->next;
            delete temp;
            size--;
        }
    }

private:
    ListNode* First;
    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);
 */

翻转链表

文档讲解: 代码随想录
视频讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法
状态:√

  1. 采用虚拟头结点
    虚拟头结点的作用是翻转之后,控制原始的第一个元素指向Null,然后剩下的方法和双指针类似了。直接使用给的头结点返回,再定义一个初始指向头结点下一个节点的指针,通过判断这个指针是否为null,来终止循环。
    这个方法还需要判断初始链表是否为空
/**
 * 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* First = new ListNode(0,head);
       if(head)
       {
       		ListNode* cur = head->next;
       		while(cur)
       		{
       			ListNode* temp = cur->next;
       			cur->next = head;
       			head = cur;
       			cur = temp;
       			temp = nullptr;
       			delete temp;
       		}
       		First->next->next = nullptr;
       }
       return head;
    }
};

temp的定义可以拿到循环外
2. 双指针
直接定义一个返回指针初始为空指针,再定义一个移动判断指针初始为头节点,每次移动利用临时变量存储移动指针的下一个节点,然后改变其的指向,使其指向前一个节点就是返回指针此时所在的位置,之后在进行赋值操作就可以了。
这里要特别注意指针的删除:delete指针只是释放指针的内存空间,而不是指针本身。称为野指针,如果后续进行new一个新的指针,编译器可能会把内存分配到这个指针指向的内存,就出现了两个指针指向同一块内存的操作。所以我们在删除之后,需要将指针变为空指针。在力扣中会出现这样的报错
不过还是最好将temp节点放在循环外定义和删除,节省空间 在这里插入图片描述

/**
 * 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* temp;
        //双指针,一个用于记录已反转的链表,一个记录还未翻转链表的头节点
        ListNode* res = nullptr;
        ListNode* cur = head;
        while(cur)
        {
            ListNode* temp = cur->next;
            cur->next = res;
            res = cur;
            cur = temp;
            temp = nullptr;
            delete temp;
        }
        //delete temp;
        return res;
    }
};
  1. 递归
    感觉任何循环都可以划为递归,同样任何递归可以变为循环
    我们定义一个递归函数,双指针循环中我们实际上有两个参数,res和cur,分别用于存储翻转后的链表和还未翻转的链表的“头结点”,在反转之后将cur赋值给res,将temp赋值给cur所以递归可以这样写
ListNode* reverse(ListNode* res,ListNode* cur)
{
	if(cur == nullptr) return res;
	ListNode* temp = cur->next;
	cur->next = res;
	return reverse(cur,temp);
}
  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值