单链表
什么是单链表
线性表的链式存储就是所谓的单链表
单链表的结构
单链表的特点
单链表没有个数限制,在使用的时候是通过动态分配内存的形式来开辟节点的空间的,开辟出来的新的节点和原来的单链表通过一个指针进行关联。
单链表节点的格式
节点的结构体定义
#define datatype int
typedef struct node{
datatype data;
struct node *next;
//这个指针指向的是下一个节点,节点是结构体类型,所以指针也要定义成结构体指针
}linklist_t;
单链表的操作
单链表的创建
linklist_t *LinkListCreate(void)
{
linklist_t *h;
h = (linklist_t *)malloc(sizeof(*h));
if(h==NULL){
printf("malloc head memory error\n");
return NULL;
}
//将数据赋值为0
h->data = (datatype)0;
//将指针域指向NULL
h->next = NULL;
return h;
}
单链表头插法
/*
*功能:单链表头插法插入节点
*参数:
* @h:单链表的头
* @data:被插入的数据
*返回值:成功返回0,失败返回-1
*/
int LinkListInsertHead(linklist_t *h,datatype data)
{
linklist_t *tmp;
//1.分配节点的内存,将data放到这个节点的数据域中
tmp = (linklist_t *)malloc(sizeof(*tmp));
if(tmp == NULL){
printf("alloc node memory error\n");
return -1;
}
tmp->data = data;
//2.将这个节点通过头插法的方式插入到链表中即可
//步骤1:让tmp的next指向h的后一个节点
tmp->next = h->next;
//步骤2:将h的next指针域指向tmp
h->next = tmp;
return 0;
}
单链表尾插法
int LinkListInsertTail(linklist_t* h, datatype data)
{
linklist_t* tmp;
// 1.分配节点的内存,将data放到这个节点的数据域中
tmp = (linklist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("alloc node memory error\n");
return -1;
}
tmp->data = data;
// 2.让h走到最后一个节点的位置
while (h->next) {
h = h->next;
}
// 3.将这个节点通过头插法的方式插入到链表中即可
// 步骤1:让tmp的next指向h的后一个节点
tmp->next = h->next;
// 步骤2:将h的next指针域指向tmp
h->next = tmp;
return 0;
}
单链表位置插入
// ①判断pos是否等于0,如果pos为零,直接将节点插入在当前节点的位置,退出
// ②如果pos不为0, 1.pos-- 2.h = h->next。
// ③ 循环执行上述的①和②步骤,如果输入pos太大(不合法)链表到结尾了也要退出循环
int LinkListInsertByPos(linklist_t* h, int pos, datatype data)
{
linklist_t* tmp;
// 1.判断pos是否合法
if (pos < 0) {
printf("插入的位置不合法\n");
return -1;
}
// 2.通过循环找出插入节点的位置
while (h) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 3.找到了插入节点的位置,将节点插入即可。
tmp = (linklist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("malloc memory error\n");
return -1;
}
tmp->data = data;
tmp->next = h->next;
h->next = tmp;
return 0;
}
}
printf("插入的位置不合法\n");
return -1;
}
单链表的遍历
// 思想:判断h->next是否等于NULL,如果h->next为NULL说明链表没有要访问的成员了。
// :如果h->next不是NULL,说明链表不为空,可以将它的数据域打印出来
// :printf("data:%d\n",h->next->data);
// :执行完上述的步骤之后将h->next赋值给h h = h->next;
// :在循环上述步骤
void LinkListShow(linklist_t *h)
{
while(h->next){
printf("-%d",h->next->data);
h = h->next;
}
printf("-\n");
}
单链表的头删法
datatype LinkListDeleteHead(linklist_t* h)
{
datatype data;
linklist_t* tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp指向h的下一个节点
tmp = h->next;
// 3.让h指向tmp的下一个节点
h->next = h->next->next;
// 4.释放tmp的内存
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
单链表的尾删法
datatype LinkListDeleteTail(linklist_t* h)
{
datatype data;
linklist_t *tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
//2.通过循环让h走到倒数第二个节点的位置
while(h->next->next){
h = h->next;
}
//3.让tmp指向h->next
tmp = h->next;
//4.让h->next指向空
h->next = h->next->next;
//5.释放tmp
data = tmp->data;
if(tmp != NULL){
free(tmp);
tmp = NULL;
}
return data;
}
单链表的位置删除
datatype LinkListDeletePos(linklist_t* h, int pos)
{
datatype data;
linklist_t* tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置删除失败\n");
return (datatype)-1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("删除的位置不合法\n");
return (datatype)-1;
}
// 3.通过循环找出删除节点的位置
while (h->next) {
if (pos > 0) {
pos--;
h = h->next;
} else {
// 4.找到了删除节点的位置,将节点删除即可。
tmp = h->next;
h->next = h->next->next;
data = tmp->data;
if(tmp != NULL){
free(tmp);
tmp = NULL;
}
return data;
}
}
printf("删除的位置不合法\n");
return (datatype)-1;
}
单链表的通过位置查询数据
datatype LinkListCheckDataByPos(linklist_t* h, int pos)
{
datatype data;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置查询失败\n");
return (datatype)-1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("查询的位置不合法\n");
return (datatype)-1;
}
// 3.通过循环找到查询的位置
while (h->next) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 查询到了位置
data = h->next->data;
return data;
}
}
printf("查询的位置不合法\n");
return (datatype)-1;
}
单链表的位置更新数据
int LinkListUpdateDataByPos(linklist_t* h, int pos, datatype data)
{
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置更新失败\n");
return -1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("更新的位置不合法\n");
return -1;
}
// 3.通过循环找到更新的位置
while (h->next) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 更新数据
h->next->data = data;
return 0;
}
}
printf("更新的位置不合法\n");
return -1;
}
单链表的逆序(进行一遍头插)
h-10-20-30-40-
h- tmp-10-20-30-40
h-10 tmp-20-30-40
h-20-10 tmp-30-40
void LinkListReverse(linklist_t* h)
{
linklist_t *tmp,*s;
//先让tmp指向h的下一个节点
tmp = h->next;
//将h->next赋值为NULL
h->next = NULL;
//如果tmp不为NULL,循环继续
while(tmp){
s = tmp;
tmp = s->next;
s->next = h->next;
h->next = s;
}
}
对单链表进行排序
void LinkListInsertSort(linklist_t* h)
{
linklist_t *tmp, *s, *th = h;
// 先让tmp指向h的下一个节点
tmp = h->next;
// 将h->next赋值为NULL
h->next = NULL;
// 如果tmp不为NULL,循环继续
while (tmp) {
//从tmp中取到开头的节点
s = tmp;
tmp = s->next;
//如果h没有到结尾,并且s节点的数据大于h中的数据,就让h往后走
while (h->next != NULL && s->data > h->next->data) {
h = h->next;
}
//将s插入到当前的位置
s->next = h->next;
h->next = s;
//恢复h到开头的位置
h = th;
}
}
单链表的整体代码
linklist.h
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <stdlib.h>
#define datatype int
typedef struct node{
datatype data;
struct node *next;
//这个指针指向的是下一个节点,节点是结构体类型,所以指针也要定义成结构体指针
}linklist_t;
linklist_t *LinkListCreate(void);
int LinkListInsertHead(linklist_t *h,datatype data);
int LinkListInsertTail(linklist_t *h,datatype data);
int LinkListInsertByPos(linklist_t *h,int pos,datatype data);
void LinkListShow(linklist_t *h);
datatype LinkListDeleteHead(linklist_t* h);
datatype LinkListDeleteTail(linklist_t* h);
datatype LinkListDeletePos(linklist_t* h,int pos);
datatype LinkListCheckDataByPos(linklist_t* h,int pos);
int LinkListUpdateDataByPos(linklist_t* h,int pos,datatype data);
void LinkListReverse(linklist_t* h);
void LinkListInsertSort(linklist_t* h);
#endif
linklist.c
#include "linklist.h"
linklist_t* LinkListCreate(void)
{
linklist_t* h;
h = (linklist_t*)malloc(sizeof(*h));
if (h == NULL) {
printf("malloc head memory error\n");
return NULL;
}
// 将数据赋值为0
h->data = (datatype)0;
// 将指针域指向NULL
h->next = NULL;
return h;
}
/*
*功能:单链表头插法插入节点
*参数:
* @h:单链表的头
* @data:被插入的数据
*返回值:成功返回0,失败返回-1
*/
int LinkListInsertHead(linklist_t* h, datatype data)
{
linklist_t* tmp;
// 1.分配节点的内存,将data放到这个节点的数据域中
tmp = (linklist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("alloc node memory error\n");
return -1;
}
tmp->data = data;
// 2.将这个节点通过头插法的方式插入到链表中即可
// 步骤1:让tmp的next指向h的后一个节点
tmp->next = h->next;
// 步骤2:将h的next指针域指向tmp
h->next = tmp;
return 0;
}
// 思想:判断h->next是否等于NULL,如果h->next为NULL说明链表没有要访问的成员了。
// :如果h->next不是NULL,说明链表不为空,可以将它的数据域打印出来
// :printf("data:%d\n",h->next->data);
// :执行完上述的步骤之后将h->next赋值给h h = h->next;
// :在循环上述步骤
void LinkListShow(linklist_t* h)
{
while (h->next) {
printf("-%d", h->next->data);
h = h->next;
}
printf("-\n");
}
int LinkListInsertTail(linklist_t* h, datatype data)
{
linklist_t* tmp;
// 1.分配节点的内存,将data放到这个节点的数据域中
tmp = (linklist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("alloc node memory error\n");
return -1;
}
tmp->data = data;
// 2.让h走到最后一个节点的位置
while (h->next) {
h = h->next;
}
// 3.将这个节点通过头插法的方式插入到链表中即可
// 步骤1:让tmp的next指向h的后一个节点
tmp->next = h->next;
// 步骤2:将h的next指针域指向tmp
h->next = tmp;
return 0;
}
// ①判断pos是否等于0,如果pos为零,直接将节点插入在当前节点的位置,退出
// ②如果pos不为0, 1.pos-- 2.h = h->next。
// ③ 循环执行上述的①和②步骤,如果输入pos太大(不合法)链表到结尾了也要退出循环
int LinkListInsertByPos(linklist_t* h, int pos, datatype data)
{
linklist_t* tmp;
// 1.判断pos是否合法
if (pos < 0) {
printf("插入的位置不合法\n");
return -1;
}
// 2.通过循环找出插入节点的位置
while (h) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 3.找到了插入节点的位置,将节点插入即可。
tmp = (linklist_t*)malloc(sizeof(*tmp));
if (tmp == NULL) {
printf("malloc memory error\n");
return -1;
}
tmp->data = data;
tmp->next = h->next;
h->next = tmp;
return 0;
}
}
printf("插入的位置不合法\n");
return -1;
}
// 如果链表是空这个函数返回1,否则返回0(非空)
int LinkListIsEmpty(linklist_t* h)
{
return h->next == NULL ? 1 : 0;
}
datatype LinkListDeleteHead(linklist_t* h)
{
datatype data;
linklist_t* tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
// 2.让tmp指向h的下一个节点
tmp = h->next;
// 3.让h指向tmp的下一个节点
h->next = h->next->next;
// 4.释放tmp的内存
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
datatype LinkListDeleteTail(linklist_t* h)
{
datatype data;
linklist_t* tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,头删失败\n");
return (datatype)-1;
}
// 2.通过循环让h走到倒数第二个节点的位置
while (h->next->next) {
h = h->next;
}
// 3.让tmp指向h->next
tmp = h->next;
// 4.让h->next指向空
h->next = h->next->next;
// 5.释放tmp
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
datatype LinkListDeletePos(linklist_t* h, int pos)
{
datatype data;
linklist_t* tmp;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置删除失败\n");
return (datatype)-1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("删除的位置不合法\n");
return (datatype)-1;
}
// 3.通过循环找出删除节点的位置
while (h->next) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 4.找到了删除节点的位置,将节点删除即可。
tmp = h->next;
h->next = h->next->next;
data = tmp->data;
if (tmp != NULL) {
free(tmp);
tmp = NULL;
}
return data;
}
}
printf("删除的位置不合法\n");
return (datatype)-1;
}
datatype LinkListCheckDataByPos(linklist_t* h, int pos)
{
datatype data;
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置查询失败\n");
return (datatype)-1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("查询的位置不合法\n");
return (datatype)-1;
}
// 3.通过循环找到查询的位置
while (h->next) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 查询到了位置
data = h->next->data;
return data;
}
}
printf("查询的位置不合法\n");
return (datatype)-1;
}
int LinkListUpdateDataByPos(linklist_t* h, int pos, datatype data)
{
// 1.判空
if (LinkListIsEmpty(h)) {
printf("链表是空,位置更新失败\n");
return -1;
}
// 2.判断pos是否合法
if (pos < 0) {
printf("更新的位置不合法\n");
return -1;
}
// 3.通过循环找到更新的位置
while (h->next) {
if (pos != 0) {
pos--;
h = h->next;
} else {
// 更新数据
h->next->data = data;
return 0;
}
}
printf("更新的位置不合法\n");
return -1;
}
void LinkListReverse(linklist_t* h)
{
linklist_t *tmp, *s;
// 先让tmp指向h的下一个节点
tmp = h->next;
// 将h->next赋值为NULL
h->next = NULL;
// 如果tmp不为NULL,循环继续
while (tmp) {
s = tmp;
tmp = s->next;
s->next = h->next;
h->next = s;
}
}
void LinkListInsertSort(linklist_t* h)
{
linklist_t *tmp, *s, *th = h;
// 先让tmp指向h的下一个节点
tmp = h->next;
// 将h->next赋值为NULL
h->next = NULL;
// 如果tmp不为NULL,循环继续
while (tmp) {
//从tmp中取到开头的节点
s = tmp;
tmp = s->next;
//如果h没有到结尾,并且s节点的数据大于h中的数据,就让h往后走
while (h->next != NULL && s->data > h->next->data) {
h = h->next;
}
//将s插入到当前的位置
s->next = h->next;
h->next = s;
//恢复h到开头的位置
h = th;
}
}
main.c
#include "linklist.h"
int main(int argc, char const* argv[])
{
linklist_t* h;
// 1.创建单链表
h = LinkListCreate();
if (h == NULL) {
printf("create linklist error\n");
return -1;
}
// 2.通过头插法插入数据
LinkListInsertHead(h, 30);
LinkListInsertHead(h, 20);
LinkListInsertHead(h, 10);
// 3.遍历链表中的成员
LinkListShow(h);
// 4.尾部插入成员
// LinkListInsertTail(h,60);
// LinkListInsertTail(h,70);
// LinkListInsertTail(h,80);
// LinkListShow(h);
// 5.位置插入
LinkListInsertByPos(h, 2, 555);
LinkListShow(h);
// 6.头删
// printf("delete head = %d\n",LinkListDeleteHead(h));
// printf("delete head = %d\n",LinkListDeleteHead(h));
// printf("delete head = %d\n",LinkListDeleteHead(h));
// LinkListShow(h);
// // 7.尾删
// printf("delete tail = %d\n", LinkListDeleteTail(h));
// printf("delete tail = %d\n", LinkListDeleteTail(h));
// printf("delete tail = %d\n", LinkListDeleteTail(h));
// LinkListShow(h);
// 8.位置删除
// printf("delete pos = %d\n", LinkListDeletePos(h, 4));
// LinkListShow(h);
printf("Check pos = %d\n", LinkListCheckDataByPos(h, 2));
LinkListUpdateDataByPos(h, 4, 999);
LinkListShow(h);
LinkListReverse(h);
LinkListShow(h);
LinkListInsertSort(h);
LinkListShow(h);
return 0;
}