鏈表的簡單總結
這兩周還是重點學習的鏈表,因為之前一直對指針不熟練,所以就拖了很久。
引言:
先來舉一個小例子,有一個程序員他的名字是阮阮,他需要儲存一段數據,所以他把這段數據的大小告訴了內存管理器,讓他給自己分配一段內存
但是這段內存的大小是固定的,阮阮非常粗心,他忘記儲存了一個數據,但是這個數組已經滿了,沒辦法,他只能再向任務管理器要一段更大的內存
然后把原來的數據復制到這個新的數組中去。這樣為了后面還能繼續儲存沒辦法,只能像管理員要一塊很大的地方。這樣就造成了浪費,那么怎么解決
這個浪費問題呢?
這里就要涉及到鏈表這個東西了,鏈表在我的理解就是在堆中沒有連續的內存塊,用指針來把各個內存塊連在一起。這樣,我需要插入一個數據,就多創建一個節點。
把然后再通過指針把這個節點插入到鏈表中,這樣就不會像阮阮一樣造成內存浪費了。
一,鏈表的基本結構:
struct Node { int data; struct Node* next; };
struct Node* head;
鏈表分為兩個部分,一個部分用來儲存數據,另一個部分儲存着下一個,節點的地址。這個鏈表用一個struct來表示,int只是data儲存的變量練習,也可以是char
后面就是指向下一個地址的指針。還要有一個head指向鏈表頭,這里先大體介紹一下,詳細后面再說。
二,數組與鏈表:
其實鏈表和數組是沒有所謂的優劣之分了,只是在不同的應用場景下有不同的作用。
1,首先,在遍歷訪問數據方面,數組每一個數據占4個字節(整型),但是在鏈表中,因為有兩個部分數據域和指針域,所以占8個字節所以在訪問數據上
數組勝出(這里好像可以用時間復雜度來考慮,但是我還不是很會)。
2,然后我覺得鏈表對於內存的利用更加的高效,不會像數組一樣浪費內存,在pta做題的時候,為了不會數組溢出,一般都會把數組設置的特別大,在儲存
比較多的數據的時候,為了避免內存的浪費,還是優先選用鏈表。而且數組大小是固定的,不想鏈表可以分成很多個內存塊那么靈活。
3,然后就是數據的處理方面,假設我現在要在一個數組的中間插入一個數,那么我需要把這個數之后的所有數都往后移動一格,然后再把這個數插入到這個
位置,時間復雜度為O(n/2),而用鏈表的話就是O(1),所以鏈表再數據的處理上有一定優勢。
三,在鏈表中插入數據:
先上代碼
#include <stdio.h> #include <stdlib.h>
struct Node { int data; struct Node* next; }; struct Node* head; void Insert(char data, int n); void Print(); void Delete(int n); int main() { head = NULL; Insert(1, 1); Insert(2, 2); Insert(5, 3); Insert(6, 4); Insert(7, 5); int n; scanf_s("%d", &n); Delete(n); Print(); return 0; } void Insert(int data, int n) { struct Node* temp = (struct Node*)malloc(sizeof(struct Node)); temp->data = data; temp->next = NULL;//這里就是創建一個臨時鏈表了
if (n == 1) { head = temp; return; } struct Node* temp1 = head; for (int i = 0; i < n - 2; i++) { temp1 = temp1->next; } temp->next = temp1->next; temp1->next = temp; } void Print() { struct Node* temp = head; while (temp != NULL) { printf("%c", temp->data); temp = temp->next; } printf("\n"); }
現在來解釋一下這段代碼:
重點看這個Insert函數:
首先,創建一個節點,在堆中分配一個結點大小的內存給這個節點(感覺描述的怪怪的),然后把數據加到這個結點的數據域中,然后把把這個temp->next
置為NULL。然后判斷插入的位置是不是第一個位置,如果是第一個位置的話head就是temp,然后再創建一個結點,進行遍歷這個鏈表,把temp2->next的值
賦給temp2。假設我要插入n的位置,那就讓temp2遍歷到n-1的位置,一共遍歷n-2次。到了這個位置后,把temp2->next指向的值賦給temp->next,然后temp2-
>next= temp,也就是把temp2的指針指向那個要插入結點的首地址。
print函數比較簡單,這里就不做過多描述了。
四,就是鏈表的刪除:
我這里用一個detele函數來進行刪除元素。
void Delete(int n) { struct Node* temp1 = head; if (n == 1) { head = temp1->next;//如果是1的話就是把temp給跳過去所以這個地方用next
return;//這個地方也可以用else來替代
} int i; for (i = 0; i < n - 2; i++) { temp1 = temp1->next; } struct Node* temp2 = temp1->next;//temp的位置是n-1所以這個temp2就是要刪除的n的那個位置;
temp1->next = temp2->next;//把temp2下一個賦給temp1的位置的next這樣就可以做到跳過temp2
free(temp2); }
就說一下大意把,解釋在代碼中,就是創建一個temp1所指向的結點,然后把temp2跳過並且釋放掉他的內存。
下次博客打算寫一下鏈表的進階(像反轉之類的),還要去學習(其實已經有學了只是太多不好寫所以多分幾次)。