目录
链表的概念
链表的两种结构
物理结构和逻辑结构
物理 存储单元 上非连续、非顺序的 存储结构,
逻辑上是连续的,他们之间用指针连续起来
链表中每一个元素称为节点,由一系列的节点组成。
每个节点包括两部分,存储数据元素的数据域(data)和存储下一个节点地址的指针域(next)。
如图所示:下图有四个节点,头节点一般不存数据,head为头节点,next为指向下一个节点的
指针(存储下一个节点的地址),单向不循环链表最后一个节点的指针域,指向空(NULL)。
1、设计节点类型
/**************设计节点类型**************/
typedef struct ListNode
{
int data; //数据域
struct ListNode *next; //地址域
} node_t;
2、创建链表
申请头节点空间
/********创建链表********/
node_t *CreateList()
{
node_t *head = (node_t *)malloc(sizeof(node_t)); //申请头节点空间
if (head == NULL)
{
perror("head is null\n"); //如果空间申请失败报错
return NULL;
}
head->data = 0; //头节点数据域不放数据,初始化
head->next = NULL;
return head;
}
3、头插法
/************头插法************/
int InsertHListNode(node_t *head, int newdata)
{
// 1、判断
if (head == NULL)
{
perror("head is null\n");
return -1;
}
// 2、申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("newnode is null\n");
return -1;
}
// 3、填写数据
newnode->data = newdata;
// 4、修改指针
newnode->next = head->next;
head->next = newnode; //头节点指向新节点
return 0;
}
头插法就是在头节点后插入新节点(头节点不放数据所以从头节点后一个位置插入),核心就是 将新节点两条线连接起来
4、尾插法
/************尾插法************/
int InserttListNode(node_t *head, int newdata)
{
// 1、判断
if (head == NULL)
{
perror("head is null\n");
return -1;
}
// 2、申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("malloc error\n");
return -1;
}
// 3、填写数据
newnode->data = newdata;
// 4、找尾
node_t *temp = head; //因为头节点不能动所以申请一个中间变量
while (temp->next != NULL) //当temp->next = NULL时跳出循环
{
temp = temp->next;
}
// 5、修改指针
newnode->next = temp->next; //新节点指向最后
temp->next = newnode; //新节点接到最后一个节点后
return 0;
}
尾插法 核心就是找尾,找到尾后修改指针的指向,代码中的指针指向跟图中的指针指向是同一个意思,只是写法不同。
5、打印链表节点数据
遍历, 循环打印,将链表中的所有的节点数据打印到终端
/*************打印链表***************/
void PrintList(node_t *head)
{
if (head == NULL)
{
perror("There is no data in the linked list, please input it first\n");
return;
}
node_t *temp = head;
while (temp->next != NULL)
{
temp = temp->next; //因为头节点不放数据所以从第二个节点开始
printf("%d\t", temp->data);
}
printf("\n");
}
6、头删法
/***************头删法*******************/
int DeleteHList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
//设置一个临时变量
node_t *temp = head->next;
head->next = temp->next;
free(temp); //删除成功释放空间
temp = NULL;
return 0;
}
头删法:相对来说是比较简单的,只需要改变一个指针指向。
7、尾删法
/***************尾删法*******************/
int DeleteTList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
//设置一个临时变量
node_t *prev = NULL; //指向尾的前一个节点
node_t *temp = head;
//找尾
while (temp->next->next != NULL)
{
temp = temp->next;
}
prev = temp;
temp = temp->next;
prev->next = NULL;
free(temp);
temp = NULL;
return 0;
}
尾删法:找两个节点先找倒是第二个,在通过倒数第二个找到最后一个,找最后一个是为了释放节点空间。
8、链表节点数据个数
遍历整个链表,然后返回节点数据个数(因为头节点不放数据所以不包括头节点)
/****************链表的节点元素个数**********************/
int ListLength(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
node_t *temp = head;
int i = 0;
while (temp->next != NULL)
{
temp = temp->next;
i++;
}
return i;
}
9、指定位置插入
/***************指定位置插入***************/
int InsertLocaList(node_t *head, int newdata, int loca)
{
//判错
int len = ListLength(head);
if (head == NULL || loca <= 0 || loca > len + 1) //插入的位置超范围
{
perror("head is null or out of scope\n");
return -1;
}
//申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("malloc error\n");
return -1;
}
//填写数据,建立联系
newnode->data = newdata;
node_t *temp = head;
int i = 1;
while (loca != i)
{
temp = temp->next;
i++;
}
newnode->next = temp->next;
temp->next = newnode;
return 0;
}
在写之前一定要判断该链表和该位置是否为空,如果都不存在链表和该位置,要插入的节点就接不上了。
10、指定位置删除
/***************指定位置删除***************/
int DeleteLocaList(node_t *head, int loca)
{
//判错
int len = ListLength(head);
if (head == NULL || loca <= 0 || loca > len) //删除的位置超范围
{
perror("head is null or out of scope\n");
return -1;
}
node_t *prev = NULL; //指向尾的前一个节点
node_t *temp = head;
int i = 1;
//找要删除节点的前一个位置
while (loca != i)
{
temp = temp->next;
i++;
}
prev = temp;
temp = temp->next;
prev->next = temp->next;
return 0;
}
11、链表冒泡排序
/************链表冒泡排序*************/
node_t *SortList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return NULL;
}
int len = ListLength(head);
if (len < 2)
{
return head;
}
node_t *r, *p, *q; //表示前中后三个节点位置
r = head;
for (int i = 0; i < len - 1; i++)
{
r = head;
for (int j = 0; j < len - i - 1; j++)
{
p = r->next;
q = p->next;
if (q->data < p->data)
{
p->next = q->next;
q->next = p;
r->next = q;
}
r = r->next;
}
}
return head;
}
12、菜单
void menu(node_t *head)
{
char ch;
while (1)
{
printf("***************************************\n");
printf("***** 1.头插 *****\n");
printf("***** 2.尾插 *****\n");
printf("***** 3.头删 *****\n");
printf("***** 4.尾删 *****\n");
printf("***** 5.在指定位置插入 *****\n");
printf("***** 6.在指定位置删除 *****\n");
printf("***** 7.打印单链表 *****\n");
printf("***** 8.查看链表中有多少元素 *****\n");
printf("***** 9.排序 *****\n");
printf("***** 0.退出 *****\n");
printf("***************************************\n");
int option = 0, loca = 0, newdata = 0;
printf("请输入选项:> ");
scanf("%d", &option);
switch (option)
{
case 9:
head = SortList(head);
break;
case 8:
printf("链表中有%d个元素\n", ListLength(head));
break;
case 7:
PrintList(head);
break;
case 6:
printf("请输入你要删除的位置:");
scanf("%d", &loca);
DeleteLocaList(head, loca);
break;
case 5:
printf("请输入你要插入的位置:");
scanf("%d", &loca);
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InsertLocaList(head, newdata, loca);
break;
case 4:
DeleteTList(head);
break;
case 3:
DeleteHList(head);
break;
case 2:
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InserttListNode(head, newdata);
break;
case 1:
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InsertHListNode(head, newdata);
break;
case 0:
return;
default:
printf("输入错误\n");
break;
}
printf("是否继续(y/n)\n");
getchar(); //空吞
ch = getchar();
if (ch == 'n' || ch == 'N')
{
return;
}
}
}
整体代码显示
#include <stdio.h>
#include <stdlib.h>
/**************设计节点类型**************/
typedef struct ListNode
{
int data; //数据域
struct ListNode *next; //地址域
} node_t;
node_t *CreateList(); //创建链表
int InsertHListNode(node_t *head, int newdata); //头插法
int InserttListNode(node_t *head, int newdata); //尾插法
int InsertLocaList(node_t *head, int newdata, int loca); //在指定位置插入
int DeleteHList(node_t *head); //头删法
int DeleteTList(node_t *head); //尾删法
int DeleteLocaList(node_t *head, int loca); //在指定位置删除
int ListLength(node_t *head); //链表的节点元素个数
void PrintList(node_t *head); //打印链表
void menu(node_t *head); //菜单
node_t *SortList(node_t *head); //冒泡排序
int main()
{
node_t *head = CreateList();
if (head == NULL)
{
perror("head is null\n");
return -1;
}
menu(head);
return 0;
}
/*****************菜单***********/
void menu(node_t *head)
{
char ch;
while (1)
{
printf("***************************************\n");
printf("***** 1.头插 *****\n");
printf("***** 2.尾插 *****\n");
printf("***** 3.头删 *****\n");
printf("***** 4.尾删 *****\n");
printf("***** 5.在指定位置插入 *****\n");
printf("***** 6.在指定位置删除 *****\n");
printf("***** 7.打印单链表 *****\n");
printf("***** 8.查看链表中有多少元素 *****\n");
printf("***** 9.排序 *****\n");
printf("***** 0.退出 *****\n");
printf("***************************************\n");
int option = 0, loca = 0, newdata = 0;
printf("请输入选项:> ");
scanf("%d", &option);
switch (option)
{
case 9:
head = SortList(head);
break;
case 8:
printf("链表中有%d个元素\n", ListLength(head));
break;
case 7:
PrintList(head);
break;
case 6:
printf("请输入你要删除的位置:");
scanf("%d", &loca);
DeleteLocaList(head, loca);
break;
case 5:
printf("请输入你要插入的位置:");
scanf("%d", &loca);
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InsertLocaList(head, newdata, loca);
break;
case 4:
DeleteTList(head);
break;
case 3:
DeleteHList(head);
break;
case 2:
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InserttListNode(head, newdata);
break;
case 1:
printf("请输入你要插入的元素:");
scanf("%d", &newdata);
InsertHListNode(head, newdata);
break;
case 0:
return;
default:
printf("输入错误\n");
break;
}
printf("是否继续(y/n)\n");
getchar(); //空吞
ch = getchar();
if (ch == 'n' || ch == 'N')
{
return;
}
}
}
/********创建链表********/
node_t *CreateList()
{
node_t *head = (node_t *)malloc(sizeof(node_t)); //申请头节点空间
if (head == NULL)
{
perror("head is null\n"); //如果空间申请失败报错
return NULL;
}
head->data = 0; //头节点数据域不放数据,初始化
head->next = NULL;
return head;
}
/************头插法************/
int InsertHListNode(node_t *head, int newdata)
{
// 1、判断
if (head == NULL)
{
perror("head is null\n");
return -1;
}
// 2、申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("newnode is null\n");
return -1;
}
// 3、填写数据
newnode->data = newdata;
// 4、修改指针
newnode->next = head->next;
head->next = newnode; //头节点指向新节点
return 0;
}
/************尾插法************/
int InserttListNode(node_t *head, int newdata)
{
// 1、判断
if (head == NULL)
{
perror("head is null\n");
return -1;
}
// 2、申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("malloc error\n");
return -1;
}
// 3、填写数据
newnode->data = newdata;
// 4、找尾
node_t *temp = head; //因为头节点不能动所以申请一个中间变量
while (temp->next != NULL) //当temp->next = NULL时跳出循环
{
temp = temp->next;
}
// 5、修改指针
newnode->next = temp->next; //新节点指向最后
temp->next = newnode; //新节点接到最后一个节点后
return 0;
}
/*************打印链表***************/
void PrintList(node_t *head)
{
if (head == NULL)
{
perror("There is no data in the linked list, please input it first\n");
return;
}
node_t *temp = head;
while (temp->next != NULL)
{
temp = temp->next; //因为头节点不放数据所以从第二个节点开始
printf("%d\t", temp->data);
}
printf("\n");
}
/***************头删法*******************/
int DeleteHList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
//设置一个临时变量
node_t *temp = head->next;
head->next = temp->next;
free(temp); //删除成功释放空间
temp = NULL;
return 0;
}
/***************尾删法*******************/
int DeleteTList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
//设置一个临时变量
node_t *prev = NULL; //指向尾的前一个节点
node_t *temp = head;
//找尾
while (temp->next->next != NULL)
{
temp = temp->next;
}
prev = temp;
temp = temp->next;
prev->next = NULL;
free(temp);
temp = NULL;
return 0;
}
/****************链表的节点元素个数**********************/
int ListLength(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return -1;
}
node_t *temp = head;
int i = 0;
while (temp->next != NULL)
{
temp = temp->next;
i++;
}
return i;
}
/***************指定位置插入***************/
int InsertLocaList(node_t *head, int newdata, int loca)
{
//判错
int len = ListLength(head);
if (head == NULL || loca <= 0 || loca > len + 1) //插入的位置超范围
{
perror("head is null or out of scope\n");
return -1;
}
//申请新节点空间
node_t *newnode = (node_t *)malloc(sizeof(node_t));
if (newnode == NULL)
{
perror("malloc error\n");
return -1;
}
//填写数据,建立联系
newnode->data = newdata;
node_t *temp = head;
int i = 1;
while (loca != i)
{
temp = temp->next;
i++;
}
newnode->next = temp->next;
temp->next = newnode;
return 0;
}
/***************指定位置删除***************/
int DeleteLocaList(node_t *head, int loca)
{
//判错
int len = ListLength(head);
if (head == NULL || loca <= 0 || loca > len) //删除的位置超范围
{
perror("head is null or out of scope\n");
return -1;
}
node_t *prev = NULL; //指向尾的前一个节点
node_t *temp = head;
int i = 1;
//找要删除节点的前一个位置
while (loca != i)
{
temp = temp->next;
i++;
}
prev = temp;
temp = temp->next;
prev->next = temp->next;
return 0;
}
/************链表冒泡排序*************/
node_t *SortList(node_t *head)
{
if (head == NULL)
{
perror("head is null\n");
return NULL;
}
int len = ListLength(head);
if (len < 2)
{
return head;
}
node_t *r, *p, *q; //表示前中后三个节点位置
r = head;
for (int i = 0; i < len - 1; i++)
{
r = head;
for (int j = 0; j < len - i - 1; j++)
{
p = r->next;
q = p->next;
if (q->data < p->data)
{
p->next = q->next;
q->next = p;
r->next = q;
}
r = r->next;
}
}
return head;
}
测试