1. 程式人生 > >連結串列中刪除節點引發的思考!!!

連結串列中刪除節點引發的思考!!!

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

當釋放多個指標時:不取決誰先釋放,而應該是取決於釋放的所指向的內容,在連結串列上的先後順序吧!!!