C:無空頭鏈表

1.鏈表的結構體以及全局變量聲明函數

#include <stdio.h>
#include <stdlib.h>

// 建立Node結構體
struct Node
{
    int a;
    struct Node * pNext;
};

//鏈表頭尾指針
struct Node* g_pHead = NULL; //剛開始鏈表頭部爲null(頭部:內容,該節點的內容存儲)
struct Node* g_pEnd = NULL; //剛開始鏈表尾部爲null      頭尾都爲null,表示空頭鏈表指針

 

2.建立鏈表函數(插尾法和插頭法)io

①插尾插入法
//建立鏈表,在鏈表中增長一個數據,尾添加(g_pEnd發生變化,可是g_pHead不變化,只與第一個傳入的Temp有關)
void AddListTail(int a)
{
   //建立一個結點
   struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
   //結點數據進行賦值
   pTemp->a = a;
   pTemp->pNext = NULL;
   //連接
   if(NULL == g_pHead || NULL == g_pEnd) //也就是剛開始,頭尾都是空,其實寫一個就能夠,頭是空,尾就是空
   {
       g_pHead = pTemp; //此時pTemp的地址既是頭也是尾,在以後的調用中,g_pHead始終等於第一次傳入的pTemp變量

  //g_pEnd = pTemp; //g_pEnd賦值成爲pTemp新結點
   }
   else//此時鏈表再也不爲空
    {
        g_pEnd->pNext = pTemp; //g_pEnd尾部指向新來的結點List

  //g_pEnd = pTemp;//指向以後,g_pEnd下移成爲pTemp新的結尾結點
    }
    g_pEnd = pTemp;//在每一次調用函數後,g_pEnd都等於此時傳入的pTemp          g_pHead    g_pEnd     此時因爲是尾插入,g_pEnd地址指向尾部新增,而且以後g_pEnd變爲最後一個
}循環

 

在這裏有一點很巧妙,g_pHead = pTemp只是在第一次參數傳入的時候賦值,可是在第一次傳入參數的最後讓g_pEnd也等於pTemp,這樣作使在以後,g_pEnd指向下一個值的結構體指針也能被g_pHead使用遍歷

同時,若是去打印g_pHead以及g_pEnd的pNext地址能夠發現鏈表

  (1)g_pHead的pNext永遠指向首結點的下一個地址數據

  (2)g_pEnd的pNext是變化的,由於g_pEnd結點在變化,pNext指向當前的下一跳結點查詢

②插頭插入法

//建立鏈表,在鏈表中增長一個數據,頭添加(g_pHead發生變化,可是g_pEnd不變化,只與第一個傳入的Temp有關)

void AddListHead(int a)
{
   //建立一個結點
   struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
   //結點數據進行賦值
   pTemp->a = a;
   pTemp->pNext = NULL;
   //連接
   if(NULL == g_pHead || NULL == g_pEnd) //也就是剛開始,頭尾都是空,其實寫一個就能夠,頭是空,尾就是空
   {
       g_pHead = pTemp;            //此時pTemp的地址既是頭也是尾,在以後的調用中,g_pHead始終等於第一次傳入的pTemp

  g_pEnd = pTemp;            //g_pEnd賦值成爲第一個傳入的pTemp,以後再也不改變                    
   }
   else//此時鏈表再也不爲空
    {
       g_pTemp->pNext = g_pHead;

  g_pHead = g_pTemp;                                g_pHead    g_pEnd     此時因爲是頭插入,新增的g_pTemp地址指向頭部,而且以後g_pHead變爲第一個
    }
}

3.鏈表的遍歷

①所有遍歷

void ScanList()
{
    struct Node *pTemp = g_pHead; //初始化*pTemp爲頭部,若是不初始化,則pTemp爲最後一位,在本案例爲10
    while(pTemp != NULL)//當鏈表結點的內容指向null,說明遍歷結束
    {
        printf("%d\n", pTemp->a);
        pTemp = pTemp->pNext; //每一次循環指向鏈表前一個結點
    }
}

在main函數中

直接調用ScanList()

②查詢指定結點  經過返回結構體類型,而後經過結構體類型變量pFind來接收

struct Node * SelectNode(int a)
{
    struct Node *pTemp = g_pHead; //初始化*pTemp爲頭部,若是不初始化,則pTemp爲最後一位,在本案例爲10
    while(pTemp != NULL)//當鏈表結點的內容指向null,說明遍歷結束
    {
        if (pTemp->a == a)
        {
            return pTemp;
        }
        pTemp = pTemp->pNext; //每一次循環指向鏈表前一個結點
    }
    return NULL; // 若是找不到返回null
}

在main函數中

    struct Node * pFind = SelectNode(-1);
    if (pFind != NULL)
    {
        printf("%d\n", pFind->a);
    }
    else
    {
        printf("鏈表中沒有該值\n");
    }

 

4.鏈表清空

①清空列表

void FreeList()
{
    struct Node *pTemp = g_pHead; //初始化*pTemp爲頭部,若是不初始化,則pTemp爲最後一位,在本案例爲10
    while(pTemp != NULL)//當鏈表結點的內容指向null,說明遍歷結束
    {
        struct Node * pt = pTemp;
        pTemp = pTemp->pNext; //每一次循環指向鏈表前一個結點
        free(pt);
    }

    // 頭尾清空
    g_pHead = NULL;
    g_pEnd = NULL;
}

在這裏,重要的是free要放在最後free

若是free(pTemp)再去指向,則pTemp已經爲空,沒有指針的做用,若是先指向以後直接free(pTemp)則會致使指向失敗

關鍵是經過結構體指針變量來接收以後,free該變量。最後千萬記得去頭尾清空,否則g_pHead以及g_pEnd變爲野指針,再也不是NULL

此時必定要置野指針爲NULL

 

5.指定位置插入結點

void AddListRand(int index, int a)
{
    //鏈表爲空
    if (NULL == g_pHead)
    {
        printf("鏈表沒有結點\n");
        return;
    }
    //找位置
    struct Node * pt = SelectNode(index);
    if (NULL == pt)
    {
        printf("沒有指定結點\n");
        return;
    }

    //有此結點
    //給a建立結點
    struct Node * pTemp = (struct Node*)malloc(sizeof(struct Node));
    //給結點成員進行賦值
    pTemp->a = a;
    pTemp ->pNext = NULL;

    //連接到鏈表上
    if (pt == g_pEnd)
    {
        g_pEnd->pNext = pTemp;
        g_pEnd = pTemp;
    }
    else
    {
        //先連
        pTemp->pNext = pt->pNext;
        //後斷
        pt->pNext = pTemp;
    }
}

 

6.刪除結點

①頭刪除

void DeleteListHead()
{
    if (NULL == g_pHead)
    {
        printf("鏈表爲NULL,無需釋放\n");
        return;
    }
    //記錄舊的頭
    struct Node*pTemp = g_pHead;
    //頭的下一個結點變成新的頭
    g_pHead = g_pHead->pNext;
    //釋放舊的頭
    free(pTemp);
}

   從鏈表的首項開始刪除

②尾刪除

void DeleteListEnd()
{
    //判斷鏈表是否爲空
    if (NULL == g_pEnd)
    {
        printf("鏈表爲NULL,無需釋放\n");
        return;
    }

    //鏈表不爲空
    //鏈表有1個結點
    if (g_pHead == g_pEnd)
    {
        free(g_pHead);
        g_pHead = NULL;
        g_pEnd = NULL;
    }
    else
    {
        //找到尾巴前一個結點
        struct Node * pTemp = g_pHead;
        while (pTemp->pNext != g_pEnd)
        {
            pTemp = pTemp->pNext;
        }
        //找到了,刪除尾巴
        //釋放尾巴
        free(g_pEnd);
        //尾巴前移
        g_pEnd = pTemp;
        //尾的下一個指針賦值NULL
        g_pEnd->pNext = NULL;
    }
}

  從列表的尾部開始刪除,思想:首先從鏈表的首項開始遍歷,找到最後一項的前一個則中止循環

③指定位置刪除

void DeleteListRand(int a){    if (NULL == g_pHead)    {        printf("鏈表爲NULL,無需釋放\n");        return;    }    //鏈表有東西,找這個結點    struct Node* pTemp = SelectNode(a);    if (NULL == pTemp)    {        printf("查無此結點\n");        return;    }    //找到了    //只有一個結點    if (g_pHead == g_pEnd)    {        free(g_pHead);        g_pHead = NULL;        g_pEnd = NULL;    }    //有兩個結點    else if (g_pHead->pNext == g_pEnd)    {        if (g_pHead == pTemp)        {            DeleteListHead();        }        else        {            DeleteListEnd();        }    }    else //有多個結點    {        if (g_pHead == pTemp)        {            DeleteListHead();        }        else if (g_pEnd == pTemp)        {            DeleteListEnd();        }        else        {            //找刪除結點的前一個結點            struct Node * pT = g_pHead;            while (pT->pNext != pTemp)            {                pT = pT->pNext;            }            //找到了            //連接            pT->pNext = pTemp->pNext;            //釋放            free(pTemp);        }    }}

相關文章
相關標籤/搜索