提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
最近学了一下关于单链表的知识以及做了一些链表有关的习题,接下来做一下单链表的总结,所用语言为C
一、什么是单链表
如上图,这就是一个简单的单链表示意图。(ppt制作有点烂哈)
首先,在回答什么是单链表前,先简单谈谈什么是链表。链表就是一种数据结构,而之前学到的数组也是一种数据,链表和数组算是两种最简单数据结构了。
而单链表,顾名思义,就是单向的链表,如上面的示意图,每个方块就是链表的一个节点,每个节点一般至少包含两个变量,val和next,val指的是数值(上图中的1、2、3),next是结构体指针变量,它指向的是后一个节点。单链表中最特殊的就是头节点和尾节点。头节点指的是最前面的节点,为什么说头节点特殊,因为单链表的指向性是单向的,也就说如果想要遍历全部节点,我们需要从头节点开始遍历。而尾节点则是指向NULL空节点的节点,上图的尾节点是 3 节点,注意,并不是NULL节点!
此处所用的IDE为codebolcks
代码如下(示例)
//头文件
#include //标准输入输出头文件
#include // 包含了C常用的系统函数
// 宏定义相关变量
#define TYPE struct ListNode // 宏定义结构体变量名
//定义链表,创建结构体
struct ListNode
{
int val;
// 声明结构体指针变量,指向结构体——链表
struct ListNode* next;
};
int main(void)
{
TYPE *head, *t; //声明头节点
struct ListNode a, b, c; //声明三个链表节点
/* 1. A.B 则A为对象或结构体
2. A->B 则A为指针,->表示成员提取,即结构体内的元素变量,比如这里用的val,next
*/
a.val = 1; //对应成员赋值
a.next = &b; //标注节点指向
b.val = 2;
b.next = &c;
c.val = 3;
c.next = NULL;
head = &a; //头节点位置
t = head;
while(t != NULL)
{
printf("%d\n",t->val);
t = t->next;
}
return 0;
}
需要引入动态分配内存头文件
#include //动态分配内存函数头文件
链表声明定义同上
创建链表函数
TYPE* creat(void)
{
TYPE* head;
//声明两个节点
TYPE* a, *b;
//声明节点数变量并初始化为0
int number = 0;
//动态分配节点内存空间,开辟节点
a = b = (TYPE*)malloc(sizeof(struct ListNode));
printf("input: val = \n");
scanf("%d", &a->val);
head = NULL;
//当输入77时,循环终止
while(a->val != 77)
{
++ number;
//当节点数为1时,头指针指向第一个节点信息
if(number == 1)
{
head = a;
}
else
{
//节点数大于1时,用next保存当前节点信息
b->next = a;
}
//保存当前节点信息
b = a;
a = (TYPE*)malloc(sizeof(struct ListNode));
scanf("%d",&a->val);
}
//循环终止后,将尾节点赋值为NULL
b->next = NULL;
//返回head头节点
return (head);
}
循环输出主函数
int main(void)
{
TYPE *kk;
//将kk赋值为head
kk = creat();
//默认链表不是空链表
if(kk != NULL)
{
//循环打印链表的val值,直到空节点
do{
printf("\n val=%d\n", kk->val);
kk = kk->next;
}while(kk != NULL);
}
return 0;
}
输出结果
creat()函数的定义参考了https://blog.csdn.net/xiaoxiaodawei/article/details/104807198
这位dalao对于单链表的解释远胜于我,有需要的不妨去看看。
我认为编程的学习离不开刷题,以下题目都是来自于leetcode网站
编写一个函数,使其可以删除某个链表中的给定的(非末尾)节点,传入函数的唯一参数为 要被删除的节点。
比如一个链表=[4,5,1,9],如下图
输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
链接:https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
题目的模板如下
**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
}
这道题并没有给出链表的head节点,而是给出了具体要删除的节点,这时候怎么做呢?
很简单,只需这个节点的后一个节点等于它,在把它的指向变成下下个节点即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
void deleteNode(struct ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
题目链接 https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
题目模板
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int getlength(struct ListNode* head){
int len = 0;
while(head){
len++;
head = head->next;
}return len;
}
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* newLN = malloc(sizeof(struct ListNode));
newLN->val = 0; newLN->next = head;
int len = getlength(head);
struct ListNode* cur = newLN;
for(int i = 1; i < len - n + 1; ++i){
cur = cur->next;
}
cur->next = cur->next->next;
struct ListNode* ans = newLN->next;
free(newLN);
return ans;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct Stack{
struct ListNode* val;
struct Stack* next;
};
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* dummy = malloc(sizeof(struct ListNode));
dummy->val = 0, dummy->next = head;
struct Stack* stk = NULL;
struct ListNode* cur = dummy;
while(cur){
struct Stack* tmp = malloc(sizeof(struct Stack));
tmp->val = cur, tmp->next = stk;
stk = tmp;
cur = cur->next;
}
for(int i=0; inext;
free(stk);
stk = tmp;
}
struct ListNode* prev = stk->val;
prev->next = prev->next->next;
struct ListNode* ans = dummy->next;
free(dummy);
return ans;
}
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
题目链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
// 声明前节点, 并初始为 NULL 空值
struct ListNode* prev = NULL;
// 声明节点,初始为所给头节点
struct ListNode* curr = head;
// 循环,当节点不为空值循环
while(curr)
{
// 声明节点,初始化为 curr 的下一节点
struct ListNode* next = curr -> next;
// 将前节点 赋值给 curr(当前节点)的下一节点
curr -> next = prev;
// 前节点赋值为当前节点
prev = curr;
// curr(当前节点)赋值为原来的下一节点
curr = next;
}
// 返回前节点,此时为原来的链表的最后一个非空节点
return prev;
}
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL || head -> next == NULL)
{
return head;
}
// 声明新的头节点,并递归初始化,最后新的头节点变成原来的最后一个节点
struct ListNode* newHead = reverseList(head -> next);
// 把节点k+1+1 赋值为 k
head -> next -> next = head;
// 把k+1 赋值为空值
head -> next = NULL;
//返回新头节点
return newHead;
}
单链表,其实可以理解为一个数据域,每个节点都有相应的数据信息,且只能指向后一个节点,一个有限的单链表的尾节点会指向NULL