203 太久没写链表了非常不熟练,有dummy 没dummy全弄懂 加上理清自己问题,写总结花了1.5h
第一个版本
遇到的一些问题:
1. head->val 是指向的大框,head->next 是大框后面的小框
2. curr= curr->next 这个是遍历,因为curr是我们自己创的东西,本质上没改变链表结构
curr->next=curr->next->next 这个是删除,因为 curr->next是列表结构的内容
3. 不用dummy的话 head就要特殊处理,还要用while,一直把需要删掉的head全部删掉
4. 要把一会失去ptr的东西 先用tmp存起来,之后delete释放内存 (java python)不用
5. NULL 和 nullptr应该是一样的
ListNode* removeElements(ListNode* head, int val) {
while(head != nullptr && head->val == val) {
ListNode* tmp = head;
head = head->next;
delete tmp;
}
if (head == nullptr) {
return nullptr;
}
ListNode *curr=head;
while(curr->next!=nullptr){ //没有处理head 开头
if(curr->next->val==val){
ListNode* tmp = curr->next;
curr->next=curr->next->next;
delete tmp;
}
else{
curr=curr->next;
}
}
return head;
}
第二个版本
while那里改成while(curr!=nullptr && curr->next!=nullptr),直接去掉 if (head == nullptr) {return nullptr; }
版本三 dummy head
ListNode* removeElements(ListNode* head, int val) {
ListNode * dummy = new ListNode(0);
dummy->next = head;
ListNode *curr = dummy;
while(curr->next != nullptr ){
if(curr->next->val == val){
ListNode* tmp = curr->next;
curr->next = curr->next->next;
delete tmp;
} else {
curr = curr->next;
}
}
head = dummy->next;
delete dummy;
return head;
}
关于遍历和删除的while的判断条件
一般要遍历 要走完, 应该是 while curr!=nullptr (这里是不处理,跳出。是说 我这样 遍历其实是没走完的,但是由于我的逻辑是用到 curr->next->val ,后续改也是 next变 next next,所以不用走完 只用走到倒数第二个
#707 居然自己写过了,好久没有自己implementation无误写出来题了,good for me!dummy head 在处理增删方面真是好用
才发现lc的 test and run功能,好用的
class MyLinkedList {
public:
struct Node {
int val;
Node* next;
Node() : val(0), next(nullptr) {}
Node(int x) : val(x), next(nullptr) {}
Node(int x, Node *next) : val(x), next(next) {}
};
int _size;
Node* _dummyHead;
MyLinkedList() {
_size=0;
_dummyHead= new Node(0);
}
int get(int index) {
if(index>_size-1|| index<0){return -1;}
Node * curr= _dummyHead->next;
int idx=0;
while(curr!=NULL&& idx!=index){
curr=curr->next;
idx++;
}
if(curr==NULL){return -1;}
return curr->val;
}
void addAtHead(int val) {
Node * newnode= new Node (val);
newnode->next=_dummyHead->next;
_dummyHead->next=newnode;
_size++;
}
void addAtTail(int val) {
Node * curr= _dummyHead;
while (curr->next!=NULL){
curr=curr->next;
}
Node * newnode= new Node (val);
curr->next=newnode;
newnode->next=NULL;
_size++;
}
void addAtIndex(int index, int val) {
if(index>_size){return;}
if(index==_size){ addAtTail(val); return;}
Node * curr=_dummyHead;
while(index>0){
curr=curr->next;
index--;
}
Node * newnode= new Node (val);
newnode->next=curr->next;
curr->next=newnode;
_size++;
}
void deleteAtIndex(int index) {
if (index<0|| index >_size-1){return;}
Node * curr=_dummyHead;
while(index>0){
curr=curr->next;
index--;
}
if(curr->next!=NULL){
Node * tmp= curr->next;
curr->next=curr->next->next;
delete tmp;
}
}
};
/**
* 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 面试经常考,反转链表,法一双指针, pre是指向当前这个方块的,cur是用来遍历的
ListNode* reverseList(ListNode* head) {
if (head==NULL){return head;}
ListNode * pre = NULL;
ListNode * curr = head;
ListNode * tmp;
while(curr!=NULL){
tmp= curr->next;
curr->next=pre;
pre = curr;
curr = tmp;
}
return pre;
}
双指针 时间复杂度: O(n) 空间复杂度: O(1)
代码随想录另一个方法是递归 时间复杂度: O(n) 空间复杂度: O(n)
【gpt说:空间复杂度由递归栈的深度(n)决定 。
递归栈并不是你在代码中直接看到的一个结构,而是在程序执行递归函数时,操作系统或运行时环境隐式地使用的一个数据结构。
当你调用一个函数时,系统会在内存中为这个函数调用创建一个"栈帧"。这个栈帧包含了函数的局部变量,返回地址,以及其他函数调用需要的信息。当函数返回时,这个栈帧就会被销毁。
如果函数是递归调用的,那么系统会为每次递归调用都创建一个新的栈帧,并把这些栈帧都压入到一个"调用栈"(或者叫"递归栈")中。这就是为什么你可以在一个函数中调用它自己,然后再返回到原来的函数调用中:每个递归调用都有它自己的栈帧,包含了它自己的局部变量,返回地址等信息。
因此,在分析递归函数的空间复杂度时,需要考虑这个隐式的调用栈。如果递归的深度(也就是调用栈的深度)是n,那么空间复杂度就是O(n)。这里的n并不是指ListNode的数量,而是指的递归的深度,即递归调用的次数。】
今天总共花了3h左右