1. 程式人生 > 其它 >關於學習連結串列的總結

關於學習連結串列的總結

連結串列的簡單總結

這兩週還是重點學習的連結串列,因為之前一直對指標不熟練,所以就拖了很久。

引言:

  先來舉一個小例子,有一個程式設計師他的名字是阮阮,他需要儲存一段資料,所以他把這段資料的大小告訴了記憶體管理器,讓他給自己分配一段記憶體
但是這段記憶體的大小是固定的,阮阮非常粗心,他忘記儲存了一個數據,但是這個陣列已經滿了,沒辦法,他只能再向工作管理員要一段更大的記憶體
然後把原來的資料複製到這個新的陣列中去。這樣為了後面還能繼續儲存沒辦法,只能像管理員要一塊很大的地方。這樣就造成了浪費,那麼怎麼解決
這個浪費問題呢?

這裡就要涉及到連結串列這個東西了,連結串列在我的理解就是在堆中沒有連續的記憶體塊,用指標來把各個記憶體塊連在一起。這樣,我需要插入一個數據,就多建立一個節點。

把然後再通過指標把這個節點插入到連結串列中,這樣就不會像阮阮一樣造成記憶體浪費了。

一,連結串列的基本結構:

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跳過並且釋放掉他的記憶體。

下次部落格打算寫一下連結串列的進階(像反轉之類的),還要去學習(其實已經有學了只是太多不好寫所以多分幾次)。