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);        }    }}

相关文章
相关标签/搜索