連結串列中刪除節點引發的思考!!!
1,我們刪除連結串列節點的時候,最常用的就是根據前趨節點,來改變指向,但是如果不用前趨節點,我們能刪除嗎??
2,在連結串列中,刪除節點的過程中,我們必須呼叫free函式嗎??如果呼叫,它一定能正確的刪除嗎??如果不呼叫free 函式,那麼它就一定不會刪除嗎??
3,一個連結串列中,如果在輸出具體儲存的資料之前,我們用其他指標p指向某一個數據,然後我們在將指標p給free掉,會產生什麼影響??如果我們free掉p->next,呢??繼續free掉p->next->next呢??會產生什麼影響???
對與刪除連結串列節點:(我們用簡單的程式說明)
程式1:
#include <stdio.h> #include <malloc.h> typedef struct node{ int data; struct node *next; }Lnode, *LinkList; LinkList Creat_LinkList(){ LinkList H = (LinkList)malloc(sizeof(Lnode)); H->next = NULL; Lnode *s; int x; scanf("%d", &x); while(x != -1) { s = (LinkList)malloc(sizeof(Lnode)); s->data = x; s->next = H->next; H->next = s; scanf("%d", &x); } return H; } void scan_LinkList(LinkList H) { LinkList s = (LinkList)malloc(sizeof(Lnode)); s = H->next; while(s!=0) { printf("%d ", s->data); s = s->next; } printf("\n"); } void delete_LinkList(LinkList p, LinkList H){ LinkList q = (LinkList)malloc(sizeof(Lnode)); q = H->next; if(q == p){ H->next = H->next->next; free(q); return ; } while(q->next != p && q->next != NULL) q = q->next; if(q->next == p && q->next != NULL) { q->next = p->next; free(p); } } int main(){ LinkList H = (LinkList)malloc(sizeof(Lnode)); //定義指標變數,並且分配記憶體空間 H = Creat_LinkList(); //建立連結串列,採用了頭插法 printf("ssssssssssssssssssss\n"); //便於觀察結果 scan_LinkList(H); //列印連結串列,從表頭開始 delete_LinkList(H->next->next->next,H); //刪除第三個元素 H為頭節點,H->next指向第一個節點 printf("aaaaaaaaaaaaaaaaaaaa\n"); scan_LinkList(H); return 0; }
如上的delete_LinkList函式:採用的根據前趨節點來刪除節點
(引入新的指標變數,作為前趨節點,通過迴圈q = q->next,來確保q->next == p,也就是q是p的前趨節點,這一中比較常見,大家應該很容易明白!!)(通常情況下需要兩層迴圈,所以時間複雜度也就是O(n^2))
執行結果:
採用無前趨節點的方法
delete_LinkList函式
void delete_LinkList(LinkList p){ p->data = p->next->data; p->next = p->next->next; }
從上面的delete_LinkList函式可以看到,這裡的引數跟上次的是不同的(還有主函式中也需要改改),還有重要的一點就是當前的時間複雜度極低,大大的提高效率,但是有的同學可能會疑惑,刪除一個連結串列的節點,咋可能不需要free指標呢,這裡程式執行的結果一定有問題???但是,真的是嗎??如果,這裡我們free一個指標又會產生什麼樣的問題呢,會得到正確的結果,還是出乎意料的結果呢??
執行結果:
可以看到我們的程式沒有一點問題,但是這是為什麼呢??這裡可是真的沒有free指標啊!!!,(按道理來說,這是行不同的啊!!)
如果我們free呢,又會產生什麼結果呢???
執行結果:void delete_LinkList(LinkList p){ p->data = p->next->data; p->next = p->next->next; free(p); }
可以看到本應該出現4的地方,變成了0,啊啊,這是為什麼啊!!!(當時一看到這個就快要炸了,為什麼啊!!)
後來經過同學的點撥,恍然大悟啊
我們程式兩條語句的意思是:要刪除第三個節點,我們先將第四個節點指向的內容賦值給第三個節點(p->data = p->next->data;),然後讓第三個節點的下一個指向(p->next)指向第五個節點,(p->next = p->next->next)
這裡,我們跳過第四個節點,因為第四個節點指向的內容和第三個節點指向的內容是一樣的,所以我們不用管第四個節點,訪問的時候,可以直接訪問第五個節點指向的內容,就間接的達到了刪除的目的
而不需要free的原因也很簡單,因為此刻第四個節點的內容並沒有任何的指標指向,所以也就不能釋放了,如果非要釋放,可以一開始用一個指標指向過來,值得注意的是:此刻已經不能在free(p)了,因為p指向的並不是我們要刪除的節點(此刻,我們要刪除的是第四個節點,而p指向的是第三個節點)
其實:不釋放的情況下:只是佔用了一塊記憶體,但是並不影響函式的目的(刪除節點),可能你會覺得不太合理,那我們就來釋放一下這個記憶體吧!!!
釋放的情況:
void delete_LinkList(LinkList p){
LinkList q = (LinkList)malloc(sizeof(Lnode));
p->data = p->next->data;
q = p->next;
p->next = p->next->next;
free(q);
}
執行結果:
可以看到執行完美,並且多餘的記憶體也被刪除掉了
但是,上面的0到底是咋樣產生的呢???
仔細分析之後, 覺得:free(p)之後,只是清空了p所指向那塊記憶體的內容(可能因為是第一次free某一塊記憶體,在連結串列中,所以清空之後,並沒有將一個隨機值存放到該記憶體,而是存放了一個0,但是值得注意的是p的地址並沒有改變)
,然而,記憶體還在,所以我們可以通過頭指標來完成遍歷,並且在輸入的時候,輸出了一個0(個人理解)1,我們刪除連結串列節點的時候,最常用的就是根據前趨節點,來改變指向,但是如果不用前趨節點,我們能刪除嗎??
可以看出,我們來刪除連結串列節點,可以根據前趨節點,也可以不用前趨節點來刪除連結串列節點,有可能效率更高
2,在連結串列中,刪除節點的過程中,我們必須呼叫free函式嗎??如果呼叫,它一定能正確的刪除嗎??如果不呼叫free 函式,那麼它就一定不會刪除嗎??
從上面可以看出:不一定要呼叫free函式,如果呼叫,也不一定能正確刪除,有可能導致產生0,如果不呼叫free函式,那麼也會產生正確的結果,正確的刪除(但是,此刻有可能還有一個記憶體塊佔用記憶體著呢),當然,如果我們能用其他的一個指標來指向的話,那麼也是可以正確刪除的,並且還清理了記憶體塊的佔用情況!!!
3,一個連結串列中,如果在輸出具體儲存的資料之前,我們用其他指標p指向某一個數據,然後我們在將指標p給free掉,會產生什麼影響??如果我們free掉p->next,呢??繼續free掉p->next->next呢??會產生什麼影響???
上面我們已經討論了free(p)的情況,那麼如果我們如果直接釋放掉p->next呢??
如下:
void delete_LinkList(LinkList p){
p->data = p->next->data;
p->next = p->next->next;
free(p->next);
}
執行結果:
可以看到我們上面談到的結論也是正確的
那麼如果,我們一起free呢??會產生什麼結果??
void delete_LinkList(LinkList p){
p->data = p->next->data;
p->next = p->next->next;
free(p);
free(p->next);
}
執行結果:
很明顯可以看到:輸出結果中,出現了0,還出現了垃圾值
那麼這樣呢??
void delete_LinkList(LinkList p){
p->data = p->next->data;
p->next = p->next->next;
free(p->next);
free(p);
}
可以很容易的看到:兩者釋放的順序不一樣,但是結果呢??
執行結果:
看得出來,
當只有一個釋放時:清空原有的值,然互給其賦值一個0
當釋放多個指標時:不取決誰先釋放,而應該是取決於釋放的所指向的內容,在連結串列上的先後順序吧!!!