C_12_链表

链表

概述:

是一种数据结构

分为单链表与双链表两种

单链表:

链表种节点是离散的在内存中开辟空间的 因为是离散开辟,内存地址通常不是连续的,地址不一定相邻,甚至可能存在其他数据在它们之间。

在这里插入图片描述

双链表

在这里插入图片描述

1 定义节点

分析:

一个节点就是一个结构体变量。

如:

单链表节点

typedef struct node
{
 //数据域
 结构体成员变量;
 //int  age ;.....
 //地址域
 Node *next;
}Node;

双链表节点

但是Node别名 是在下面定义的 内部怎么使用呢

typedef struct node
{
 //数据域
 结构体成员变量;
 //地址域
// Node *next;  // 但是 Node别名  是在下面定义的 内部怎么使用呢 
// Node *head;  // 使用别名就会报错

     //地址域  
  //解决 使用全名 也就是结构体 加结构体名
     struct node *next;
     struct node *head;
}Node;

2 组建链表节点

2.1 静态组建节点

#include <stdio.h>

// 静态组建双链表节点

typedef struct node
{
 // 数据域
 int num;
 // 地址域
 struct node *next;
 struct node *head;
} Node;
int main(int argc, char const *argv[])
{
 Node n01 = {1, NULL, NULL}; // 节点1
 Node n02 = {2, NULL, NULL}; // 节点2
 Node n03 = {3, NULL, NULL}; // 节点3

 n01.next = &n02;
 n02.head = &n01; // 1 2 链表进行关联上了

 n02.next = &n03;
 n03.head = &n02; // 2 3 链表进行关联上了

 // 链表遍历
 void print_link(Node * head)
 {
     // 将首节点 赋值给当前节点
     Node *n = head;
     // 判断当前节点是否为NULL
     while (n != NULL)
     {
         // 当前节点不为空,打印其数据
         printf("%d\n", n->num);
         // 移动当前节点到下一个节点
         n = n->next;
     }
 }
 print_link(&n01);
 printf("==============\n");
 print_link(&n02);
 return 0;
}

2.2 动态组建节点【*】

可以动态的操作 也就是局限性小

有序的 是指存的顺序 和取出顺序一样

就是怎么存的 怎么取的

比如 4 2 1 3 取出的时候 顺序也是 4 2 1 3

因为动态组建链表节点 是 使用 指针 所以

节点之间的链接方式,不应该使用取地址 & 操作,而应该直接使用节点指针。

>   n01->next = &n02;   错误的  

>   n01->next = n02;    正确的

动态创建链表节点是和静态的创建节点是有区别的

#include <stdio.h>
#include <stdlib.h>
// 动态组建双链表节点
typedef struct node
{
 // 数据域
 int num;
 // 地址域
 struct node *next;
 struct node *head;
} Node;
int main(int argc, char const *argv[])
{
 Node *n01 = (Node *)calloc(1, sizeof(Node)); // 节点1
 Node *n02 = (Node *)calloc(1, sizeof(Node)); // 节点2
 Node *n03 = (Node *)calloc(1, sizeof(Node)); // 节点3
 // 判空
 if (n01 == NULL || n02 == NULL || n03 == NULL)
 {
     return 0;
 }
 // 首先  头节点置空
 n01->head = NULL; // 1 链表的头节点置空

 // 节点之间的链接方式,不应该使用取地址 & 操作,而应该直接使用节点指针。  n01->next = &n02;   错误的
 n01->next = n02; // 1 2 链表就关联上了
 n02->next = n03;
 n03->head = n02; // 2 3 链表就关联上了

 // 尾节点置空
 n03->next = NULL;

 n01->num = 1;
 n02->num = 2;
 n03->num = 3;

 // 链表遍历
 void print_link(Node * head)
 {
     // 将首节点 赋值给当前节点
     Node *n = head;
     // 判断当前节点是否为NULL
     while (1)
     {
         // 当前节点不为空,打印其数据
         printf("%d\n", n->num);
         // 移动当前节点到下一个节点
         if (n->next == NULL)
         {
             break;
         }
         n = n->next;
     }
 }

 // 遍历链表 方式2 
 void print_link1(Node * head)
 {
     Node *jiedian = head;
     while (jiedian != NULL) // 只要有 节点就进行遍历
     {
         // 打印节点数据
         printf("节点数据为 %d\n", jiedian->num);
         // 节点往后移动一个
         jiedian = jiedian->next;
     }
 }

printf("=======\n");
print_link(n01); // 传递的参数应该是节点指针 p,而不是节点指针的地址 &p。
 print_link1(n01);
 printf("=======\n");
 print_link(n03);
 printf("=======\n");
 print_link(n02);
 
 // 手动释放内存
free(n01);
 free(n02);
 free(n03);
 return 0;
 }
 

在这里插入图片描述

3 链表的操作

3.1 添加:

​ 目的: 链表尾部增加

​ 所需条件: 1 给谁添加 (首节点 head)

​ 2 要添加的节点 newNode

​ 函数:

/*
参数:
       head : 链表的第一个位置  就是首节点
        newNode: 要添加的节点
返回值 :
        链表的首节点
*/
// 链表的添加
Node *add_node(Node *head, Node *newNode)
{
 if (head == NULL)
 {
        head = newNode;
        return head;
    }
    Node *n = head; // n 为设置的 当前节点
    while (n->next != NULL)
    {
        n = n->next; // 将当前节点移动到下一节点
    }
    // 当循环结束后n就是最后一个节点 【要是单链表就结束了】
    n->next = newNode;
    // 将新节点的上一个节点设置为原来的最后一个节点 【双链表结束】
    newNode->head = n;
    return head;
}

优化:

/*
参数:
		head : 链表的第一个位置  就是首节点
		newNode: 要添加的节点
返回值 :
		链表的首节点
*/
Node*  add_node(Node *head,Node *newNode)
{
if(head == NULL)
{
  head = newNode;
    return head;
}
Node *n = head; //n 为设置的 当前节点
while( n->next != NULL)
{
 n = n->next;  //将当前节点移动到下一节点
}
//当循环结束后n就是最后一个节点 【要是单链表就结束了】
 n->next = newNode;  
//将新节点的上一个节点设置为原来的最后一个节点 【双链表结束】
 newNode->head = n;
 return head;
}

3.2 遍历

/*
    参数:
         head :要遍历的链表的首节点
*/
void print_link(Node *head) // 遍历节点
{
     // 将首节点 赋值给当前节点
     Node *n = head;
     // 判断当前节点是否为NULL
     while (n != NULL)
     {
           // 当前节点不为空,打印其数据
           printf("%d\n", n->num);
           // 移动当前节点到下一个节点
           n = n->next;
     }
}

3.3 删除

/*
参数:
  head: 头节点
  delNode:要删除的节点
返回值:
  删除后链表的首节点
作用:
  删除指定节点
*/
Node *del_node(Node *head, Node *delNode)
{
     // 判断是否有首节点
     if (head == NULL)
     {
           printf("没有首节点\n");
           return NULL;
     }
     // 判断要删除的节点是不是空的
     if (delNode == NULL)
     {
           printf("要删除的节点为NULL\n");
           return head;
     }
     // 当要删除的节点就是首节点
     if (head == delNode)
     {
           // 要删除的节点为首节点
           // 将下一个节点设为首节点
           head = head->next;
           // 将 先在的首节点的头节点 置空
           head->head = NULL;
           // 释放原来的首节点
           free(delNode);
           return head;
     }
     // 要删除的节点不是首节点,此时寻找要删除的节点
     // n 当前节点
     Node *n = head;
     while (n != delNode && n != NULL)
     {
           n = n->next;
     }
     if (n == NULL)
     {
           printf("没有要删除的节点");
     }
     //  判断要删除的节点就是最后一个节点的时候
     if (n->next == NULL)
     {
           Node *headNode = n->head;
           headNode->next = NULL;
           free(n);
           return head;
     }

     // n就是要删除的节点
     Node *headNode = n->head;  // 取出n的上一个节点
     Node *nextNode = n->next;  // 取出n的下    一个节点
     free(n);                   // 释放n
     headNode->next = nextNode; // 让n的上一个节点的下一个节点为n的下一个节点
     nextNode->head = headNode; // 让n的下一个节点的下一个节点为n的上一个节点
     return head;
}

3.4 修改

/*
参数:
 head:首节点 
 num:查找的值
 newNum:修改后的值
返回值:
无  
作用: 
按值 按位 修改指定节点的值   
// 按值 修改
#include <stdio.h>
void update_link(Node *head, int num, int newNum)
{
    Node *n = find_link(head, num);
    if (n == NULL)
    {
        return;
    }
    n->num = newNum;
}
/*
作用:
	按位置修改指定节点的值
参数:
	head:首节点
	index:修改的位置
	newNum:修改后的值
返回值:
	无
*/
void update_i(Node *head, int index, int newNum)
{
    Node *n = find_i(head, index);
    if (n == NULL)
    {
        return;
    }
    n->num = newNum;
}	

3.5 插入

目的:随意位置插入

/*
参数:
 head:  首节点
 index   要插入的节点位置
    newNode 新节点
   返回值:
      插入后首节点
作用:
    按位置插入新节点

   */
// 思路  按你想要插入的位置插入
Node *insertlink(Node *head, int index, Node *newNode)
{
    if (index == 0)
    {
         newNdode->next = head;
         head->head = newNode;
           head = newNode;
           return head;
       }
       // index 位置原来的节点
     Node *n = find_index_link(head, index)
     {
         Node *n = find_index_link(head, index);
         if (n == NULL)
           {
               add_link(head, newNode);
               // add_link  是添加  链表节点 函数
               return head;
           }
           Node *headNode = n->head;
           headNode->next = newNode;
           newNode->head = headNode;
   
           newNode->next = n;
        n->head = newNode;
           return head;
       }
   }

获取链表长度

/*
参数:
        首节点
作用:
        获取链表长度
返回值:
        链表长度
*/
int get_len(Node *head)
{
     int i = 0;
     Node *n = head;
     while (n != NULL)
     {
           i++;
           n = n->next;
     }
     return i;
}

3.6 查找

按位置进行查找节点

  • 1 按值查找
  • 2 按位查找
/*
作用:
	按结构体中存储的值查找对应的节点
参数:
	 head:首节点
	 num:查找的值
返回值:
	查询到的节点,如果为NULL证明不存在
*/
//【 1 按值查找 】
Node * find_link(Node * head,int num)
{
 Ndoe *n =head;
 while(n != NULL)
 {
     if(n-> num == num)
     {
         return n;
     }
     n = n->next;
 }
 return NULL;
}
// 【 2 按位查找 】
/*
作用:
按结构体在链表中的位置查找对应的节点
参数:
head:首节点
index:下标
返回值:
查询到的节点,如果为NULL证明不存在
*/
Node *find_i(Node *head, int index)
{
   Node *n = head;
   int i = 0;
   while (n != NULL)
   {
       if (i == index)
       {
           return n;
       }
       n = n->next;
       i++;
   }
   return NULL;
}

3.7 逆序

#include <stdio.h>
// 逆序排列
void print_link_r(Node *head)
{
   int len = get_len(head);
   int index = len - 1; // 下标就是长度减一 就是最后一位的下标
   Node *n = find_i(head, index);
   while (n != NULL)
   {
          printf("%d\n", n->num);
          n = n->head;
   }
}

3.8 排序

顺序

Node *sort_link(Node *head)
{
    if (head == NULL)
    {
        return NULL;
    }
    int len = get_len(head);
    for (int i = 0; i < len; i++)
    {
        for (int j = 0; j < len - 1; j++)
        {
            Node *n = find_i(head, j);
            Node *nn = n->next;
            if (n->num > nn->num)
            {
                if (j == 0)
                {
                    Node *nnn = nn->next;
                    nn->next = n;
                    n->head = nn;
                    nn->head = NULL;
                    n->next = nnn;
                    nnn->head = n;
                    head = nn;
                }
                else if (j == len - 2)
                {
                    Node *hn = n->head; // 倒数第三个节点
                    hn->next = nn;
                    nn->head = hn;
                    nn->next = n;
                    n->head = nn;
                    n->next = NULL;
                }
                else
                {
                    Node *hn = n->head;
                    Node *nnn = nn->next;
                    hn->next = nn;
                    nn->head = hn;
                    nn->next = n;
                    n->head = nn;
                    n->next = nnn;
                    nnn->head = n;
                }
            }
        }
    }
    return head;
}

逆序排列

//逆序排列
void print_link_desc(Node *head)
{
    int len = get_len(head);
    int index =len-1 ;   // 下标就是长度减一 就是最后一位的下标
    Node *n = find_i(head,index);
    while (n != NULL)
    {
        printf("%d\n",n->num);
        n = n->head;
    }
}

3.9 释放

#include <stdio.h>
void free_link(Node *head)
{
    Node *n = head;
    while (n != NULL)
    {
        Node *nextNode = n->next;
        free(n);
        n = nextNode;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值