—— 引子
今天給大家帶來的是一個新的數據結構,鏈表!
上一次的順序表,大家一定覺得非常的容易,沒錯,作爲一個入門級別的數據結構,要是讓大家感覺到喫力,那還了得!
一起來回顧一下我們上次的順序表,作爲一個比較簡單的數據結構,順序表利用自身的管理結構,完成了對於整個結構的控制,利用結構體將三個參數封裝起來,size用來表示當前順序表中一共有多少元素,而capacity表示最大可以儲存多少元素,最後,還有這一個指針,用來指向一片堆區中的連續空間,用來真正的保存我們的數據。
相信現在大家聽到我的介紹,恐怕陌生感要減少很多了吧,所以,不要放棄,要相信,熟能生巧,總有一天,這區區數據結構,你一定是能將他拿下的!
什麼是鏈表?
對於鏈表這個數據結構,相信很多小夥伴還是比較頭疼的,放心,等會,就讓你完全弄懂它!
首先,鏈表的關鍵,在窩瓜看來,就是這個“鏈”字!
我想佛珠大家一定都見過,窩瓜覺得,佛珠的整體形態就和我們的鏈表有着異曲同工之妙!
首先,我們可以將每一顆珠子看成是我們的一個節點,而這珠子與珠子之間的線,我們就將其想象爲一個看的見、摸得到的指針,這樣,每一顆佛珠,就可以通過這條線,找到下一個佛珠,這不正是我們的鏈表嗎!
現在我們來模擬一下鏈表的實現!
首先,桌面上什麼都沒有,當我們插入第一個元素的時候,桌面上出現了一個佛珠(也就是一個節點),這時,我們又需要第二顆佛珠,首先,將線從第一顆佛珠上穿過,然後拿出第二顆佛珠,用線將其串在第一顆佛珠的後面,以此類推,這種用線將佛珠一顆顆串在一起的過程,在鏈表中,就是我們的插入操作!
此時我們發現其中一顆佛珠的品相不好,不想要它了,我們要怎麼將它去掉呢?
很多小夥伴一定會說,這也太簡單了,直接把線間斷,然後把珠子取出來,然後將斷了的線連接上,不就好了!
沒錯,就是這麼簡單!其實在我們鏈表的世界中,也是這樣的,上面的情況,就完美的模擬了我們刪除節點的情況,在刪除節點的時候,我們只需要將要刪除節點的前一個節點找到,然後切斷這個節點與要刪除節點之間的線(指針),然後將這個節點直接指向要刪除節點的後一個節點,這不就完成了所謂的刪除操作。
所以說,鏈表其實也不難!
接下來,就讓我們看看怎樣用代碼模擬上面的過程,從而實現我們的鏈表!
鏈表的實現
上次介紹了順序表的特點,那麼,鏈表有着什麼特點呢?讓我們一起來看看!
鏈表的特點:
- 空間不固定,隨用隨建,避免浪費
- 邏輯上每個節點相鄰,物理上不一定
- 插入和刪除比較簡單,不用遍歷整個結構
- 查詢因爲沒有索引的緣故,性能不如順序表,時間複雜度爲O(n)
定義頭節點和節點
typedef struct N{
int data;
struct N * next;
}Node;
//節點
typedef struct{
Node * head;
Node * tail;
int size;
}List;
//頭節點
初始化鏈表
List * init_list(List * p){
p->head = (void *)0;
p->tail = (void *)0;
p->size = 0;
return p;
}//初始化鏈表
清空鏈表
List * clear_list(List * p){
while(p->head){
Node * del = p->head;
p->head = del->next;
free(del);
}
p->tail = (void *)0;
p->size = 0;
return p;
}//清空鏈表
銷燬鏈表
void * destroy_list(List * p){
clear_list(p);
}//銷燬鏈表
尾插法
List * push_back_list(List * p, int data){
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
p->tail->next = new_node;
p->tail = new_node;
p->size++;
return p;
}//尾部插入元素
頭插法
List * push_front_list(List * p, int data){
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
new_node->next = p->head;
p->head = new_node;
p->size++;
if(p->size == 1){
p->tail = new_node;
}
return p;
}//頭部插入元素
頭刪法
List * pop_front_list(List * p){
if(p->size == 0){
return (void *)0;
}
Node * del = p->head;
p->head = p->head->next;
free(del);
p->size--;
if(p->size == 0){
p->tail = (void *)0;
}
return p;
}//頭部刪除元素
尾刪法
List * pop_back_list(List * p){
if(p->size == 0){
return (void *)0;
}
Node * del = p->tail;
Node * tem = p->head;
if(p->size == 1){
p->head = p->tail = (void *)0;
}else{
while(tem->next != p->tail){
tem = tem->next;
}
tem->next = (void *)0;
p->tail = tem;
}
free(del);
p->size--;
return p;
}//尾部刪除元素
打印鏈表
void print(List * p){
Node * tem = p->head;
if(tem){
printf("現在有%d個元素,分別是:%d",p->size, tem->data);
tem = tem->next;
while(tem){
printf("、%d",tem->data);
tem = tem->next;
}
printf("\n");
}else{
printf("鏈表爲空!\n");
}
}//打印鏈表
查找指定元素位置
Node * find_list(List * p, int data){
if(p->size == 0){
return (void *)0;
}
Node * tem = p->head;
while(tem && tem->data == data){
tem = tem->next;
}
return tem;
}//查找指定元素
刪除指定位置元素
List * erase_list(List * p, Node * pos){
if(p->size == 0 || pos == p->tail){
return (void *)0;
}
if(pos == (void *)0){
return pop_back_list(p);
}
Node * del = pos->next;
pos->next = del->next;
p->size--;
if(del == p->tail){
p->tail = pos;
}
free(del);
return p;
}//刪除指定
在指定位置插入元素
List * insert_list(List * p, Node * pos, int data){
if(p->size == 0 || pos == (void *)0){
return push_front_list(p , data);
}
if(pos == p->tail){
return push_back_list(p , data);
}
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
new_node->next = pos->next;
pos->next = new_node;
p->size++;
return p;
}//在指定位置插入元素
測試代碼
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
int i;
List list;
init_list(&list);
push_front_list(&list, 1);
push_front_list(&list, 2);
print(&list);
push_back_list(&list, 3);
push_back_list(&list, 4);
print(&list);
pop_front_list(&list);
pop_back_list(&list);
print(&list);
insert_list(&list,find_list(&list, 3) , 5);
print(&list);
erase_list(&list, find_list(&list, 5));
print(&list);
clear_list(&list);
print(&list);
destroy_list(&list);
return 0;
}
結果展示
這就是這次給大家帶來的關於鏈表的實現,下次,將會給大家帶來順序棧和鏈式棧的實現!謝謝大家~~~~~~~~~