前一小節介紹使用陣列實現了線性表,這一小節使用指標來實現:
先看那12個函式:
#include #include typedef int elemtype;
typedef struct lnode
lnode,*linklist;
//初始化鍊錶
bool initlist(linklist *lst)
//刪除鍊錶
void deletelist(linklist *lst)
free(*lst);
*lst = null;
}//清空鍊錶
void clearlist(linklist *lst)
(*lst)->next = null;
}//判斷鍊錶是否為空
bool is_empty(linklist *lst)
else }
//遍歷鍊錶並列印
void printlist(linklist *lst)
printf("\n");
}//計算鍊錶中的元素個數
int listlength(linklist *lst)
return cnt;
}//在指定位置插入元素
bool insertelem(linklist *lst,int index,elemtype *e)
++cnt;
p = p->next;
} return false;
}//刪除某個位置的元素
bool deleteelem(linklist *lst,int index,elemtype *e)
p = p->next;
q = q->next;
++cnt;
} return false;
}//獲取某個元素的位置
int locateelem(linklist *lst,elemtype e)
return -1;
}//獲取某個位置的元素
bool getelem(linklist *lst,int index,elemtype *e)
++cnt;
p = p->next;
} return false;
}//獲取下乙個元素
bool nextelem(linklist *lst,elemtype curr,elemtype *nxt)
p = p->next;
} return false;
}//獲取前乙個元素
bool priorelem(linklist *lst,elemtype curr,elemtype *pre)
q = q->next;
p = p->next;
} return false;
}void inputlist(linklist *lst,int n,int* arr)
}
其中最後乙個並不是必須的,只為了輸入的時候方便。
對指標不太熟悉的朋友可能有點不明白引數是怎麼傳遞的,這裡通過乙個具體的例子來說明一下。
void reset(int *x)
在主函式中:
int a = 1;
printf("before reset: %d\n",a);
reset(&a);
printf("after reset: %d\n",a);
這個程式相信大家都見過,但是我還是要仔細地說明一下編譯器是怎麼對待函式呼叫的:它會將實參複製乙份給形參,然後對形參進行操作。所以,形參的改變是不會影響實參的,如果想改變主調函式中某個實參的值,必須將它的位址傳遞給被調函式—雖然這裡還是會發生實參複製給形參的情況,但是由於複製的是位址,所以被掉函式中,只要對位址進行解引,就能操作主調主調函式中的值了。
再來看我們的程式:
typedef struct lnode
lnode,*linklist;
定義了指向結構體的指標,當我們的操作並不需要修改指標本身時,我們大可以傳遞指標本身:
//判斷鍊錶是否為空
bool is_empty(linklist lst)
else
}
但是如果我們的操作需要修改這個指標時,那麼我們只能給函式傳遞這個指標的位址,然後在函式中通過解引操作「*」來或得指標本身,然後在進行記憶體分配、插入、刪除等操作。所以我們的很多函式的型參都有linklist *lst。而在主函式中,將這個指標的位址傳遞給函式,比如initlist(&mylist);
與「值」傳遞相比,傳遞指標看上去很麻煩,但是它有乙個明顯的效率優勢:當直接傳遞值時,會複製乙個值,如果複製的是乙個複雜的結構體,那麼代價是非常大的;但如果複製的僅僅是乙個指標,那麼只需要4個位元組(因為我們的電腦是32位的,所以位址也是32位,就是4個位元組)。
相比之下,如果使用c++中的「引用」概念就要方便的多:直接將實參與形參關聯起來,通過形參的改變,就能改變實參,而且並沒有任何「複製」發生。
與陣列實現的線性表相比,鍊錶的優勢在於可以方便的插入、刪除元素;但是對於隨機訪問某個元素,卻非常緩慢,必須要通過從頭開始遍歷整個鍊錶開始,所以程式中總是出現
linklist p = l->next;
while(p)
的結構。總體上來說,二者各有利弊,如果的資料需要頻繁的插入、刪除,那麼選擇鍊錶;如果需要頻繁的隨機范文,那麼選擇順序表。
其實鍊錶還有另外幾種複雜的結構,比如:迴圈鍊錶、雙向鍊錶、雙向迴圈鍊錶等等,由於基本的原理是相同的,這裡只對他們進行簡要的介紹。
迴圈鍊錶很簡單,就是最後乙個節點的指標next重新指向煉表頭。它的好處在於不論你現在處於鍊錶的什麼位置,總能遍歷到鍊錶的所有元素。程式部分的改動也比較小,主要是在初始化時,將next指向煉表頭,在遍歷鍊錶的過程中,迴圈的停止條件變為:while(p != (*lst))。
雙向鍊錶是在基本鍊錶的基礎上,增加乙個指向前乙個節點的指標prior,在插入、刪除操作時,指標的指向需要仔細:
#include #include #define elemtype int
typedef struct dnode
dnode,*dlinklist;
bool initdlist(dlinklist* dlst)
void deletedlist(dlinklist* dlst)
free(*dlst);
*dlst = null;
}bool insertelem(dlinklist* dlst,int index,elemtype e)
p = p->next;
++cnt;
} return false;
}void deleteelem(dlinklist* dlst,int index,elemtype *e)
++cnt;
p = p->next; }}
void printlist(dlinklist* dlst)
printf("\n");
}
這裡給出了比較詳細的**。尤其需要注意,當你第一次向鍊錶中插入元素,或者插入元素的位置在鍊錶的末尾,或者刪除鍊錶的最後乙個元素時,此時p->是為null的,所以不能取p->next->prior 。
使用指標實現的線性表 鍊錶
前一小節介紹使用陣列實現了線性表,這一小節使用指標來實現 先看那12個函式 include include typedef int elemtype typedef struct lnode lnode,linklist 初始化鍊錶 bool initlist linklist lst 刪除鍊錶 v...
線性表的鍊錶實現
include include define elemtype int typedef struct lnode list struct lnode struct lnode l list ptrl list makeempty list ptrl 建立空表 int length list ptrl...
線性表的鍊錶實現
include printf輸出函式和scanf輸入函式所在標頭檔案 include exit退出函式所在標頭檔案 include malloc動態記憶體分配函式 realloc函式所在的標頭檔案 includeusing namespace std 用 define巨集定義來定義符號常量 函式結果...