链表基础
链表定义
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){}
}
遍历二叉树(递归法)
递归法的思路: (从前往后)
- 确定递归函数的参数和返回值
- 确定终止条件
- 确定单层递归的逻辑
前序遍历
//该递归函数没有返回值,参数有两个
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);
}