链表相关知识

前置知识:

内存区域:

在这里插入图片描述

一.内存四区:

  1. 代码区:函数代码 存放在代码区 函数名就是函数地址。
  2. 全局区:全局的变量 字符串常量 初始化 int a = 0;
  3. 栈 区:告诉计算机 int double char 定义一个变量c,系统开辟 释放。
  4. 堆 区:自己确定有多大,装什么数据?用完之后是否继续用?自己决定开辟,释放。

例如:其中的b,c,d就是定义在栈区。

int main(){
	int b;
	float c;
	double d;
	}

4个字节可以用以下存放:
1.char c[4];
2.int
3.float
4.short c[2];

#include<stdlib.h>  
int *p;
p =  (int *)malloc(size);  //在堆区开辟size个字节的内存,指针p指向这段内存
//(用malloc开辟的内存,不会自己释放,需要主动释放)
free(p);   //释放

任何数据都可以用malloc()。

二、数据结构:

用一套具体的方法,管理这些内存。高效有序

  • 链表:内存之间互相连接的一种数据结构。(两种:空头链表,无空头链表)
  • 结点:每一块内存。
  • 数据:每一个结点里面,存放数据。

链表的操作:

(一)链表的头尾添加:

在这里插入图片描述

g_pEnd->pNext = pTemp;      
g_pEnd = pTemp;

在这里插入图片描述

pTemp->pNext = g_pHead;    
g_pHead = pTemp;
#include<stdio.h>
#include<stdlib.h>
struct Node
{
    int a;//数据成员
    struct Node* pNext;//指向下一个结点的指针
};
//链表头尾指针
struct Node* g_pHead = NULL;
struct Node* g_pEnd = NULL;
//创建链表,在链表中增加一个数据。(尾添加)
void AddListTill(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;
        g_pEnd = pTemp;
    }
    else    //正常情况,往尾巴上添加
    {
        g_pEnd->pNext = pTemp;
            // 尾巴一直指向最后一个节点
        g_pEnd = pTemp;
    //逻辑顺序:先把新的节点接到尾上,再挪动尾结点,使他一直指向最后一个节点。
    }
}

//创建链表,在链表中增加一个数据。(头添加)
void AddListHead(int a)
{
    //创建一个节点
    struct Node* pTemp = (struct Node*)malloc(sizeof(struct Node);
    //节点数据赋值
    pTemp->a = a;
    pTemp->pNext = NULL;
    //接在链表上
    if(NULL == g_pHead)
    {
        g_pHead = pTemp;
        g_pEnd = pTemp;
    }
    else
    {
        pTemp->pNext = g_pHead;
        g_pHead = pTemp;
    //逻辑顺序:先把新的结点接到头上,再挪动头结点,使他一直指向第一个节点。
    }
}



int main()
{
    int a[8] = {1,2,3,4,5,6,7,8};
    int i;
    g_pHead;
    for(i = 0;i<8;i++){
    AddListTill(a[i]);
    AddListHead(a[i]);
    }
    return 0;
}

(二):链表的遍历:

在这里插入图片描述

1. 链表的全部遍历:

注:操作链表时,头指针不能变,可以定一个中间指针(pTemp)

int main()
{
    int a[8] = {1,2,3,4,5,6,7,8};
    int i;
    g_pHead;
    for(i = 0;i<8;i++){
    AddListTill(a[i]);
    //AddListHead(a[i]);
    }
    ScanList();
    return 0;
}

void ScanList()
{
    struct Node *pTemp = g_pHead;
    while(pTemp != NULL)
    {
        pTemp = pTemp->pNext;
        //逻辑:pTemp指向pTemp的后一个,直到遍历结束
        printf("%d\n",pTemp->a);
    }
}

2. 查询指定结点:

int main()
{
    int a[8] = {1,2,3,4,5,6,7,8};
    int i;
    g_pHead;
    for(i = 0;i<8;i++){
    AddListTill(a[i]);
    //AddListHead(a[i]);
    }

   struct Node *pFind = SelectNode(5);
     if(pFind != NULL)
     {
        printf("%d\n",pFind->a);
     }
    else
    {
        printf("没找到!");
    }
    return 0;
}



struct Node* SelectNode(int a)
{
    struct Node *pTemp = g_pHead;
    while(pTemp != NULL)
    {
       if(a == pTemp->a)
       {
           return pTemp;
       }
        pTemp = pTemp->pNext;
    }
    //没找到
    return NULL;
}

(三)链表清空:

一个节点一个节点的遍历,释放。
在这里插入图片描述

void FreeList()
{
    //记录头,防止头被修改,丢内存
    struct Node *pTemp = g_pHead;
    while(pTemp != NULL)
    {
       struct Node* pt = pTemp;
       pTemp = pTemp->pNext;
       free(pt);
    }
    //头尾清空
    g_pHead = NULL;
    g_pEnd = NULL;
}

(四)指定位置插入节点:

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

(五)头删除:

栈 = 头添加+头删除;

在这里插入图片描述

逻辑:

在这里插入图片描述

void DeleListHead()
{
    //链表检测
    if(NULL == g_pHead)
    {
        printf("链表为空,无需释放!\n");
        return;
    }
    //记住旧的头
    struct Node* pTemp = g_pHead;
    //头的下一个节点变成新的头
    g_pHead = g_pHead->pNext;
    //释放旧的头
    free(pTemp);
}
队列 = 头添加+尾删除(尾添加+头删除)

在这里插入图片描述

(六)尾删除:

逻辑:
在这里插入图片描述

void DeleListTail()
{
    //链表检测
    if(NULL == g_pHead)
    {
        printf("链表为空,无需释放!\n");
        return;
    }
    //链表不为空,考虑链表元素个数
    //链表有一个节点
    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 DeleListRand(int a)
{
    //链表判断
    if(NULL == g_pHead)
    {
        printf("链表为空,无需释放!\n");
        return;
    }
   //链表有东西,找这个节点
   struct Node* pTemp = SelectNode(a);
   if(NULL == pTemp)
   {
       printf("查无此节点\n");
       return;
   }
   //找到了
   //只有一个节点
   if(g_pHead == g_pEnd)
   {
       DeleListHead();
   }
   //有两个节点
   else if(g_pHead->pNext == g_pEnd)
   {
       if(g_pHead == pTemp)
       {
           DeleListHead();
       }
       else
       {
           DeleListTail();
       }
   }
   //有多个节点
   else
   {
       if(g_pHead == pTemp)
       {
           DeleListHead();
       }
       else if(g_pEnd == pTemp)
       {
           DeleListTail();
       }
       //删除中间节点
       else
       {
        //找到要删除的节点的前一个节点
        struct Node* pT = g_pHead;
        while(pTemp->pNext != pTemp)
        {
            pT = pT->pNext;
        }
        //找到了
        //链接
        pT->pNext = pTemp->pNext;
        //释放
        free(pTemp);
       }
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

且陶陶º

感谢大人投喂~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值