单链表是数据结构中最基础的一类结构,本文将讲述单链表的结构定义和实现过程,以及其基本操作。
数据结构=结构定义+结构操作
首先来讲结构定义:
单链表由两个结构组成:1.结点;
2.指向第一个结点的指针,称作头指针(这个指针可以只是一个指针变量,也可以由一个结点构成。头指针为单独的一个变量时,该链表叫做无头链表,否则叫做有头链表);
可以理解成单链表的外部结构。
每个结点又由两个部分组成:1.数据域data;
2.指向下个结点的地址域next;
可以理解成结点的结构。
我们根据结构定义来进行编码:
typedef struct Node {
int data; //数据域data
struct Node *next;//指针域next
} Node; //结点的结构定义
单链表中的头指针我们不进行额外的定义,一般在主函数中去定义。
接下来是单链表的结构操作:
Node getNewNode(int n) //生成一个新的结点(可以理解为将一个数据存放在一个结点中的 data域中)
void clear(Node* head) //删除单链表
int insert(Node* head,int val,int pos)//插入操作
int find(Node* head,int pos) //查询操作
int output_LinkList(Node* head) //输出整个链表
1.生成一个新的结点:
Node *getNewNode(int val) {
Node *p = (Node *)malloc(sizeof(Node));//先为结点分配空间
p->data = val; //给数据域赋值
p->next = NULL; //给指针域赋值为空
return p; //返回结点的地址
}
参数val为 结点中存放的数据,首先给结点开辟空间,然后给结点中的两部分分别赋值即可。
最后返回结点的地址。
2.删除单链表:
void clear(Node *head) {
if (head == NULL) return ; //头指针为空,证明链表不存在
for (Node *p = head, *q; p; p = q) {
q = p->next;
free(p);
}//从链表头遍历至末尾,依次释放链表中每个结点的值
return ;
}
首先判断链表是否为空,如果为空,则不需要释放,直接结束该操作函数。
如果不为空,则定义指针p和q,其中p永远指向被释放的结点,q永远指向被释放结点的下一个结点。
3.插入操作:
Node *insert(Node *head, int pos, int val) {
Node new_head, *p = &new_head, *node = getNewNode(val);
new_head.next = head;
for (int i = 0; i < pos; i++) p = p->next;//让指针p指到待插入元素的前一个元素的位置
node->next = p->next;//先让新结点指向它的下一个结点
p->next = node;//再让上一个结点指向新结点本身
return new_head.next;//返回单链表的首地址
}
想在单链表中插入一个结点,首先我们要先让一个结点指针p,指向待插入元素的前一个结点。
之后让新结点的指针域指向后一个结点,最后让前一个结点指向新结点即可。
那么这个时候你可能会有问题:如果我先让前一个结点指向新结点行不行呢?
答案是不行,因为当你执行了这个操作之后,你会发现你找不到待插入结点的下一个结点的地址,因为下一个结点的地址一开始是记录在未插入前上一个结点的指针域中的,但是你已经让上一个结点指向新的结点了,这意味着你已经丢失了下一个结点的地址。
这个问题实际上称作“内存泄漏”,在服务器的长期运作的程序中,这种情况是非常严重的。
回到上面这个代码,你还可能发现程序中定义了一个new_head变量,这个变量是否是多余的呢?为什么不能直接返回head呢?
这就要回到刚刚的结构定义中了,我们知道单链表的头部是一个指针head,其中存放着链表首元素的地址,那如果我们想要在首元素之前(pos==0)插入元素呢?是不是会发生首元素地址的改变?这个时候我们需要一个新的head指针来表示该单链表首元素的地址了。
4.输出整个链表:
int out_putLinkList(Node* head){
if(head)return 0;//链表为空,不需要输出,返回0
Node* p=head;
while(p){
printf("%d",p->data);
p=p->next;
}
return 1;//输出成功则返回1.
}
5.查询操作:
int find(Node *head, int val) {
Node *p = head;
int n = 0;//设置n的值来记录p所遍历的位置
while (p) {
if (p->data == val) {
printf("%d",n);//输出被查询元素在单链表中的位置
}
n += 1;
p = p->next;
}
return 0;
}