文章目录
1.什么是链表?
我的理解是:链表就是由一系列的节点组成,而每个节点又包括了两个部分;一部分是数据域,用来储存数据,另一部分指针域,用来指向下一节点。最后把这些节点串起来就形成了链表。
1.1节点构造
struct list_node
{
int data ; // 定义数据域,用于存储数据
struct list_node *next ; //定义指针域,可以用来访问节点数据,也可以遍历,指向下一个节点
};
1.2链表的构造
首先了解链表的组成部分:
说明:
头节点:在单链表的第一个结点之前附设一个结点,它没有直接前驱,称之为头结点,头结点的数据域可以不存储任何信息,指针域指向第一个节点(首节点)的地址。头结点的作用是使所有链表(包括空表)的头指针非空。
2.创建链表
2.1 单向链表
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list_node //定义结构体
{
int data ; //数据域
struct list_node *next ; //指针域
}
typedef struct list_node list_single ;
int main(void)
{
list_single *node = NULL ; //定义一个头指针
node = (list_single *)malloc(sizeof(list_single)); //分配内存空间
if(node == NULL)
{
printf("分配空间失败\n");
}
memset(node,0,sizeof(list_single)); //清空
node->data = 123 ; //给链表节点的数据域赋值
node->next = NULL ; //将链表的指针域指向空
printf("node_data=%d\n",node->data);
printf("node_next=%d\n",node->next);
free(node);
return 0 ;
}
运行结果:
该链表就是:
上述程序中出现了malloc函数,该函数分配的内存大小至少为参数所指定的字节数 ,而且malloc和free是配对的,如果申请后不释放就是内存泄露,如果无故释放那就是什么也没做,释放只能释放一次,如果一块空间释放两次或者两次以上会出现错误(但是释放空指针例外,释放空指针也等于什么也没做,所以释放多少次都是可以的。)
详细解释可以参考:malloc函数详解
2.2遍历输出链表
void looklist()
struct list_node *newp =head; //定义一个临时变量指向头节点
//通过循环遍历整个链表并输出
while(newp != NULL )
{
printf("%d\n",newp->data);
newp = newp->next;
}
2.3查询链表某节点
struct Node* FindNode(int a )
{
struct list_node *newp=head;
while( newp != NULL) //遍历整个链表
{
if(data == temp->data) //找到则返回该节点
return temp;
temp = temp->next;
}
return NULL; //未找到
}
2.4尾增加一个节点
void addlist(int data)
{
//创建一个新节点,并申请内存空间
struct list_node* newp=(struct list_node*)malloc(sizeof(struct list_node));
//第一种是链表为空,即一个节点也没有
if(head = NULL )
{
head = newp;
}
else//第二种链表不为空
{
end->next=newp;
}
end = newp; //尾结点始终指向最后一个
}
2.5尾删除一个节点
void delectend()
{
//第一种链表为空
if(end = NULL)
{
printf("链表为空,无需删除\n");
return 0;
}
//第二种链表不为空
//只有一个节点
if (head == end)
{
free(head);
head=NULL;
end=NULL;
}
else //多个节点
{
struct list-node* newp =head;
while (temp->next!=end)//找到尾部
{
newp= newp->next;
}
//释放尾巴
free(end);
//再将倒数第二个设为end
end=temp;
//尾巴指针为NULL
end->next=NULL;
}
}
2.6头删除一个节点
void delecthead()
{
struct list_node* newp=head; //保留旧头
//判断是否为空
if(NULL == head)
{
printf("链表为空,无需删除\n");
return 0;
}
head=head->next;//头的第二个节点变成新的头
free(newp); //将旧头空间释放
}
2.7删除指定节点
void delectlist(int data)
{
//判断链表是否为空
if(NULL==head)
{
printf("链表为空,无需删除\n");
return 0;
}
//不为空,则遍历链表找到这个节点
struct list_node* newp = head
while(newp !=NULL)
{
if(data == newp->data)
{
return newp;
}
newp = newp->next;
}
if(NULL == newp)
{
printf("查无此点\n");
return 0;
}
if(head==end) //只有一个节点
{
free(head);
head = NULL;
end=NULL;
}
else if(head->next==end) //有两个节点
{ if(end==newp)
{ delestend(); }
else if(newp==head)
{ delecthead(); }
}
else //多个节点时
{
//看是删除头还是删除尾
if(end==newp)
delectend();
else if(temp==newp)
delecthead();
else
{ //遍历,找到需要删除的前一个
struct list_node*pt =head;
while(pt->next!=newp)
{
pt=pt->next;
}
pt->next=newp->next;
free (newp);
}
}
}
如图所示
2.8增加指定节点
void addlist(int index,int data)
{
//首先判断是否为空
if (NULL==head)
{
printf("链表为空\n");
return 0 ;
}
struct list_node* pt =FindNode(index); //调用之前定义的查询
if(NULL==pt) //没有此节点
{
printf("没有找到指定节点\n");
return 0;
}
//若有节点,则创建临时节点
struct list_node* newp =(struct Node *)malloc(sizeof(struct Node));
newp->data=data;
newp->next=NULL;
if (pt == end) //末端插入
{
end->next=newp;
end=newp;
}
else //中间插入
{
newp->next=pt->next;
pt->next=newp;
}
}
在中间也类似。
2.9 主函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list_node
{
int data ;
struct list_node *next ;
}
struct list_node* head= NULL;
struct list_node* end = NULL;
//定义链表头尾指针
void main ()
{
struct Node* FindNode;
for(i=0;i<10;i++)
addlist(i); //直接调用之前的2.4中的函数,进行链表的生成
/* delectend(); //删除尾节点
delectlist(5); //删除第五个节点
*/
looklist() //遍历链表,输出
}
3.单向和双向循环链表
1.单向循环链表
就是表的最后一个元素指向了头节点而不是为空(null)
2.双向循环链表
相当于两个单向循环链表的叠加。
4.总结
通过一段时间leetcode网站的刷题,发现了自己的很多问题,之前学习的基础知识不够扎实,在刷题有很多时候一道题需要很长的时间,而且很多答案不是特别看得懂,思路不清晰,所以打算整理一下一些数据结构方面的基础。