链表理论基础
单链表
双向链表(两个指针域)
循环链表(首尾相连)
链表删除和添加节点的操作
删除时记得free
209.移除链表元素
class Solution203{
public:
/// @brief 给出头节点head,整数val 删除所有满足Node.val==val的节点
/// @param head 链表头节点
/// @param val 整数
/// @return 删除后的头节点
ListNode* removeElements(ListNode* head,int val){
ListNode* virtualHead=new ListNode;
virtualHead->next=head;
ListNode* p=virtualHead;
ListNode* temp;
while(p->next!=nullptr){
if(p->next->val==val){
temp=p->next;
p->next=p->next->next;
delete temp;
}
else{
p=p->next;
}
}
return virtualHead->next;
}
};
利用了虚拟头节点方便操作。
在修改之前的代码运行时出现了如下错误。
要注意new,delete以及malloc,free的结对使用。后续补上二者的差别
在使用malloc和free来处理动态内存的时候,仅仅是释放了这个对象所占的内存,而不会调用这个对象的析构函数;使用new和delete就可以既释放对象的内存的同时,调用这个对象的析构函数。
707.设计链表
大概没有什么多说的,在数据结构课上都有学到,这里重新用c++再实现一次,头脑清晰些就ok
class MyLinkedList {
public:
/// @brief 节点结构的定义
struct Node{
int val;
Node* next;
Node(int val):val(val),next(nullptr){};
};
MyLinkedList() {
_size=0;
_head=new Node(0); //虚拟头节点
}
/// @brief 获取链表中下标为index的值
/// @param index 下标
/// @return 链表中下标为index的值
int get(int index) {
if(index<0||index>=_size){
return -1;
}
// 0 _head->next->value;
Node* tempPtr=_head;
for(int i=0;i<=index;i++){
tempPtr=tempPtr->next;
}
return tempPtr->val;
}
/// @brief 将值为val的节点插入到头节点
/// @param val 要插入的值
void addAtHead(int val) {
Node* temp=new Node(val);
temp->next=_head->next;
_head->next=temp;
_size++;
}
/// @brief 在末尾增加值为val的节点
/// @param val 要增加的值
void addAtTail(int val) {
Node* temp=new Node(val);
Node* p=_head;
while(p->next!=nullptr)p=p->next;
p->next=temp;
_size++;
}
/// @brief 在index位置的节点之前增加值为val的节点
/// @param index 位置
/// @param val 要增加的值
void addAtIndex(int index, int val) {
if(index<0||index>_size){
return;
}
Node *p=_head;
while(index>0){
p=p->next;
index--;
}
Node *temp=new Node(val);
temp->next=p->next;
p->next=temp;
_size++;
}
/// @brief 删除下标为index的节点(如果下标有效)
/// @param index 要删除的节点的下标
void deleteAtIndex(int index) {
if(index<0||index>=_size){
return;
}
Node *p=_head;
while(index>0){
p=p->next;
index--;
}
Node* temp=p->next->next;
delete p->next;
p->next=temp;
_size--;
}
private:
Node* _head;
int _size;
};
206.反转链表
刚看到题的想法是用栈,然后就用了递归,注意当前节点状态,虽然有点绕还是写出来了。
class Solution206{
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr){
return nullptr;
}
ListNode* virtualResultHead=new ListNode();
solve(head,virtualResultHead);
return virtualResultHead->next;
};
ListNode* solve(ListNode* nowSource,ListNode* nowResult){
if(nowSource->next==nullptr){
nowResult->next=nowSource;
return nowResult->next;
}
nowResult=solve(nowSource->next,nowResult);
nowResult->next=nowSource;
nowSource->next=nullptr;
return nowResult->next;
}
};
然后看文章,真没考虑到这种反转思路,先实现一下:思路写在注释里
ListNode* reverseList2(ListNode* head) {
//用两个指针来实现
//考虑需要保存的状态
//当前节点与前置节点
// temp保存当前节点的next
// 当前节点的next指向pre
// pre移动到当前节点
// cur=temp
// 直到cur为nullptr
if(head==nullptr){
return head;
}
ListNode *now=head;
ListNode *pre=nullptr;
ListNode *temp;
while(now!=nullptr){
temp=now->next;
now->next=pre;
pre=now;
now=temp;
}
return pre;
};
仔细思考了一下,两种方法都是用某种方法来保存上一个节点的地址,只是保存的结果不同。而递归的坏处嘛,,递归太深的时候会由于资源不够而溢出,所以还是得想办法用这种迭代的方法。