链表总结(单链表和双链表)

一.链表的基本概念

1.链表的概念:

  • 链表是一种逻辑顺序是连续的但物理存储单元上非连续、非顺序的存储结构。

2.链表的实现方法:

  • 链表是通过以结构体为节点,然后将所有节点通过结构体的指针域指向下一个节点连接起来(连接的实现),将数据存储到数据域中(存储的实现)。
typedef struct Node//结点结构体
{
	int val;//数据域(数据域可以多组数据,多种类型)
	struct Node *next;//指针域(用于指向下一结点)
}Node, *Linklist;//重定义:定义struct Node 类型为 Node, struct Node * 类型为 Linklist

3.链表的分类:

  • 含头结点的链表
  • 不含头结点的链表
  • 静态链表:不能动态增添结点,但对其操作和数组类似,比较便捷。(通过结构体数组实现)
    内存分配:分配一整片连续的内存空间,各个结点集中安置。
  • 双向链表:结构体内含两个指针域,一个指向前一结点,另一个指向下一结点
    双向链表的优点:对结点的删除等操作比单链表便捷,不用独立指针记录前一结点
    双向链表结构体创建:
typedef struct Node
{
	int val;//数据域
	struct Node *next;//指向后一结点
	struct Node *prev;//指向前一结点	
}*Linklist, Node;

4.链表的优缺点(同数组相比):

  • 优点:不需要预先知道数据的个数,删除添加只需要改变附近节点的指针域
  • 缺点:创建使用麻烦

5.说明:

  • 头指针为与链表节点类型相同的指针
  • 一般情况情况下,头指针所指向的第一个节点不存数据(此结点称为头结点),这样对在后面进行添加删除排序有很大的便利
  • 最后一个节点指针域存NULL。方便在遍历链表时提示遍历完成

但一般只对单链表(含头节点)和双向链表使用较多,所以只对它们进行讲解

二.链表的建立

1.含头结点:对于含头结点链表的所有操作头指针一直不变,一直指向头结点

头结点的创建
Linklist CrheadNode()//创建头结点 
{
	Linklist headNode = (Linklist)malloc(sizeof(Node));
	headNode->next = NULL;
	return headNode;
 } 
新结点的创建
Linklist CrnewNode(int val)//创建新结点 
{
	Linklist P = (Linklist)malloc(sizeof(Node));
	P->val = val; 
	return P;
}
将新结点插到第一结点前

思路:直接让新结点指向第一节点(即继承头结点的指向),再让头结点指向新结点

void AddNodehead(Linklist headNode, Linklist P)//将新结点插到第一结点结点前 
{
	P->next = headNode->next;
	headNode->next = P;	
}
将新节点插到尾之后
void AddNodetail(Linklist headNode, Linklist P)//将新节点插到尾之后 
{
	while(headNode->next)
	{
		headNode = headNode->next;	
	} 
	P->next = NULL;
	headNode->next = P;
}

头插法创建链表(重复将新结点插到第一结点前过程即可)
void CrLinsthead(Linklist headNode, int n)//头插法创建链表,n为链表长度 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val);
		AddNodehead(headNode,CrnewNode(val));//将新结点插在头结点与第一结点之间		
	}
}
尾插法创建链表(重复将新结点插到尾之过程即可)
void CrListtail(Linklist headNode, int n)//尾插法创建链表 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val); 
		AddNodetail(headNode, CrnewNode(val));	
	}
}
寻找结点(寻找第几结点前一结点,为了方便后续操作)
Linklist SeekNode(Linklist headNode, int index)//寻找第几结点前一结点,为了方便后续操作
{
	if(!headNode->next || index <= 0)
		return NULL;	
	while(--index)
	{
		if(headNode->next == NULL)
			return NULL;
		headNode = headNode->next;		
	}
	return headNode;	
} 
在第几结点位置增添结点,超出链表范围则不进行增添操作
void AddIndexnode(Linklist headNode, int index)//增添结点 
{
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	printf("请输入你要插入的值:"); 
	scanf("%d", &val);	
	Linklist P = CrnewNode(val);
	P->next = headNode->next;
	headNode->next = P;		
} 
将第几结点修改为val ,超出链表范围则不进行增添操作
void AtlerNode(Linklist headNode, int index)//将第几结点修改为val 
{ 
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)//判断是否超范围
		return;
	headNode = headNode->next;//找到结点
	if(!headNode)//判断是否超范围(避免给NULL指针赋值)
		return;	
	printf("请输入你修改的值:");//修改结点
	scanf("%d", &val);
	headNode->val = val; 		
}
删除第几结点
void DeleteNode(Linklist headNode, int index)//删除第几结点 
{
	headNode = SeekNode(headNode,index);
	if(!headNode || !headNode->next)
		return;
	Linklist p = headNode->next;	
	headNode->next = headNode->next->next;
	free(p);
}
查找第几结点并返回 (若查找失败则返回-1)
int SearchNode(Linklist headNode, int index)//查找第几结点并返回 
{
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return -1;
	headNode = headNode->next;
	if(!headNode)//判断是否超范围(避免给NULL指针赋值)
		return -1;	
	return headNode->val;	
}
链表逆置
void Listreverse (Linklist headNode)//链表逆置   逆置思想:将头结点提出来,后续将链表排在头结点后,将其依次插到头结点与第一节点之间
{ 
	Linklist p, s;
	p = headNode->next;
	headNode->next = NULL;//让头节点指向空 
	
	while (p)//若节点为空,则停止逆置 
	{
		s = p;
		p = p->next;
		
		s->next = headNode->next;//先指向头结点的指向 
		headNode->next = s;//让头结点指向自己(插入头结点和后续节点之间)		
	} 
}

返回倒数第几结点(快慢指针法)(若检索失败返回-1)
int Seeknodetail (Linklist headNode, int index)//快慢指针法 ,返回倒数第几节点的值 
{
	Linklist p_1, p_2;
	int i = 0;
	p_1 = headNode;
	p_2 = headNode;
	while (p_1 != NULL)
	{
		p_1 = p_1->next;
		if (i < index)
		{
			i++;
			continue;	
		}
		p_2 = p_2->next;
	}
	if(!p_1)
		return -1;
	return p_2->val ;
}
输出链表
void PrintList(Linklist headNode)//输出链表 
{
	headNode = headNode->next;
	while(headNode)
	{
		printf("%d  ", headNode->val);
		headNode = headNode->next;	
	}	
} 

完整代码

#include<stdio.h>
#include<stdlib.h>

typedef struct Node//结点结构体
{
	int val;//数据域(数据域可以多组数据,多种类型)
	struct Node *next;//指针域(用于指向下一结点)
}Node, *Linklist;

Linklist CrheadNode()//创建头结点 
{
	Linklist headNode = (Linklist)malloc(sizeof(Node));
	headNode->next = NULL;
	return headNode;
}

Linklist CrnewNode(int val)//创建新结点 
{
	Linklist P = (Linklist)malloc(sizeof(Node));
	P->val = val; 
	return P;
} 

void AddNodehead(Linklist headNode, Linklist P)//将新结点插到第一结点结点前 
{
	P->next = headNode->next;
	headNode->next = P;	
}

void AddNodetail(Linklist headNode, Linklist P)//将新节点插到尾之后 
{
	while(headNode->next)
	{
		headNode = headNode->next;	
	} 
	P->next = NULL;
	headNode->next = P;
}

void CrLinsthead(Linklist headNode, int n)//头插法创建链表 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val);
		AddNodehead(headNode,CrnewNode(val));		
	}
}

void CrListtail(Linklist headNode, int n)//尾插法创建链表 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val); 
		AddNodetail(headNode, CrnewNode(val));	
	}
}

Linklist SeekNode(Linklist headNode, int index)//寻找第几结点前一结点,为了方便后续操作
{
	if(!headNode->next || index <= 0)
		return NULL;	
	while(--index)
	{
		if(headNode->next == NULL)
			return NULL;
		headNode = headNode->next;		
	}
	return headNode;	
} 

void AddIndexnode(Linklist headNode, int index)//增添结点 
{
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	printf("请输入你要插入的值:"); 
	scanf("%d", &val);	
	Linklist P = CrnewNode(val);
	P->next = headNode->next;
	headNode->next = P;		
} 

void AtlerNode(Linklist headNode, int index)//将第几结点修改为val 
{ 
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	headNode = headNode->next;
	if(!headNode)
		return;	
	printf("请输入你修改的值:");
	scanf("%d", &val);
	headNode->val = val; 		
}

void DeleteNode(Linklist headNode, int index)//删除第几结点 
{
	headNode = SeekNode(headNode,index);
	if(!headNode || !headNode->next)
		return;
	Linklist p = headNode->next;	
	headNode->next = headNode->next->next;
	free(p);
}


int SearchNode(Linklist headNode, int index)//查找第几结点并返回 
{
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return -1;
	headNode = headNode->next;
	if(!headNode)//判断是否超范围(避免给NULL指针赋值)
		return -1;	
	return headNode->val;	
}

void PrintList(Linklist headNode)//输出链表 
{
	headNode = headNode->next;
	while(headNode)
	{
		printf("%d  ", headNode->val);
		headNode = headNode->next;	
	}	
} 

int main(void)
{
	int n, m;
	scanf("%d", &n);
	Linklist headNode = CrheadNode();//创建头结点
	//CrLinsthead (headNode, n);//头插法创建链表
	CrListtail (headNode, n);//尾插法创建链表
	scanf("%d", &m);
	//AddIndexnode(headNode, m);//增添结点
	//AtlerNode(headNode, m);//修改结点
	//DeleteNode(headNode, m);//删除结点
	//Listreverse(headNode)//链表逆置
	//printf("%d\n", SearchNode(headNode, m));//查找结点
	//printf("%d\n", Seeknodetail(headNode, m));//返回倒数第几结点
	//PrintList(headNode);//输出链表
	
	return 0;
}

2.双链表:双链表和单链表操作基本相似,只是在查找结点时不必查找想要查找到的结点的前一位,因为你可直接通过指向前一结点的指针找到前一结点,所以对于双链表的增添和删除过程要比单链表简单一些

双向链表结构体创建:
typedef struct Node
{
	int val;//数据域
	struct Node *next;//指向后一结点
	struct Node *prev;//指向前一结点	
}*Linklist, Node;
寻找结点(直接寻找想要结点)
Linklist SeekNode(Linklist headNode, int index)//寻找第几结点
{
	if(!headNode->next || index <= 0)//若链表为NULL,返回NULL
		return NULL;
	while(index--)//与单链表的区别,index--与--index的区别导致偏移一位
	{
		if(headNode->next == NULL)
			return NULL;
		headNode = headNode->next;		
	}
	return headNode;	
} 
将新结点插到第一结点前
void AddNodehead(Linklist headNode, Linklist P)//将新结点插到第一结点结点前 
{
	P->next = headNode->next;
	P->prev = headNode;
	if(headNode->next != NULL)/*与单链表的区别,在头插时判断链表是否为	
空,不为空则需让第一结点的前指针指向当前结点*/
		headNode->next->prev = P;
	headNode->next = P;	
}

完整代码:双链表操作和单链表基本相似,只是在每次操作时需要同时对前指针操作,使之指向前一节点

#include<stdio.h>
#include<stdlib.h> 

typedef struct Node
{
	int val;
	struct Node *next;
	struct Node *prev;	
}*Linklist, Node;

Linklist CrheadNode()//创建头结点 
{
	Linklist headNode = (Linklist)malloc(sizeof(Node));
	headNode->next = NULL;
	headNode->prev = NULL;
	return headNode;
 } 

Linklist CrnewNode(int val)//创建新结点 
{
	Linklist P = (Linklist)malloc(sizeof(Node));
	P->val = val; 
	return P;
}

void AddNodehead(Linklist headNode, Linklist P)//将新结点插到第一结点结点前 
{
	P->next = headNode->next;
	P->prev = headNode;
	if(headNode->next != NULL)
		headNode->next->prev = P;
	headNode->next = P;	
}

void AddNodetail(Linklist headNode, Linklist P)//将新节点插到尾之后 
{
	while(headNode->next)
	{
		headNode = headNode->next;	
	} 
	P->next = NULL;
	P->prev = headNode;
	headNode->next = P;
}

void CrLinsthead(Linklist headNode, int n)//头插法创建链表 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val);
		AddNodehead(headNode,CrnewNode(val));		
	}
} 

void CrListtail(Linklist headNode, int n)//尾插法创建链表 
{
	int val;
	for(int i = 0; i < n; i++)
	{
		scanf("%d", &val); 
		AddNodetail(headNode, CrnewNode(val));	
	}
}

Linklist SeekNode(Linklist headNode, int index)//寻找第几结点(规定结点从1算起) 
{
	if(!headNode->next || index <= 0)
		return NULL;	
	while(index--)
	{
		if(headNode->next == NULL)
			return NULL;
		headNode = headNode->next;		
	}
	return headNode;	
} 

void AddIndexnode(Linklist headNode, int index)//增添结点 (结点前插)
{
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	printf("请输入你要插入的值:"); 
	scanf("%d", &val);	
	Linklist P = CrnewNode(val);
	P->next = headNode;
	P->prev = headNode->prev;
	headNode->prev->next = P;
	headNode->prev = P;		
} 

void AtlerNode(Linklist headNode, int index)//将第几结点修改为val 
{ 
	int val;
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	printf("请输入你修改的值:");
	scanf("%d", &val);
	headNode->val = val; 		
}

void DeleteNode(Linklist headNode, int index)//删除第几结点 
{
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return;
	headNode->prev->next = headNode->next;
	if(headNode->next != NULL)
		headNode->next->prev = headNode->prev;	
}

int SearchNode(Linklist headNode, int index)//查找第几结点并返回 
{
	headNode = SeekNode(headNode,index);
	if(!headNode)
		return -1;
	return headNode->val;	
} 


void PrintList(Linklist headNode)//输出链表 
{
	headNode = headNode->next;
	while(headNode)
	{
		printf("%d  ", headNode->val);
		headNode = headNode->next;	
	}	
} 

int main(void)
{
	int n, m;
	scanf("%d", &n);
	Linklist headNode = CrheadNode();//创建头结点
	//CrLinsthead (headNode, n);//头插法创建链表
	CrListtail (headNode, n);//尾插法创建链表
	scanf("%d", &m);
	//AddIndexnode(headNode, m);//增添结点
	//AtlerNode(headNode, m);//修改结点
	//DeleteNode(headNode, m);//删除结点
	printf("%d\n", SearchNode(headNode, m));//查找结点
	PrintList(headNode);//输出链表
	
	return 0;
} 

简单应用

1.升序合并(力扣21)
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    if(!l1)//若其中一个为空,直接返回另一链表
        return l2;
    if(!l2)
        return l1;    
    struct ListNode *headNode = (struct ListNode*)malloc(sizeof(struct ListNode));//创建头结点
    struct ListNode *p//记录头结点
    p = headNode;
    while(l1 && l2)//将两链表中较小的结点加在p后,更新p的指向
    {
        if(l1->val < l2->val)
        {
            p->next = l1;
            l1 = l1->next;
            p = p->next;
        }
        else
        {
            p->next = l2;
            l2 = l2->next;
            p = p->next;
        }      
    }
    if(!l1)//将为遍历完的链表加在p后
        p->next = l2;
    if(!l2)
        p->next = l1;
    return headNode->next;//返回链表    
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值