一.链表的概念:
定义:
链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
特点:
链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成 (malloc),每个节点包括两个部分:
一个是存储数据元素的数据域
另一个是存储下一个节点地址的指针域
![](https://img-blog.csdnimg.cn/img_convert/da6699b4bff34ae5993237ab20eb26b2.png)
链表的构成:
链表通常是采用结构体的形式组织,例如:
typedef struct node {
data_t data;//数据域,用来存放每个节点的数据
struct node *next;//指针域,用来指向下一个节点
}link_t;
![](https://img-blog.csdnimg.cn/img_convert/0ea3b65784524f4bbc26b3b43d036444.png)
链表的操作
链表最大的作用是通过节点把离散的数据链接在一起,组成一个表,这大概就是链表 的字面解释了吧。 链表常规的操作就是节点的插入和删除,为了顺利的插入,通常一条链 表我们会人为地规定一个头节点。通常头节点还会有一个节点计数器,用于统计整条链表的节点个数。
![](https://img-blog.csdnimg.cn/img_convert/f53797a214b5478ea400d1233da2f3c0.png)
链表与数组的对比
链表是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现 对数据的存取。而数组是通过开辟一段连续的内存来存储数据,这是数组和链表最大的区 别。数组的每个成员对应链表的节点,成员和节点的数据类型可以是标准的 C 类型或者是 用户自定义的结构体。数组有起始地址和结束地址,而链表是一个圈,没有头和尾之分, 但是为了方便节点的插入和删除操作会人为的规定一个根节点。
![](https://img-blog.csdnimg.cn/img_convert/c9ded5f3d69f42988edd48dfdd308cc6.png)
链表.h文件的封装
#ifndef _LINKLIST_H
#define _LINKLIST_H
typedef int data_t;//给int型变量起别名,防止如果要插入的数据不是int型
typedef struct node{
data_t data;//数据域
struct node *next;//指针域
}link_t;
//链表的创建
link_t *linklist_create();
//遍历链表
void linklistShow(link_t *head);
//按位置插入元素
int linklistInsertPos(link_t *head,int pos,data_t data);
//求链表的长度
int linklistGetLength(link_t *head);
//按位置删除节点
int linklistDeletePos(link_t *head,int pos);
//判空
int linklistIsEmpty(link_t *head);
//按位置查找
link_t *linklistSearchPos(link_t *head,int pos);
//按数据查找
link_t *linklistSearchData(link_t *head,data_t data);
//按数据删除
int linklistDeleteData(link_t *head,data_t data);
//按位置修改数据
int linklistUpdataPos(link_t *head,int pos,data_t newdata);
//按数据修改数据
int linklistUpdataData(link_t *head,data_t olddata,data_t newdata);
//清空链表
int linklistClear(link_t *head);
//销毁链表
int linklistDestory(link_t *head);
//倒置
int linklistInvert(link_t *head);
//排序
int linklistSort(link_t *head);
#endif
链表.c文件的封装
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#incldue <assert.h>
#include "linklist.h"
link_t *linklist_create()//链表头节点的创建
{
link_t *head = (link_t *)malloc(sizeof(link_t));
assert(NULL != head);
head->data = -1;//头节点不存在有效元素
head->next = NULL;
return head;
}
void linklistShow(link_t *head)//遍历
{
if(NULL == head)
{
return;
}
link_t *p = head->next;//新建一个指针指向头节点的下一个
while(p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
puts(" ");
}
int linklistInsertPos(link_t *head,int pos,data_t data)
{
//判断链表是否存在
if(NULL == head)
{
return -1;
}
//判断位置是否合法
int len = linklistGetLength(head);
if(pos < 0 || pos > len)//插入位置小于或大于链表长度
{
return -1;
}
//创建新节点
link_t *new = (link_t *)malloc(sizeof(link_t));
new->data = data;
//找到插入位置前一个节点
link_t *p = head;
for(int i = 0;i < pos;i++)
{
p = p->next;
}
new->next = p->next;
p->next = new;
return 0;
}
int linklistGetLength(link_t *head)//获得有效长度
{
if(NULL == head)
{
return -1;
}
int len = 0;
link_t *p = head;
while(p->next != NULL)
{
len++;
p = p->next;
}
return len;
}
int linklistDeletePos(link_t *head,int pos)
{
//判表存在
if(NULL == head)
{
return -1;
}
//判空
if(linklistIsEmpty(head))
{
return -1;
}
//判断位置是否合法
int len = linklistGetLength(head);
if(pos < 0 || pos > len-1)
{
return -1;
}
//找到要删除位置的前一个位置
link_t *p = head;
for(int i = 0;i < pos;i++)
{
p = p->next;
}
//删除
link_t *q = p->next;//q保存的是要删除的节点地址
p->next = q->next;
free(q);//删除后要释放q的空间
q = NULL;
return 0;
}
int linklistIsEmpty(link_t *head)//判空
{
if(NULL == head)
{
return -1;
}
return head->next == NULL;
}
link_t *linklistSearchPos(link_t *head,int pos ,data_t data)//按位置查找
{
if(NULL == head)
{
return NULL;
}
int len = linklistGetLength(head);
if(pos < 0 || pos > len-1)
{
return NULL;
}
link_t *p = head->next;//查找的位置就是p的位置
for(int i = 0;i < pos;i++)
{
p = p->next;
}
return p;
}
link_t *linklistSearchData(link_t *head,data_t data)//按数据查找
{
if(NULL == head)
{
return NULL;
}
link_t *p = head->next;
while(p != NULL)
{
if(p->data == data)
{
return p;
}
p = p->next;//如果不是该数据往下查找
}
reutrn NULL;
}
int linklistDeleteData(link_t *head,data_t data)//按数据删除
{
if(NULL == head)
{
return -1;
}
link_t *p = head;
while(p->next != NULL)
{
if(p->next->data == data)
{
link_t *q = p->next;//保存要删除的地址
p->next = q->next;
free(q);
q = NULL;
return 0;//删除完后正确返回
}
p = p->next;//如果没有找到该数据则遍历表
}
return -1;//如果没有说明没有该数据,错误返回
}
int linklistUpdataPos(link_t *head,int pos,data_t newdata)//按位置修改数据
{
if(NULL == head)
{
return -1;
}
//查找这个数据
link_t *p = linklistSearchPos(head,pos);
if(NULL == p)
{
return -1;
}
p->data = newdata;
return 0;
}
int linklistUpdataData(link_t *head,data_t olddata,data_t newdata)
{
if(NULL == head)
{
return -1;
}
link_t *p = linklistSearchData(head,olddata);
if(NULL == p)
{
return -1;
}
p->data = newdata;
return 0;
}
int linklistClear(link_t *head)
{
if(NULL == head)
{
return -1;
}
while(head->next != NULL)
{
linklistDeletePos(head,0);
}
return 0;
}
int linklistDestory(link_t **head)
{
if(NULL == *head)//
{
return -1;
}
linklistClear(*head);//通过指向头节点的地址一个一个删头节点后的表
free(*head);//最后删完释放这篇空间
*head = NULL;
return 0;
}
int linklistInvert(link_t *head)//倒置
{
if(NULL == head)
{
return -1;
}
if(linklistIsEmpty(head))
{
return -1;
}
link_t *p =head->next;
head->next = NULL;//将头节点和后面节点断开
link_t *q;
while(p != NULL)
{
q = p->next;
p->next = head->next;
head->next = p;
p = q;
}
return 0;
}
int linklistSort(link_t *head)
{
if(NULL == head)
{
return -1;
}
link_t *p = head->next;
link_t *r = NULL,*q = NULL;
head->next = NULL;//将链表断开
while(p != NULL)
{
r = head;//r始终在头节点
q = p->next;//保持待插入节点的下一个节点
while(r->next != NULL && r->next->data)//r->next != NULL 当插入第一个节点的时候,r->next == NULL,不需要进入循环。直接插入在r的后面;
//r->next->data > p->data 时候说明从大到小排列,要插入的元素应该从后面放,所有r需要循环偏移.直到r->next->data < p->data的时候就插入在r的后面;
{
r = r->next;
}
p->next = r->next;//将p插在r后面
r->next = p;
p = q;//将q保存的节点赋值给p,继续下一次循环
}
return 0;
}
链表倒置详解图
![](https://img-blog.csdnimg.cn/img_convert/ae02ee62d3a84e27818d6a38a3cb9f57.png)
main.c函数实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include "linklist.h"
int main()
{
link_t *head = linklist_create();
if(NULL == head)
{
return -1;
}
printf("create success..\n");
for(int i = 1; i < 11;i++)
linklistInsertPos(head,0,i*10);
linklistShow(head);
return 0;
}