基本數據結構 -- 鏈表的遍歷、查找、插入和刪除

  本文將使用 C 語言來實現一個單鏈表,並實現遍歷、查找、插入、刪除等操做。node

1、建立一個單鏈表算法

   首先,定義一個存放結點相關信息的結構體,結構體有兩個元素,分別是鍵值和一個指向下一節點的指針。數據結構

/* 用於存放結點信息的結構體 */
struct node {
    int key;
    struct node *next;
};

typedef struct node Node;
typedef struct node *PtrToNode;

   想要建立一個單鏈表,能夠先建立一個表頭結點(啞結點),而後在表頭結點後不斷插入新的結點便可,須要注意的是,每新建一個結點都要爲該結點分配一段內存空間。spa

/* 建立一個鏈表 */
PtrToNode CreateList(int listLen)
{
    int i, keyValue;

    /* 建立一個表頭結點,併爲其分配內存空間 */
    PtrToNode headPtr = (PtrToNode)malloc(sizeof(Node));

    if (headPtr == NULL) {
        perror("malloc failed!\n");
        exit(EXIT_FAILURE);
    }

    PtrToNode tailNode = headPtr;    // 建立一個表尾結點,並將表頭結點賦給尾結點
    tailNode->next = NULL;

    for (i = 0; i < listLen; i++) {
        /* 建立一個新結點,併爲其分配內存空間 */
        PtrToNode newNode = (PtrToNode)malloc(sizeof(Node));
        if (newNode == NULL) {
            perror("malloc failed!\n");
            exit(EXIT_FAILURE);
        }

        printf("請輸入第 %d 個結點的鍵值:", i + 1);
        scanf_s("%d",&keyValue);

     // 將 newNode 插入鏈表尾部
        newNode->key = keyValue;    // 賦鍵值
        newNode->next = NULL;     // next 指針指向 NULL
        tailNode->next = newNode;   // 這裏的 tailNode 存放的是上一次循環中建立的 newNode,也就是如今新建結點的前驅結點
                       // 故而這裏是將前驅結點的 next 指針指向當前結點

        tailNode = newNode;       // 將當前結點賦給 tailNode(實際上,tailNode 就起着一個臨時結點的做用)
    }

    return headPtr;
}

 

2、遍歷一個單鏈表指針

/* 遍歷鏈表 */
void TraverseList(PtrToNode List)
{
    PtrToNode ptr = List->next;
    if (ptr == NULL) {
        printf("鏈表爲空\n");
    }

    while (ptr != NULL) {
        printf("%d ", ptr->key);
        ptr = ptr->next;
    }
}

   這段代碼根據鏈表表尾結點的 next 指針指向 NULL 來遍歷整個鏈表。code

 

3、查找一個元素blog

/* 查找一個元素 */
PtrToNode FindElement(PtrToNode List,int val)
{
    PtrToNode ptr = List->next;
    if (ptr == NULL) {
        printf("鏈表爲空\n");
        return NULL;
    }

    while (ptr != NULL && ptr->key != val) {
        ptr = ptr->next;
    }

    if (ptr != NULL) {
        printf("找到 %d 了\n", val);
    }
    else
    {
        printf("沒有找到 %d\n", val);
    }    
    
    return ptr;
}

  這段代碼查找元素 val 是否在鏈表中,若是在,則打印元素已找到的信息,並返回該元素在鏈表中所在的結點;若是不在鏈表中,則打印沒找到的信息,並返回一個空指針。內存

 

4、插入一個元素it

/* 插入一個元素 */
void InsertElement(PtrToNode List,PtrToNode Position,int val) 
{
    PtrToNode tmpNode = (PtrToNode)malloc(sizeof(Node));
    if (tmpNode == NULL) {
        perror("malloc failed!\n");
        exit(EXIT_FAILURE);
    }

    tmpNode->key = val;
    tmpNode->next = Position->next;
    Position->next = tmpNode;
}

  這段代碼將元素 val 插入到鏈表中指定結點的後面。 io

 

5、刪除操做

5.1 刪除整個鏈表

/* 刪除整個鏈表 */
void DeleteList(PtrToNode List)
{
    PtrToNode position, tmpNode;
    position = List->next;
    List->next = NULL;
    while (position != NULL) {
        tmpNode = position->next;        // 先將當前結點的 next 指針賦給臨時結點保存
        free(position);                    // 而後釋放當前結點
        position = tmpNode;                // 再將以保存的 next 指針賦給 position,即爲下一個要刪除的結點
    }
}

  刪除整個鏈表時,須要注意一點,要提早將要刪除結點的 next 指針保存下來,再釋放該結點。而不能在釋放了一個結點後再去利用已釋放結點的 next 指針去釋放下一個結點,由於此時上一個結點已經被釋放了,故而找不到 next 指針。此外,因爲在建立鏈表時,每插入一個新的結點都會用 malloc 來給結點分配一塊內存,故而在刪除鏈表時,每釋放一個結點也應該使用 free 來釋放一次內存。

5.2 刪除一個元素

/* 刪除一個元素 */
void DeleteElement(PtrToNode List,int val)
{
    PtrToNode tmpNode;
    PtrToNode prev_position = FindPrevNode(List, val);
    if (prev_position->next == NULL) {
        printf("要刪除的元素不存在!\n");
    }
    else {
        tmpNode = prev_position->next;
        prev_position->next = tmpNode->next;
        free(tmpNode);
    }

}

/* 獲取元素的前驅結點 */
PtrToNode FindPrevNode(PtrToNode List, int val)
{
    PtrToNode prev_position = List;

    while (prev_position->next != NULL && prev_position->next->key != val) {
        prev_position = prev_position->next;
    }

    return prev_position;
}

  刪除一個元素時,須要先找到該元素的前驅結點。 

 

參考資料:

《算法導論 第三版》

《數據結構與算法分析——C語言描述》

相關文章
相關標籤/搜索