關於學習連結串列的總結
連結串列的簡單總結
這兩週還是重點學習的連結串列,因為之前一直對指標不熟練,所以就拖了很久。
引言:
先來舉一個小例子,有一個程式設計師他的名字是阮阮,他需要儲存一段資料,所以他把這段資料的大小告訴了記憶體管理器,讓他給自己分配一段記憶體
但是這段記憶體的大小是固定的,阮阮非常粗心,他忘記儲存了一個數據,但是這個陣列已經滿了,沒辦法,他只能再向工作管理員要一段更大的記憶體
然後把原來的資料複製到這個新的陣列中去。這樣為了後面還能繼續儲存沒辦法,只能像管理員要一塊很大的地方。這樣就造成了浪費,那麼怎麼解決
這個浪費問題呢?
這裡就要涉及到連結串列這個東西了,連結串列在我的理解就是在堆中沒有連續的記憶體塊,用指標來把各個記憶體塊連在一起。這樣,我需要插入一個數據,就多建立一個節點。
把然後再通過指標把這個節點插入到連結串列中,這樣就不會像阮阮一樣造成記憶體浪費了。
一,連結串列的基本結構:
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跳過並且釋放掉他的記憶體。
下次部落格打算寫一下連結串列的進階(像反轉之類的),還要去學習(其實已經有學了只是太多不好寫所以多分幾次)。