随想录训练营3/60 | 链表基础;LC 203 移除链表元素;LC 707 设计链表;LC 206 反转链表

文章介绍了链表的基础知识,包括删除链表元素的技巧,如LC203题目的解决方案,以及如何设计链表(LC707),包括初始化、插入、删除等操作。此外,还讨论了链表的反转问题(LC206)。最后,文章提到了二叉树的遍历方法,包括前序、中序和后序遍历的递归实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表基础

链表定义

struct ListNode{
	int val;
	ListNode* next;
	ListNode(int x): val(x), next(nullptr){}//结构体的构造函数
}

删除链表节点时要手动释放节点(delete temp)
输入的链表一般没有虚拟头节点,在解决问题时需把头节点和其他节点分开考虑。一般的解决方式是手动加虚拟头节点,代码更优雅。

LC 203 移除链表元素

题目链接LC 203 移除链表元素
思路:先加虚拟头节点,从新头节点开始遍历,若节点的下一个节点的值为target,那么先把下一个节点赋给temp(为了手动释放内存),然后将节点的指针指向下个节点的next(即,下下个节点的地址),然后释放temp。当节点的next为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* removeElements(ListNode* head, int val) {
        ListNode* dummy = new ListNode(0);//定义虚拟头节点
        dummy->next = head;
        ListNode* i;
        i = dummy;
        while(i->next!=nullptr){//当i指向尾指针时停止循环。不是处理i指向的节点,而是处理i后面的一个节点,所以到尾指针就可以结束
            if(i->next->val!=val)i = i->next;//若i的下一个节点的值不为val,则i接着向后遍历
            else{
                ListNode* temp;
                temp = i->next;
                i->next = temp->next;
                delete temp;
            }
        }
        return dummy->next;
        
    }
};

LC 707 设计链表

题目链接LC 707 设计链表
思路:链表类中的函数有:初始化链表;获取index节点的值;插入节点到指定位置;链表尾追加节点;链表头追加节点;删除指定节点。由于有增加和删除节点操作,所以用虚拟头节点比较优雅。
注意,在增加或者删除节点时要改变链表的大小。
代码

class MyLinkedList {
public:
	//定义链表的节点结构体
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x):val(x), next(nullptr){}//构造函数
    };
	//初始化链表
    MyLinkedList() {
        _dummy = new ListNode(0);//虚拟头节点
        _size = 0;//链表长度
    };
    //得到链表索引为index的节点的值,索引从0开始,不包括虚拟头节点
    int get(int index) {
        if(index<0 || index>=_size)return -1;//当index小于或超出正常范围返回-1
        ListNode* ptr = _dummy;
        for(int i=0; i<=index; ++i){//从虚拟头节点开始,找到索引为index的节点。eq当index为0时,也需要向后移动一位,因为是从虚拟节点开始的
            ptr = ptr->next;
        }
        return ptr->val;
    }
    //在链表头插入节点
    void addAtHead(int val) {
        ListNode* head = new ListNode(val);
        head->next = _dummy->next;
        _dummy->next = head;
        ++_size;//注意不要忘记更改size
    }
    //在链表尾插入节点
    void addAtTail(int val) {
        ListNode* tail = new ListNode(val);
        ListNode* ptr = _dummy;
        while(ptr->next!=nullptr){//跳出循环时ptr指向链表最后一个节点
            ptr = ptr->next;
        }
        ptr->next = tail;
        ++_size;
    }
    //在索引为index的节点前插入新节点(新节点的索引为index)
    void addAtIndex(int index, int val) {
        if(index > _size)return;//index不符合规范直接返回
        if(index == _size){//在尾部插入
            addAtTail(val);
            return;
        }
        if(index<=0)index = 0;//在表头插入
        ListNode* ptr = _dummy;
        for(int i=0; i<index; ++i){//找到索引为index的节点的前一个节点,所以没有等号
            ptr = ptr->next;
        }
        ListNode* node = new ListNode(val);
        node->next = ptr->next;
        ptr->next = node;
        ++_size;
    }
    //删除索引为index的节点
    void deleteAtIndex(int index) {
        if(index<0 || index>=_size)return;//当index大于等于链表大小,或者小于0时,直接返回
        ListNode* ptr = _dummy;
        for(int i=0; i<index; ++i){//找到索引为index的节点的上一个节点
            ptr = ptr->next;
        }
        ListNode* temp = ptr->next;
        ptr->next = temp->next;
        delete temp;
        --_size;
    }
private:
    ListNode* _dummy;
    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);
 */

LC 206 反转链表

题目链接LC 206 反转链表
思路:直接从头反转,利用两个指针,一个指向新链表的头,一个遍历整个链表。
代码

/**
 * 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* l = nullptr;//新链表的头,从空地址开始
        ListNode* r = head;//遍历head
        while(r!=nullptr){//一直到最后一个节点的next(空节点)
            temp = r;//保存当前更改next的节点地址,防止断链
            r = r->next;
            temp->next = l;
            l = temp;
        }
        head = l;
    return head;
    }
};

------------------------------------额外题目---------------------------------

(以上为训练营每日任务,本部分为自己刷题进度)

二叉树基础知识

链式存储下的二叉树的定义

struct TreeNode{
	int val;
	TreeNode* left;
	TreeNode* right;
	TreeNode(int x):val(x), left(nullptr), right(nullptr){}
	}

遍历二叉树(递归法)
递归法的思路: (从前往后)

  1. 确定递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归的逻辑

前序遍历

//该递归函数没有返回值,参数有两个
void traversal(TreeNode* cur,vector<int>& vec){//分别表示二叉树节点和保存遍历结果的vector
	//确定终止条件
	if(cur==nullptr)return;//遇到空节点时返回,不用单独考虑叶子节点,因为有的节点只有一个子树,遍历到另一个空字数也要返回,所以直接判断该节点的地址是否为空更优雅
	//确定单层递归逻辑
	//前序遍历,先中,后左,后右
	vec.push_back(cur->val);//添加中间的节点值。push_back():在vector的末尾添加元素
	traversal(cur->left, vec);//到左子树
	traversal(cur->right, vec);//到右子树
}

中序遍历

//返回值和输入参数
void traversal(TreeNode* cur, vector<int>& vec){
	//返回条件
	if(cur==nullptr)return;
	//中序递归逻辑
	traversal(cur->left, vec);
	vec.push_back(cur-val);
	traversal(cur->right, vec);
}

后序遍历

//返回值和输入参数
void traversal(TreeNode* cur, vector<int>& vec){
	//返回条件
	if(cur==nullptr)return;
	//后序递归逻辑
	traversal(cur->left, vec);
	traversal(cur->right, vec);
	vec.push_back(cur-val);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值