链表
什么是链表?
那么接下来呢,小编就用自己“清新超俗”的话来谈谈小编对链表的理解。
链表就是逻辑上连续,物理存储上不连续的一种数据结构,其以结点为单位存储数据。
为了简单快速的了解什么是链表,可以将链表与数组进行对比,数组是内存上的一段连续的存储空间,而链表,只要将它想成是数组的每个元素都被打散即可(当然这只是简单地指出了数组与链表的最大的区别而已)。
其中,结构体中包含两个元素:其一是存放的数据,其二是一个结构体指针变量,存放的是下一个节点的地址。
了解了链表的结构以后,接下来就是对链表进行操作了,常见的操作有增、删、改、查。接下来我们就用代码实现这些操作。
链表的基本操作(接口):
1.初始化:让其中结点的数目为空,即让头节点指向空。
头节点都指向空了,什么都没有!!!
2.插入:
(1)头插
无论链表是不是空,操作均是四步走:
①先申请新空间(要插入当然得有空间了,不然我住哪?对吧。)
②赋值:数据域进行赋值
③连接链表
有两种情况:
a).链表为空时。
未插入前head指向NULL。
b).链表不为空时。
(2)尾插:道理一样
①分配内存
②遍历找到尾结点
③连接链表
同样分链表为空和不为空
1)链表为空时
2)链表不为空时
以下的以此类推:
3.删除
(1)头删
①先记录下头结点的位置
②再将头结点指向它的下一个结点(此时头节点就是原来链表的第二个节点)
③再释放原来头节点的内存
(2)尾删
①先遍历找到尾结点的前一个结点
②再利用一个变量将尾结点记录下来
③再释放尾结点的空间
④再将尾节点的前一个结点的next指向NULL(即更新了尾结点)
4.查找
遍历整个链表即可
5.销毁
遍历整个链表
两个遍历指针,第一个指向头节点,第二个指向头节点的下一个节点,将前一个结点的内存再释放即可。
最后,要让原来的头节点指向空。
话不多说,上代码!!!
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//链表:
typedef struct Node
{
int data;
struct Node *next;
}Node;
//1.链表的初始化:没有结点。
void NodeInit(Node **head)
{
*head = NULL;
}
//插入操作:头插与尾插
//(1).头插
void NodeInsertFront(Node **head,int data)
{
//首先分配内存
//进行数据赋值
//链表的连接:1.将该节点的next指向原来的头节点
// 2.再将原来的头节点指向该节点
Node *node = (Node*)malloc(sizeof(Node)); //首先进行内存的分配
assert(node != NULL); //判断内存是否分配成功
node->data = data;
node->next = *head;
*head = node;
//printf("%p\n", *head);
}
//(2).尾插
void NodeInsertTail(Node **head, int data)
{
//首先分配内存
//在进行数据的赋值
//将链表连接起来:1.将原来的头节点指向该节点
// 2.再将该节点的next指向NULL
Node *node = (Node*)malloc(sizeof(Node));
assert(node != NULL);
//如果链表为空
if (*head == NULL)
{
*head = node;
node->data = data;
node->next = NULL;
return;
}
//链表不为空:
//先遍历,找到尾结点,再进行链表的连接
else {
Node *cur = *head;
while (cur->next != NULL)
{
cur = cur->next;
}
node->data = data;
cur->next = node;
node->next = NULL;
return;
}
}
//(3).在值为pos的结点后插入
void NodeInsertPos(Node** head, int pos, int data)
{
//先判空
Node *node = *head;
//if (node == NULL)
//{
// //进行尾插
// NodeInsertTail(*head, data);
// return;
//}
//链表非空,先遍历,在进行插入
while (node != NULL)
{
if (node->data == pos)
{
Node *tem = (Node *)malloc(sizeof(Node));
assert(tem != NULL);
tem->data = data;
tem->next = node->next;
node->next = tem;
return;
}
else node = node->next;
}
}
//2.删除操作:头删、尾删
//(1).头删:
void NodeDeleteFront(Node **head)
{
//先将head指向下一个
//再将第一个结点的内存进行释放
//判空
if (*head == NULL)
{
return;
}
Node* node = *head;
*head = (*head)->next;
free(node);
return;
}
//(2).尾删
void NodeDeleteTail(Node **head)
{
//先进行遍历找到倒数第二个结点
//再将倒数第二个结点的next指向空即可
//先进行判空
Node *node = *head;
if (*head == NULL)
{
printf("链表为空,无法删除!!!\n");
return;
}
//遍历找到倒数第二个节点
while (node->next->next != NULL)
{
node = node->next;
}
Node *temp = node->next;
free(temp);
node->next = NULL;
}
//(3).删除值为data的结点
void NodeDeleteData(Node** head, int data)
{
//先进行判空
if (*head == NULL)
{
printf("链表为空,无法删除!!!\n");
return;
}
//再遍历查找值为data的结点的前驱结点
Node *node = *head;
while (node != NULL)
{
//找到了,进行删除:
//创建临时变量记录该节点
//连接链表
//再将该节点的内存释放
//目标结点是第一个结点
if (node->data == data)
{
//进行头删操作
Node* tem = *head;
*head = (*head)->next;
free(tem);
return;
//NodeDeleteFront(*head);
}
if (node->next->data == data)
{
Node *temp = node->next;
node->next = node->next->next;
free(temp);
return;
}
else node = node->next;
}
}
//3.查找操作:
//查找值为data的结点
Node * NodeFind(Node **head, int data)
{
//先进行判空
if (*head == NULL)
{
return NULL;
}
//链表非空
Node *node = *head;
while (node != NULL)
{
if (node->data == data)
{
printf("找到该数!!!\n");
return node;
}
else node = node->next;
}
if (node == NULL)
{
printf("查无此数!!!\n");
return NULL;
}
}
//4.链表的销毁
void NodeDestroy(Node** head)
{
//先进行判空
Node *node = *head;
*head = NULL;
head = NULL;
if (node == NULL)
{
printf("链表为空!!!\n");
return;
}
while (node != NULL)
{
//先将结点指到下一个位置,再释放当前位置的内存
Node *temp = node;
if (node->next != NULL) {
node = node->next;
}
else {
node = NULL;
}
free(temp);
}
printf("链表已销毁!!!\n");
return;
}
//5.打印链表
void NodePrint(Node *head)
{
Node *node = head;
if (node == NULL)
{
printf("链表为空!!!\n");
}
while (node != NULL)
{
printf("%-3d ", node->data);
node = node->next;
}
}
int main()
{
Node *head ;
NodeInit(&head);
NodeInsertFront(&head, 10);
NodeInsertFront(&head, 20);
NodeInsertFront(&head, 30);
NodeInsertFront(&head, 40);
NodeInsertTail(&head, 15);
//NodeDeleteFront(&head);
//NodeDeleteTail(&head);
//NodeDeleteData(&head, 40);
//NodeInsertPos(&head, 20, 25);
Node *pos = NodeFind(&head,20);
//NodeDestroy(&head);
NodePrint(head);
system("pause");
return 0;
}
注意:无论进行什么操作,对链表判空是一件很重要的事!