2023.12.1
代码随想录刷题Day3
1. 203移除链表元素
对于链表的各种操作备考时也是烂熟于心,这么长时间没写过相关代码,也确实忘了很多,关于移除链表元素,逻辑其实很简单,主要还是看对指针的使用是否熟悉,其次就是最关键的,头节点的使用,我个人是比较习惯加头节点,头节点可以使得对链表的操作全部统一,不用特殊特殊某个节点很方便。思路很简单,加入头节点后遍历一遍链表节点值,如果是要删除的值,直接将上节点的next连接到要删除节点的next就行,也就是越过要删除的节点。再把要删除的节点delete。
/*** 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* temp = new ListNode;ListNode*cur = temp;temp->next = head;while(cur->next!=NULL){if(cur->next->val==val){cur->next = cur->next->next;}elsecur= cur->next;}head = temp->next;delete temp;return head;}
};
2. 707设计链表
这道题逻辑很简单,但就是很考察指针基本功,刚开始写的时候出现了一些指针地址错误,改了很多次,代码这东西还得多写,不然长时间不写真的会生疏,最后有个情况,测试用例过了,但提交失败,有错误的情况,发现是get函数有问题,最后通过打印链表长度,发现是addindex这个函数全部插入失败了,原因是开始的index合法判断有问题。原本这里用了和get函数一样的index判断。并没有多想,但最后出错才发现,两个逻辑不一样,get函数不允许两个端点的index,而addindex却允许有两边的。一个简单的例子:现在有三个节点,索引是0,1,2。此时如果想get索引为3的,则应该报错。因为并没有索引3,但是如果想要给索引3加节点,应该是允许的,因为索引3跟原链表是相连的,这就是我开始犯的错误,没有注意到这一点。最终整体感受,这道题对于常写链表代码的人来说。应该会很简单。但对于很长时间没写过链表代码的人来说,比如我,还是有些复杂的,逻辑简单,实现有些生疏了。
class MyLinkedList {public:struct ListNode{int val;ListNode* next;ListNode(int val):val(val),next(nullptr){}};MyLinkedList() {head = new ListNode(0);size=0; }int get(int index) {cout<<this->size<<endl;if(index > (size-1) || index<0){return -1;}else{int now_index=0;ListNode*node = new ListNode(0);node = head->next;while(now_index<index){node = node->next;now_index++;}return node->val;}cout<<this->size<<endl;}void addAtHead(int val) {ListNode *node = new ListNode(val);node->next = head->next;head->next = node;size++;cout<<this->size<<endl;}void addAtTail(int val) {ListNode*node = new ListNode(val);ListNode*temp = head;while(temp->next != nullptr){temp = temp->next;} temp->next = node;size++;cout<<this->size<<endl;}void addAtIndex(int index, int val) {if(index > size) return;if(index < 0) index = 0; int now_index = 0;ListNode*node = new ListNode(val);ListNode*temp = new ListNode(0);temp = head;while(now_index<index){temp = temp->next;now_index++;}node->next = temp->next;temp->next = node;size++;cout<<this->size<<endl;}void deleteAtIndex(int index) {if(index>size-1)return;int now_index = 0;ListNode*node = new ListNode(0);node = head;while(now_index<index){node = node->next;now_index++;}node->next = node->next->next;size--;}
private:int size;//记录链表实际长度(除头节点外)ListNode* head;
};/*** 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);*/
3. 206反转链表
这道题好像是链表的典型题,很多面试、链表操作中都会有反转链表的题。具体实现也是有两种方法,第一种很简单,遍历连边,将值重新赋予一个新链表就行,最后的新链表就是反转后的链表。当然我们需要掌握的是第二种,在原链表的基础上反转,这种方法逻辑还是有点抽象的,同样是循环,每次需要在节点处断开,前面待处理的原链表,后面是已经反转过的,所以,肯定需要三个指针,一个用来遍历,一个用来记录待处理的链表,一个指向已经处理后的反转链表。最后将已经处理后的反转链表返回即可,对于链表的循环,我记得有个很重要的点就是,每次循环的操作顺序,一定要先记录下一个节点的位置,不然更新当前节点的next后则会丢失原本的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* reverseList(ListNode* head) {ListNode* temp = nullptr; //反转后的ListNode* node = head; //遍历用的ListNode* n_node; //待处理的while(node){n_node = node->next; //一定优先记录待处理的部分,也就是该节点指向当前遍历节点的nextnode->next = temp; //进行翻转。当前节点next指向反转后的链表,也就是拼接temp = node; //更新反转节点的头,上一步已经连接的,这一步就要把头更新为新节点node = n_node; //往前循环}return temp; //最后返回反转链表即可}
};