1. 单向链表的设计

例程:创建一个动态单向链表 1.定义链表结点:数据域+指针域

2.定义链表结构体:头结点指针+结点数

3.初始化链表

4.指定位置插入新数据

5.删除指定位置数据

6.获取链表长度

7.依据数据查找所在链表位置

8.返回第一个结点

9.打印链表结点数据

10.释放链表

两个.c文件,一个头文件

linklist.c:

#include"linklist.h"/* 初始化 */
LinkList *init_linklist(void)
{LinkList *list = (LinkList *)malloc(sizeof(LinkList));list->size = 0;//头结点list->head = (LinkList *)malloc(sizeof(LinkList));list->head->data = NULL;list->head->next=NULL;return list;
}//指定位置插入,z
void insert_linklist(LinkList *list, int pos, void *data)
{int count;if(list == NULL){return ;}if(data == NULL){return ;}if(pos<0 || pos >list->size){pos=list->size;}//创建新的结点LinkNode *newnode=(LinkNode *)malloc(sizeof(LinkNode));newnode->data=data;newnode->next=NULL;//找结点//辅助指针变量LinkNode *pcurrent=list->head;int i;for(i=0;i<pos;i++){pcurrent=pcurrent->next;}
#if 0while(pcurrent!=NULL&&pcurrent->next!=NULL){pcurrent=pcurrent->next;count++;if(count=pos){break;}}
#endif//新结点插入链表newnode->next=pcurrent->next;pcurrent->next=newnode;list->size++;}
//删除指定位置的值
void del_by_pos_linklist(LinkList *list, int pos)
{if(list == NULL){return ;}    if(pos<0 || pos >list->size){return ;}//找结点//辅助指针变量LinkNode *pcurrent=list->head;int i;for(i=0;i<pos;i++){pcurrent=pcurrent->next;}//删除结点LinkNode *delnode=pcurrent->next;pcurrent->next=delnode->next;free(delnode);list->size--;}//获取链表长度int size_linklist(LinkList *list){return list->size;}
//查找
int find_linklist(LinkList *list, void *data)
{if(list == NULL){return -1;}if(data == NULL){return -2;}//遍历查找LinkNode *pcurrent = list->head->next;int i=1;while(pcurrent!=NULL){if(pcurrent->data==data){break;}i++;pcurrent=pcurrent->next;}return i;
}
//返回第一个结点
void *first_linklist(LinkList *list)
{return list->head->next->data;
}
//打印链表结点
void print_linklist(LinkList *list, printlinknode print)
{if(list == NULL){return ;}//辅助指针变量LinkNode *pcurrent=list->head->next;while(pcurrent!=NULL){print(pcurrent->data);pcurrent=pcurrent->next;}
}
//释放链表内存
void free_linklist(LinkList *list)
{if(list == NULL){return ;}//辅助指针变量LinkNode *pcurrent=list->head;while(pcurrent!=NULL){//缓存下一个结点LinkNode *pnext=pcurrent->next;free(pcurrent);pcurrent=pnext;}//释放链表内存list->size=0;free(list);
}

linklist.h

#ifndef LINKLIST_H
#define LINKLIST_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>//链表节点
typedef struct NODE
{void *data;struct NODE *next;
}LinkNode;
//链表结构体
typedef struct
{LinkNode *head;int size;
}LinkList;
//打印函数指针
typedef void (*printlinknode)(void *);
//初始链表
LinkList *init_linklist(void);
//指定位置插入
void insert_linklist(LinkList *list, int pos, void *data);
//删除指定位置的值
void del_by_pos_linklist(LinkList *list, int pos);
//获取链表长度
int size_linklist(LinkList *list);
//依据数据查找所在位置
int find_linklist(LinkList *list, void *data);
//返回第一个结点
void *first_linklist(LinkList *list);
//打印链表结点
void print_linklist(LinkList *list, printlinknode print);
//释放链表内存
void free_linklist(LinkList *list);
#endif

main.c

#include "linklist.h"
typedef struct
{char name[64];int age;int score;
}Person;
//打印函数
void my_print(void *data)
{Person *p=(Person *)data;printf("name:%s age:%d score:%d\n",p->name,p->age,p->score);
}int main()
{//创建链表LinkList *list=init_linklist();//获取链表长度printf("list 长度:%d\n",size_linklist(list));//创建数据Person p1={"大明",18,96};Person p2={"小明",19,97};Person p3={"老明",20,98};//数据插入链表insert_linklist(list, 0, &p1);//0表示头插法insert_linklist(list, 0, &p2);//打印print_linklist(list, my_print);printf("\n");//指定位置1处,插入insert_linklist(list, 1, &p3);//打印print_linklist(list, my_print);printf("\n");//依据数据找所在位置int ret=find_linklist(list, &p3);printf("\"老明\"所在位置:%d\n",ret);//指定位置1处,删除del_by_pos_linklist(list, 1);//打印print_linklist(list, my_print);printf("\n");//获取链表长度printf("list 长度:%d\n",size_linklist(list));//返回第一个结点Person *p=(Person *)first_linklist(list);printf("第一个结点:name-%s,age-%d,score-%d\n",p->name,p->age,p->score);//销毁free_linklist(list);return 0;
}

2. 单向循环链表

2.1 什么是单向循环链表?

如果把单链表的最后一个节点的指针指向链表头部,而不是指向NULL,那么就构成了一个单向循环链 表,通俗讲就是把尾节点的下一跳指向头结点使其形成一个闭环。

2.2 为什么要使用单向循环链表?

在单向链表中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破 坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。

2.3 单向循环链表我们需要注意两个问题:

在链表中我们使用的是虚拟头结点,但是在循环链表中我们在遍历的时候就会遇到麻烦,因此在单向循 环链表中我们使用的是真实头结点。

循环单链表没有明显的结束条件,当我们不注意时,很容易陷入死循环。此时,我们可以设置一个标记 点作为循环的标记,当我们知道表长,我们也可以设置一个计数器来结束循环。

单向循环链表插入节点:中间插法

2.4 单向循环链表设计

创建一个动态单向循环链表:

.定义链表结点:数据域+指针域

2.定义链表结构体:头结点指针

3.初始化链表

4.指定位置插入新数据(头插法,中间插法)

5.删除指定位置数据

6.获取链表长度

7.依据数据查找所在链表位置

8.返回第一个结点

9.打印链表结点数据

10.释放链表

例:实现单向循环链表的创建,增删改查及打印的功能

#include<stdio.h>
#include<string.h>
#include<stdlib.h>typedef struct node{int data;struct node *next;
}link_t;link_t *link_init(void);
static void insert_behind(link_t *p, link_t *node);
void link_add_tail(link_t *p, int d);
void display(link_t *p);
void link_del(link_t *p, int d);
void link_update(link_t *p, int old, int new);
int link_find(link_t *p, int d);int main()
{link_t *phead = link_init();for(int i = 0; i < 6; i++){link_add_tail(phead, i); } display(phead);link_del(phead, 3);display(phead);link_update(phead, 2, 22);display(phead);int ret = link_find(phead, 4);printf("找到节点的位置是%d\n", ret);display(phead);return 0;
}
//在堆中申请空间并进行初始化
link_t *link_init(void)
{//申请空间link_t *p = (link_t *)malloc(sizeof(link_t));if(p == NULL){perror("initError");return NULL;}//给该空间的成员赋值p->next = p;return p; }static void insert_behind(link_t *p, link_t *node)
{node->next = p->next;p->next = node;
}/**************************************@brief 插入 尾插*@param p : 头结点的地址*@param d : 需要插入的数据************************************/
void link_add_tail(link_t *p, int d)
{//保存头结点,以便后期使用 link_t *head = p;//创建一个新的结点link_t *node = (link_t *)malloc(sizeof(link_t));if(node == NULL){perror("addCreateError");exit(1);  }node->data = d;while(p->next != head){p = p->next;}//将新结点插入尾巴后面insert_behind(p, node);
}
/**************************************@brief 删除*@param p : 头结点的地址*@param d : 需要删除的数据***********************************
*/
void link_del(link_t *p, int d)
{link_t *head = p;link_t *delnode = NULL;//遍历去寻找dwhile(p->next != head){if(p->next->data == d){delnode = p->next;p->next = delnode->next;free(delnode);delnode = NULL;continue;}p = p->next;  }
}/**************************************@brief 修改*@param p : 头结点的地址*@param d : 需要删除的数据***********************************
*/
void link_update(link_t *p, int old, int new)
{link_t *head = p;//找到old的值while(p->next != head){if(p->next->data == old){p->next->data = new; }p = p->next;   }
}/**************************************@brief 查找一个节点*@param p : 头结点的地址*@param d : 需要插入的数据*@retval***********************************
*/
int link_find(link_t *p, int d)
{link_t *head = p;int count = 0;while(p->next != head){if(p->next->data == d){return count + 1;}count++;p = p->next;}return -1;
}/**************************************@brief 遍历*@param p : 头结点的地址*@param d : 需要插入的数据*@retval***********************************
*/
void display(link_t *p)
{link_t *head = p;printf("遍历结果为:\n");while(p->next != head){p = p->next;printf("%d->", p->data);}printf("\b\b\n");
}

3. 双链表

就是在单链表的的每个结点中,再设置一个指针域,也就是每个结点都有两个指针域,一个指向它的前 驱结点,一个指向它的后继结点。

虽然使用单链表能 100% 解决逻辑关系为 "一对一" 数据的存储问题,但在解决某些特殊问题时,单链表 并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无 疑是灾难性的,因为单链表更适合 "从前往后" 找,而 "从后往前" 找并不是它的强项。

为了能够高效率解决类似的问题,本节来学习双向链表(简称双链表)。

从名字上理解双向链表,即链表是 "双向" 的。

1. 指针域:用于指向当前节点的直接前驱节点;

2. 数据域:用于存储数据元素。

3. 指针域:用于指向当前节点的直接后继节点;

例:双向链表的创建,销毁,增(头部添加)删改查及显示。

#include<string.h>
#include<stdio.h>
#include<stdlib.h>/*双向链表结构*/
typedef struct List
{int data;struct List *next;struct List *front;
}NODE;NODE *CreatList();
void insertNode(NODE *head, int data);
void DeleteNode(NODE *head, int data);
void updateNode(NODE *head, int oldval, int newval);
int FindNode(NODE *head, int val);
void displayList(NODE *head);
void DestoryList(NODE *head);int main()
{NODE *head = CreatList();if(head == NULL){perror("headCreateError");exit(1);}//给双向链表插入数据for(int i = 0; i <= 6; i++){insertNode(head, i);}//显示链表数据displayList(head);DeleteNode(head, 4);displayList(head);updateNode(head, 2, 222);displayList(head);int ret = FindNode(head, 222);printf("要找的结点是:%d\n", ret);DestoryList(head);return 0;
}/* 销毁链表 */
void DestoryList(NODE *head)
{NODE *tmp;while(head->next != NULL){tmp = head;head = head->next;free(tmp);tmp = NULL;   }free(head);head = NULL;printf("空间已释放\n");
} /* 双向链表的遍历 */
void displayList(NODE *head)
{printf("------------------\n");if(head == NULL){perror("headEmptyError");exit(1);}NODE *tmpHead = head;while(tmpHead->next != NULL){tmpHead = tmpHead->next;printf("%d->", tmpHead->data);  }//倒着遍历while(tmpHead->front->front != NULL){printf("%d<-", tmpHead->data);tmpHead = tmpHead->front;  }printf("%d\n", tmpHead->data);
} //寻找值
int FindNode(NODE *head, int val)
{int count = 1;if(head == NULL){perror("headEmptyError");return -1;}NODE *tmpHead = head;while(tmpHead->next != NULL){tmpHead = tmpHead->next;if(tmpHead->data == val){return count; }count++; }return -1;
}void updateNode(NODE *head, int oldval, int newval)
{if(head == NULL){perror("headEmptyError");exit(1);}NODE *tmpHead = head;while(tmpHead->next != NULL){tmpHead = tmpHead->next;if(tmpHead->data == oldval){tmpHead->data = newval;continue; }}
}/* 删除一个结点 */
void DeleteNode(NODE *head, int data)
{if(head == NULL){perror("headEmpty");exit(1);  }NODE *tmpHead = head;while(tmpHead->next != NULL){tmpHead = tmpHead->next;if(tmpHead->data == data){tmpHead->front->next = tmpHead->next;tmpHead->next->front = tmpHead->front;break;    }   }free(tmpHead);tmpHead = NULL;
}/* 双向链表插入节点,头插法 */
void insertNode(NODE *head, int data)
{if(head == NULL){perror("headNullError");exit(1); }NODE *temHead = head;//创建一个临时结点,指向头结点if(temHead->next == NULL){/* 当双向链表只有一个头结点 */NODE *addition = (NODE *)malloc(sizeof(NODE));if(addition == NULL){perror("additionError");exit(1);  }addition->data = data;addition->next = temHead->next;temHead->next = addition;addition->front = temHead;    }else{/* 当双向链表除了头结点还有其他数据时 */NODE *addition = (NODE *)malloc(sizeof(NODE));if(addition == NULL){perror("additionCreateError");exit(1); }addition->data = data;temHead->next->front = addition;addition->front = temHead;addition->next = temHead->next;temHead->next = addition; }
}NODE *CreatList()
{NODE *head = (NODE *)malloc(sizeof(NODE));if(head == NULL){perror("CreateNodeError");return NULL; }head->next = head->front = NULL;//链表初始化return head;
}

Day 62 数据结构(单向链表,单向循环链表,双向链表)相关推荐

  1. C语言之链表:单向链表,循环链表,双向链表

    C语言之链表:单向链表,循环链表,双向链表 提起链式存储结构,其与数组是两个非常基础的数据结构,每当提到链式存储结构时,一般情况下我们都会将其与数组放到一块儿来比较. 对于数组与链表,从结构上来看,数 ...

  2. 可由一个尾指针唯一确定的链表有_L2数据结构第08课 单向链表和循环链表

    L2-数据结构-第08课 单向链表和循环链表 线性表 线性表是一种常用的数据结构,其中的每一个元素(结点)都有唯一的前驱和唯一的后续.当然,第一个元素只有后续,最后一个元素只有前驱. 线性表一般分为& ...

  3. 数据结构 结构的声明 一个结构作为另一个结构的成员 单向链表的实现 双向链表的实现

    *******************************什么是结构体******************************************************** ****** ...

  4. 数据结构之链表---单向链表

    逻辑结构: 集合结构:所有数字可以看做一个整体 线性结构:一条有顺序的线把所有的数字串起来 树状结构:所有数字都是从一个数字开始向一个方向扩展出来的 网状结构:任何两个数字之间都有直接的联系,不同数字 ...

  5. 数据结构——线性表:顺序表、单向链表、循环链表、双向链表

    线性表   是一种数据结构:n个数据元素的有限序列 表示形式: L = (a1,a2...an) a1是线性表的元素,小写. n=0时候为空表 数据元素具有相同特性 相邻元素之间存在序偶关系:即有唯一 ...

  6. 数据结构——数组、单向链表、双向链表

    原文:http://www.cnblogs.com/skywang12345/p/3561803.html 线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列.本章先介绍线性 ...

  7. c++使用单向链表存储一组有序数据_数据结构笔试题基础

    第一章 数据结构与算法 一.算法的基本概念 计算机解题的过程实际上是在实施某种算法,这种算法称为计算机算法. 1.算法的基本特征:可行性,确定性,有穷性,拥有足够的情报. 2.算法的基本要素:算法中对 ...

  8. 数据结构与算法笔记(三)—— 链表(单链表、循环链表、双向链表)

    一.前沿 1.1.为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活. 链表结构可以充分利用计算机内存空间,实现灵活的内 ...

  9. Algorithms_基础数据结构(02)_线性表之链表_单向链表

    文章目录 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 约瑟夫问题 顺序表VS 链表 链表的定义 链表的特点 常见的链表结 单向链表 单向链表的查找 单向链表的插入 头插 尾部插入 中间 ...

  10. Java 单向链表和单向循环链表的代码实现

    这个链表,以前上学的时候,学c语言,还是数据结构的时候,学过.也许也实现过吧.下面我就简单记录下这个链表的实现. 单向链表跟单向循环链表的差别就是:单向链表是有结束位的,指向null的时候,就到结尾了 ...

最新文章

  1. Hexo 个人博客 SEO 优化(3):改造你的博客,提升搜索引擎排名
  2. 超图预览osgb格式倾斜摄影文件
  3. MATLAB从入门到精通-MATLAB结构矩阵的输出
  4. 为什么美国互联网没有“运营”岗?
  5. 设计模式---组合模式
  6. Maven项目错误解决小结
  7. UnityShader之Shader分类篇【Shader资料2】
  8. python asyncio_python asyncio(一)
  9. python读取二进制数据转整形,在python中读取二进制数据(替换C代码)
  10. iOS开发之抓包工具的Charles的初步安装使用:一步一步教你学会抓包工具Charles的使用(下载破解+代理设置+证书配置)
  11. Spring cloud ribbon实现灰度发布
  12. extmail mysql数据库 重启_配置extmail过程详解 | 学步园
  13. 超越阿里云,华为云服务器究竟有多厉害!
  14. OPENGL中GLAD的代码实现过程
  15. 小区物业专属公众号管理办法
  16. 微信HOOK 3.4.5.27 CALL信息留根-2021-12-27
  17. 基于ARIMA模型的空气质量AQI时间序列分析
  18. LDU 2022年2021级测试赛-1
  19. C. Product of Three Numbers
  20. Python爬猫眼电影影评及可视化 Robin NJU

热门文章

  1. Sqlserver的merge into或delete语句堵塞了不加with (nolock)的select语句,锁类型是LCK_M_IS
  2. python functools_Python入门篇-functools
  3. IE退役,Edge接棒,成为Windows 10和Windows 11默认浏览器
  4. csdn积分任务小脚本
  5. Android FileProvider应用
  6. JAVA基础学习精简心得笔记整理
  7. Flexible源码分析
  8. vscode+nwjs开发环境配置
  9. SQL SERVER 行列转换(1)——聚合函数静态转换
  10. 彻底让你搞懂什么是Java字节码