数据结构-单链表


title: 链表笔记
date: 2022-10-11 16:24:26
tags:数据结构

单链表

特点

用一组任意的存储单元存储线性表的数据结构(这组数据结构单元可以是连续的,也可以是不连续的)

优点不要求大片连续空间,改变容量方便

缺点不可随机存取,要耗费一定空间存放指针


算法描述

typedef struct LNode
{
	ElemType data; 	//节点的数据域
    struct LNode * next;	//节点的指针域
}LNode, *LinkList;	//LinkList为指向结构体LNode的指针类型

这是下面所使用的函数原型

//定义单链表
typedef struct LNode
{
	int data; 	//节点的数据域
	struct LNode* next;	//节点的指针域
}LNode, * LinkList;	//LinkList为指向结构体LNode的指针类型

//初始化链表
void InitList(LinkList &L)
{
	L = (LNode *)malloc(sizeof(LNode));	//生成新节点作为头节点,用头指针L指向头节点
	L->next = NULL;	//头节点指针域置空
}

//创建单链表(头插法)
void CreateList_H(LinList &L, int i)
{//逆序位输入n个元素的值, 建立带头节点的单链表L
	InitList(L);	//先建立一个带头节点的空链表
   int i;
	LNode* p;
    for(i = 0; i < n; ++i)
    {
		p = (LNode*)malloc(sizeof(LNode));	//生成新节点*p
       scanf_s("%d", &p->data);	//输入元素值赋给新节点*p的数据域
   
        //将新节点*p插入到头节点之后
        p->next = L->next;	
        L->next = p;
	}
}

//创建单链表(尾插法)
void GreateList_R(LinkList &L, int n)
{//正序位输入n个元素的值, 建立带头节点的单链表L
	InitList(L);	//先建立一个带头节点的空链表
   LNode* p,*r;
    r = L;	//尾指针r指向头节点
    for(i = 0; i < n; ++i)
    {
		p = (LNode*)malloc(sizeof(LNode));	//生成新节点
       scanf_s("%d", &p->data);	//输入元素值赋给新节点*p的数据域
      	
        //将新节点*p插入到 *r之后
        p->next = NULL;	
        r->next = p;
       
        r = p;	//尾指针r指向新的尾节点*p
    } 
}

//取值
int GetElem(LinkList L, int i, int &e)
{	//在带头节点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
	LNode *p;
    int  j = 1;
    p = L -> next; 	//初始化,p指向首元节点,计数器j赋为1
    while(p && j < i)	//顺链域向后查找,直到p为空或p指向第i个元素
    {
		p = p->next;	//p指向下一个节点
        ++j;	//计数器j加1
	}
    if(!p || j > i)		//i的值不合法i > 0或 i <= 0
    {
		printf("取值失败!\n");
		exit(0);
    }
    e = p->data;	//去第i个节点的数据域
    return OK;
}

// 查找
LNode *LocateElem(LinkList L, int e)
{//在带头节点的单链表中查找值为e的元素
    LNode* p;
	p = L->next;	//初始化p指向的首元节点
    while	(p && p->data != e)		//依次顺着链域next向后查找,直到p为空或p所指向节点的数据域等于e
    {
		p = p->next;	//p指向下一个节点
	}
    if (p == NULL)
	{
		printf("查找失败!");
		exit(0);
	}
    return p;	//查找成功返回值为e的节点地址p,查找失败p为NULL
}

//插入
void ListInsert(LinkList& L, int i, int e)
{//在带头节点的单链表L中第i个位置插入值为e的新节点
	LNode *p = L;
	int j = 0;
	while (p && j < i - 1)	//查找第i - 1个节点,p指向该节点
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1)	//i > n + 1或者i < 1
	{
		printf("删除失败!");
		exit(0);
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	//生成一个新节点*s
	s->data = e;	//将新节点*s的数据域置为e
	s->next = p->next;	//将新节点*s的指针域指向节点a~i~
	p->next = s;	//将节点p的指针域指向新节点*s
}

//删除
void ListDelete(LinkList& L, int i)
{//在带头节点的单链表L中,删除第i个元素
	LNode *p = L;
	int j = 0;
	while ((p->next) && (j < i - 1))		//查找第i - 1个节点,p指向该节点
	{
		p = p->next;
		++j;
	}
	if (!(p->next) || (j > i - 1))		//i >n 或者i < 1,删除位置不合理
	{
		printf("删除失败!");
		exit(0);
	}
	LNode *q = p->next;	//临时保存被删除节点 的地址在q中,以备释放
	p->next = q->next;		//改变删除节点前驱节点的指针域
	free(q);	//释放节点的空间
}

//遍历链表
void pritfLNode(LinkList L)
{
	LNode* p;
	p = L->next;

	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
}


一. 创建单链表

1. 头插法(前插法)创建单链表 ( 时间复杂度为O(n))

(输入顺序和线性表中的逻辑顺序相反)

算法步骤

创建只有一个头节点的空链表

②根据带创建链表包括的元素个数n,循环n次执行以下操作:

  • 生成一个新节点 *p

  • 输入元素值赋给新节点 *p 的数据域

  • 将新节点 *p 插入到头节点之后先插入后递增指针

void CreateList_H(LinList &L, int i)
{//逆序位输入n个元素的值, 建立带头节点的单链表L
	InitList(L);	//先建立一个带头节点的空链表
    for(i = 0; i < n; ++i)
    {
		p = new  LNode;		//生成新节点*p
        cin>>p->data;	//输入元素值赋给新节点*p的数据域
   
        //将新节点*p插入到头节点之后
        p->next = L->next;	
        L->next = p;
	}
}

int main(void)
{
	int n;

	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_H(L,n);	//创建单链表(头插法)
	printf("链表:");
	pritfLNode(L);	//打印单链表

	return 0;
	
}

2. 尾插法(后插法)创建单链表( 时间复杂度为O(n)

(输入顺序和线性表中的逻辑顺序相反)

算法步骤

创建只有一个头节点的空链表

② 尾指针r初始化,指向头节点;

③ 根据创建链表包括的元素个数n,循环n次执行以下操作

  • 生成一个新节点*p

  • 输入元素值赋给新节点*p的数据域

  • 将新节点*p插入到 *r之后

  • 尾指针r指向新的尾节点*p先插入后递增指针

算法描述

void GreateList_R(LinkList &L, int n)
{//正序位输入n个元素的值, 建立带头节点的单链表L
	InitList(L);	//先建立一个带头节点的空链表
    r = L;	//尾指针r指向头节点
    for(i = 0; i < n; ++i)
    {
		p = new LNode;	//生成新节点
        cin>>p->data;	//输入元素值赋给新节点*p的数据域
      	
        //将新节点*p插入到 *r之后
        p->next = NULL;	
        r->next = p;
       
        r = p;	//尾指针r指向新的尾节点*p
    }
}

int main(void)
{
	int n;

	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_R(L, n); 	//创建单链表(尾插法)
	printf("链表:");
	pritfLNode(L);	//打印单链表

	return 0;
	
}

二. 基本操作

1. 初始化(创建只有一个头节点的空链表

算法描述

//带头节点
Status InitList(LinkList &L)
{
	L = new LNode;	//生成新节点作为头节点,用头指针L指向头节点
    L->next = NULL;	//头节点指针域置空
    return OK;
}

//不带头节点
Status InitList_N(LinkList &L)
{
    L->next = NULL;	//头节点指针域置空
    return OK;
}

void InitList(LinkList &L)
{
	L = new LNode;	//生成新节点作为头节点,用头指针L指向头节点
    L->next = NULL;	//头节点指针域置空
}

2.取值(平均时间复杂度为O(n))

算法步骤

用指针p指向首元节点,用j做计数器初值赋为1

从首元节点开始依次顺着链域next向下访问,只要指向当前节点的指针不为空(NULL),并且没有到达序号为i的节点,则循环执行以下内容:

  • p 指向下一个节点

  • 计数器j加一

退出循环,如果指针p为空,或者计数器j大于i,说明指定的序号i值不合法(i大于表长或i小于等于0),取值失败返回ERROR;否则取值成功,此时j = i,p指向的节点就是要找的第i个节点,用参数e保存当前节点的数据域,返回OK。

算法描述

Status GetElem(LinkList L, int i, ElemType &e)
{	//在带头节点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
	 p = L -> next; 	//初始化,p指向首元节点,计数器j赋为1
    j = 1;
    while(p && j < i)	//顺链域向后查找,直到p为空或p指向第i个元素
    {
		p = p->next;	//p指向下一个节点
        ++j;	//计数器j加1
	}
    if(!p || j > i)		//i的值不合法i > 0或 i <= 0
    {
		return ERROR;
    }
    e = p->data;	//去第i个节点的数据域
    return OK;
}

int main(void)
{
	int n;	//链表长度
	int e, i;
	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_R(L, n);
	
	printf("链表:");
	pritfLNode(L); //打印单链表

	printf("\n请输入要取值的元素序号:");
	scanf_s("%d", &i);

	e = GetElem(L, i, e);	//取值
	printf("取值结果:%d", e);	

	return 0;
}

3. 查找(平均时间复杂度为O(n))

算法步骤

用指针p指向首元节点

从首元节点开始依次顺着链域next向下查找,只要指向当前节点的指针p不为空,并且p所指向的数据域不等于给定值e,则循环执行以下操作:p指向下一个节点

返回p。若查找成功,p此时指向节点的地址值,若查找失败,则p的值为NULL

算法描述

LNode *LocateElem(LinkList L, ElemType e)
{//在带头节点的单链表中查找值为e的元素
	p = L->next;	//初始化p指向的首元节点
    while	(p && p->data != e)		//依次顺着链域next向后查找,直到p为空或p所指向节点的数据域等于e
    {
		p = p->next;	//p指向下一个节点
	}
    return p;	//查找成功返回值为e的节点地址p,查找失败p为NULL
}

int main(void)
{
	int n;	//链表长度
	int e, i;
	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_R(L, n);

	printf("链表:");
	pritfLNode(L); //打印单链表

	printf("\n请输入要查找的元素:");
	scanf_s("%d", &e);

	LNode* p = LocateElem(L, e);	//查找
	printf("查找结果:%d", p->data);

	return 0;
}

4. 插入(平均时间复杂度为O(n))

算法步骤

查找节点ai-1 并由指针p指向该节点

生成一个新节点*s

将新节点*s的数据域置为e

将新节点*s的指针域指向节点ai

将节点p的指针域指向新节点*s

算法描述

Status ListInsert(LinkList &L, int i ,ElemType e)
{//在带头节点的单链表L中第i个位置插入值为e的新节点
	p = L;
    j = 0;
    while(p && j < i - 1)	//查找第i - 1个节点,p指向该节点
    {
		p = p->next;
        ++j;
	}
    if(!p || j >i - 1)	//i > n + 1或者i < 1
    {
		return ERROR;
	}
    s = new LNode;	//生成一个新节点*s
    s->data = e;	//将新节点*s的数据域置为e
    s-next = p->next;	//将新节点*s的指针域指向节点a~i~
    p->next = s;	//将节点p的指针域指向新节点*s
    return OK;
}

int main(void)
{
	int n;	//链表长度
	int e, i;
	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_R(L, n);

	printf("链表:");
	pritfLNode(L); //打印单链表

	
	printf("\n请输入要插入的位置:");
	scanf_s("%d", &i);
	printf("\n请输入要插入的元素:");
	scanf_s("%d", &e);
	
	ListInsert(L, i, e);	//插入

	printf("链表:");
	pritfLNode(L); //打印单链表
	return 0;
}

5. 删除(平均时间复杂度为O(n))

算法步骤

① 查找节点ai-1 并由指针p指向该节点

② 临时保存待删除节点ai 的地址在q中,以备释放

③ 将节点*p的指针域指向ai 的直接后继节点

④ 释放节点ai 的空间

算法描述

Status ListDelete(LinkList &L, int i)
{//在带头节点的单链表L中,删除第i个元素
	p = L;
    j = 0;
    while((p->next) && (j < i - 1))		//查找第i - 1个节点,p指向该节点
    {
		p = p->next;
        ++j;
	}
    if( !(p->next) || (j > i - 1))		//i >n 或者i < 1,删除位置不合理
    {
		return ERROR;
    }
    q = p->next;	//临时保存被删除节点 的地址在q中,以备释放
    p->next = q->next;		//改变删除节点前驱节点的指针域
    delete q;	//释放节点的空间
    return OK;
}

算法描述

int main(void)
{
	int n;	//链表长度
	int i;
	LNode* L; //声明链表
	InitList(L); // 初始化链表
	printf("请输入链表长度:");
	scanf_s("%d", &n);
	printf("请输入链表数据:");
	CreateList_R(L, n);

	printf("链表:");
	pritfLNode(L); //打印单链表

	
	printf("\n请输入要删除的位置:");
	scanf_s("%d", &i);
	
	ListDelete(L, i);	//删除

	printf("链表:");
	pritfLNode(L); //打印单链表
	return 0;
}
												单链表完



  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值