链表:一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次 序实现的。链表由一系列结点(链表中每一个元素称为 结点)组成,结点可以在运行时动态生成。每个结点包 括两个部分:一个是存储数据元素的数据域,另一个是 存储下一个结点地址的指针域。?
涉及链表的操作有很多,今天简单讲解一下单向链表的创建 增加 删除 打印。
首先声明一个结构体要有数据域和指针域,因为链表是非连续、非顺序的存储结构,需要用指针来找到下一个结点的位置。声明常用有两种方法:
/*第一种*/
struct Node
{
int date; //数据域
struct Node * next; //指针域
};
/*第二种*/
typedef struct Node
{
int date; //数据域
struct Node * next; //指针域
}Node;
//创建链表
struct Node * createList()
{
struct Node * headNode = (struct Node *)malloc(sizeof(struct Node));
//headNode变成一个变量
//变量使用前初始化
headNode->next = NULL;
tail=headNode; //后面尾插会用到
return headNode;
};
//创建结点
struct Node * createNode(int date)
{
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->date = date;
newNode->next = NULL;
return newNode;
}
- 增加结点(头插法和尾插法)(ps:不要将头插和尾插混合使用)
- 头插法
思路:
插入的结点永远在头结点的后面,同时要保证让新结点先指向原来的头结点后一位,再让头结点记录新结点的位置,因为如果是先让头结点记录新结点的位置,那么,原来结点的位置信息就丢失了,这个链表就会中断,连不起来,结合图理解。
?代码:
//插入结点(头插法)
void insertNodebyHead(struct Node * headNode, int date)
{
//创建新结点
struct Node * newNode = createNode(date);
/*位置不能交换*/
newNode->next = headNode->next;
headNode->next= newNode;
}
? ? ? ? 2.尾插法
思路:
将新的结点加在链表的尾端,我们知道结点的位置只能通过上一个结点来找到它,所以我们就要用一个tail指针来确定最后一个结点的位置。所以我们需要一开始声明一个tail指针。
代码:
//插入结点(尾插法)
void insertNodebyTail(struct Node * headNode, int date)
{
//创建新结点
struct Node * newNode = createNode(date);
tail->next = newNode;
tail = newNode;
}
思路:
删除的话涉及查找,我们肯定是先找到,在删除,所以我们需要两个指针来遍历链表,一个在前,一个在后,后一位的指针负责查找,前一位的指针负责将删除后的链表仍然串起来。
代码:
//删除结点
void deleteNode(struct Node * headNode, int posDate)
{
struct Node * posNode = headNode->next;
struct Node * posNodefront = headNode;
if (posNode == NULL)
{
printf("无法删除链表\n");
}
else
{
/*查找*/
while (posNode->date != posDate)
{
posNodefront = posNode;
posNode = posNodefront->next;
if (posNode == NULL)
{
printf("未找到指定结点,无法删除\n");
return;
}
}
posNodefront->next = posNode->next;
free(posNode); //删除
}
}
思路:
另外声明一个指针从头结点下一位开始,检查每一个结点的情况,如果不为NULL,打印出数据,如果为NULL,则退出。
代码:
//打印链表
void printList(struct Node * headNode)
{
struct Node * pMove = headNode->next;
while (pMove != NULL)
{
printf("%d ", pMove->date);
pMove = pMove->next;
}
printf("\n");
}
完整的代码:
/*动态创建一个链表 */
//创建链表
//创建结点
//插入结点(头插法和尾插法)
//删除结点
//打印遍历链表
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Node * tail = NULL;
struct Node
{
int date;
struct Node * next;
};
//创建链表
struct Node * createList()
{
struct Node * headNode = (struct Node *)malloc(sizeof(struct Node));
//headNode变成一个变量
//变量使用前初始化
headNode->next = NULL;
tail=headNode;
return headNode;
};
//创建结点
struct Node * createNode(int date)
{
struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->date = date;
newNode->next = NULL;
return newNode;
}
//插入结点(头插法)
void insertNodebyHead(struct Node * headNode, int date)
{
//创建新结点
struct Node * newNode = createNode(date);
newNode->next = headNode->next;
headNode->next= newNode;
}
//插入结点(尾插法)
void insertNodebyTail(struct Node * headNode, int date)
{
//创建新结点
struct Node * newNode = createNode(date);
tail->next = newNode;
tail = newNode;
}
//删除结点
void deleteNode(struct Node * headNode, int posDate)
{
struct Node * posNode = headNode->next;
struct Node * posNodefront = headNode;
if (posNode == NULL)
{
printf("无法删除链表\n");
}
else
{
while (posNode->date != posDate)
{
posNodefront = posNode;
posNode = posNodefront->next;
if (posNode == NULL)
{
printf("未找到指定结点,无法删除\n");
return;
}
}
posNodefront->next = posNode->next;
free(posNode);
}
}
//打印链表
void printList(struct Node * headNode)
{
struct Node * pMove = headNode->next;
while (pMove != NULL)
{
printf("%d ", pMove->date);
pMove = pMove->next;
}
printf("\n");
}
int main(void)
{
/*自己发挥*/
return 0;
}
我是将链表的各个操作模块化了,这样看起来简洁,如果需要修改数据的话,只用改一下代码中的date就行了,同时单链表的一些其他操作也可以对上面的代码稍加修改即可。
|