LeetCode203 移除链表元素
思考:对链表进行基础的增删改查操作
很久没写链表操作了(擦汗),顺便复习一下手搓创建、输出和删除链表结点的细节,比如避免访问被释放结点内存(heap-use-after-free on address)之类的错误。
(带有虚拟头结点的单链表):
ListNode* createNode(int n)
{
ListNode* head = new ListNode,*pre=head;
head->next = nullptr;
for (int i = 0; i < n; i++) {
ListNode* temp = new ListNode;
cin >> temp->val;
temp->next = nullptr;
pre->next = temp;
pre = temp;
}
return head;
}
void display(ListNode* head) {
ListNode* temp = head->next;
int val;
while (temp != nullptr) {
val = temp->val;
cout << val << " ";
temp = temp->next;
}
}
用c++编程移除链表元素时,在修改结点next指针指向后,还需要手动delete移除的结点,防止内存占用过多。
个人更喜欢 设置一个虚拟头结点后再进行链表的增删改查操作 的方式。这样便于进行需要增加或删除的是第一个元素的操作。注意新的头结点是 “虚拟头节点->next”。
我的代码:
/**
* 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* newHead=new ListNode(0);
newHead->next=head;
ListNode* pre=newHead;
for(;pre->next!=nullptr;){
//每次新循环开始都必须要给上一轮detele的temp重新分配内存
ListNode* temp = pre->next;
if(temp->val==val){
pre->next=temp->next;
delete temp;
//ListNode* temp=pre->next;这样好像就会报 访问已被释放的内存 的错,不知道为啥
}
else{
pre=pre->next;
temp=temp->next;
}
}
//delete temp,pre;
head=newHead->next;
delete newHead;//手动释放内存
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 个节点。
好像没啥好说的,也不算多难,就是题意有地方理解错了debug一de就是俩小时......
菜,无需多盐🤓👆
*二刷的时候记得多注意索引和第几个结点的区别,还有各个判断条件是<还是<=。
我的代码:(写的是单链表,有时间也写下双链表。双链表示例可参考代码随想录的学习网站)
class MyLinkedList {
private:
int size;
ListNode* newHead;
public:
//定义链表结构
struct ListNode{
int val;
ListNode *next;
ListNode(int x):val(x),next(nullptr){}
};
//新链表构造函数
MyLinkedList() {
newHead=new ListNode(0);
size=0;
}
//获取下标为index的链表元素
int get(int index) {
ListNode*temp=newHead;
//非法下标值返回-1
if(index>=size||index<0)
return -1;
for(int i=0;i<=index;i++)
temp=temp->next;
return temp->val;
}
//在链表第一个元素前添加新元素
void addAtHead(int val) {
ListNode*newNode=new ListNode(val);
newNode->next=newHead->next;
newHead->next=newNode;
size++;
}
//在链表尾部添加新元素
void addAtTail(int val) {
ListNode*temp=newHead;
ListNode*newNode=new ListNode(val);
int index=size-1;
for(int i=0;i<=index;i++)
temp=temp->next;
temp->next=newNode;
size++;
}
//在指定下标的链表结点前添加元素
void addAtIndex(int index, int val) {
//当下标值与链表大小相等时,新元素添加在链表尾部
if(index==size)
addAtTail(val);
//合法下标值的情况
else if(index<size&&index>=0){
ListNode*temp=newHead;
ListNode*newNode=new ListNode(val);
for(int i=0;i<index;i++)
temp=temp->next;
newNode->next=temp->next;
temp->next=newNode;
size++;
}
}
//删除指定下标的链表元素
void deleteAtIndex(int index) {
ListNode*temp=newHead;
if(index<size&&index>=0){
for(int i=0;i<index;i++)
temp=temp->next;
ListNode*del=temp->next;
temp->next=temp->next->next;
delete del;
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 反转链表
思考:这题是真简单,将整个链表的所有指针方向调转180°就行,徒手画点图就很明确指针方向交换的整个过程和几个辅助指针的交换顺序。
一共需要设置三个辅助指针:cur、pre、temp,cur负责从头到尾遍历结点,pre负责在cur后面保存cur遍历的前一个结点,temp负责保存cur未遍历到的下一个结点,确保指针在交换方向前前后结点均不丢失。
我的代码:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode*pre=head;
//当链表不为空时才做调换结点指针方向的操作
if(head!=nullptr){
ListNode* cur=head->next;
ListNode*temp;
pre->next=nullptr;
while(cur!=nullptr){
temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
}
return pre;
}
};
原题有进阶难度(用迭代或递归写法编写程序),等我二刷的时候再写好了,空闲时间又刷题又写博客时间太紧张了(手忙脚乱