203.移除連結串列元素
文章連結:https://programmercarl.com/0203.移除連結串列元素.html#演算法公開課
影片講解:https://www.bilibili.com/video/BV18B4y1s7R9
題目出處:https://leetcode.cn/problems/remove-linked-list-elements/
卡哥在這裡講解了為什麼要使用虛擬頭節點,以及使用和不使用的區別,不清楚的話可以看影片講解
- 方一:直接刪除頭節點
寫的時候有幾個小問題需要注意,已經在程式碼中標出
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//不使用虛擬頭節點,直接刪除
//刪除頭節點(頭節點特殊,刪除方式不一樣,直接將head後移一個節點)
while(head!=NULL&&head->val==val){ //這裡一開始寫的時候用的if,但是如果head後面的節點依然符合要求,則會漏掉這種情況
ListNode* tmp=head;
head=head->next;
delete tmp;
}
//刪除非頭節點
ListNode* cur=head;
while(cur!=NULL&&cur->next!=NULL){ //這裡一開始寫的時候沒有寫cur!=NULL,這樣是不行的,因為如果它為空了再操作會導致空指標錯誤
if(cur->next->val==val){
ListNode* tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
}
else{
cur=cur->next;
}
}
return head;
}
};
- 方二:使用虛擬頭節點
錯誤如下圖所示:
這裡會報錯的原因是 dummyHead 沒有被初始化。dummyHead 是一個指向 ListNode 型別的指標,但沒有給它分配記憶體空間。當前程式碼只宣告瞭一個指標,但它指向的是未定義的記憶體區域,因此在訪問 dummyHead->next 時會導致未定義行為或崩潰。
所以這裡應該寫成:
ListNode* dummyHead = new ListNode(0); //
dummyHead->next = head;
總的程式碼如下所示:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//使用虛擬頭節點
ListNode* dummyHead=new ListNode(0); //注意將指標初始化
dummyHead->next=head;
ListNode* cur=dummyHead;
while(cur->next!=NULL){
if(cur->next->val==val){
ListNode* tmp=cur->next;
cur->next=cur->next->next;
delete tmp;
}
else{
cur=cur->next;
}
}
head=dummyHead->next;
return head;
}
};
- 方三:使用遞迴也很有意思
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//使用遞迴
if(head==nullptr){
return nullptr;
}
if(head->val==val){
ListNode* newHead=removeElements(head->next,val);
delete head;
return newHead;
}
else{
head->next=removeElements(head->next,val); //這裡一開始沒有想到head->next
return head;
}
}
};
707.設計連結串列
文章連結:https://programmercarl.com/0707.設計連結串列.html
影片連結:https://www.bilibili.com/video/BV1FU4y1X7WD
題目連結:https://leetcode.cn/problems/design-linked-list/description/
先構造節點,併為類寫建構函式進行初始化。
class MyLinkedList {
public:
//定義節點
struct LinkNode{
int val;
LinkNode* next;
LinkNode():next(nullptr){}//建構函式
LinkNode(int val):val(val),next(nullptr){}//過載建構函式
};
MyLinkedList() { //建構函式
dummyHead=new LinkNode();
size=0;
}
private:
int size;
LinkNode* dummyHead;
};
以下為學校內學到的寫法:
- 頭插法:
void addAtHead(int val) {
LinkNode* s=new LinkNode(val);
s->next=dummyHead->next;
dummyHead->next=s;
}
- 尾插法:
void addAtTail(int val) {
LinkNode* s=new LinkNode(val);
LinkNode* r=dummyHead;
while(r->next!=NULL){
r=r->next;
}
r->next=s;
size++;
}
- 在index之前插入
void addAtIndex(int index, int val) {
if(index>size||index<0){
return;
}
LinkNode* cur=dummyHead;
while(index--){
cur=cur->next;
}
LinkNode* s=new LinkNode(val);
s->next=cur->next;
cur->next=s;
size++;
}
- 獲取第index個節點值
int get(int index) {
if(index>(size-1)||index<0) return -1;
LinkNode* cur=dummyHead->next;
while(index--){
cur=cur->next;
}
return cur->val;
}
- 刪除第index個節點
void deleteAtIndex(int index) {
if(index<0||index>=size) return;
LinkNode* cur=dummyHead;
while(index--){
cur=cur->next;
}
LinkNode* tmp= cur->next;
cur->next=cur->next->next;
delete tmp;
tmp=nullptr;//這裡最好將其賦為空指標
//delete命令指示釋放了tmp指標原本所指的那部分記憶體,
//被delete後的指標tmp的值(地址)並非就是NULL,而是隨機值。也就是被delete後,
//如果不再加上一句tmp=nullptr,tmp會成為亂指的野指標
//如果之後的程式不小心使用了tmp,會指向難以預想的記憶體空間
size--;
}
206.反轉連結串列
- 雙指標的寫法:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur=head;
ListNode* pre=nullptr;
while(cur!=NULL){
ListNode* tmp=cur->next;
cur->next=pre;
pre=cur;
cur=tmp;
}
return pre;
}
};
- 遞迴寫法:
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode*cur){
// if(cur!=NULL){
// ListNode* tmp=cur->next;
// cur->next=pre;
// reverse(cur,tmp);
// }
// else return pre;
if(cur==NULL) return pre;
ListNode* tmp=cur->next;
cur->next=pre;
return reverse(cur,tmp);
}
ListNode* reverseList(ListNode* head) {
ListNode* pre=nullptr;
ListNode* cur=head;
return reverse(pre,cur);
}
};
第一次寫,寫成了註釋掉的那部分,說明對遞迴還不太熟