【数据结构】链表—C/C++实现

🌈个人主页:Sarapines Programmer
🔥 系列专栏:《数据结构奇遇记》
🔖墨香寄清辞:墨痕寄壮志,星辰梦未满。 通幽径心凝意,剑指苍穹势如山。

目录结构


前言

1. 单链表

1.1 定义

1.2 常用操作

1.2.1 创建

1.2.2 输出

1.2.3 插入

1.2.4 删除

1.2.5 销毁

1.3 完整代码

2. 双链表

2.1 定义

2.2 常用操作

2.2.1 创建

2.2.2 输出

2.2.3 插入

2.2.4 删除

2.2.5 销毁

2.3 完整代码

3. 循环链表

3.1 定义

3.2 扫描链表结束的临界条件

3.3 环形链表

致读者


前言

什么是链表?

线性表的链式存储结构称为链表(Linked List)。链表是一种常见的线性数据结构,用于组织和存储一系列元素,这些元素以节点(Node)的形式连接在一起。每个节点包括两个主要部分:用于存储数据的数据域(Data Field)和指向节点的指针域(Next Pointer)。链表可以有不同的变种,包括单链表双链表循环链表等。

双链表和单链表的优缺点


1. 单链表

1.1 定义

typedef struct LNode//单链表定义
{
    int data;
    struct LNode *next;
}LNode;

1.2 常用操作

1.2.1 创建

尾插法创建:(额...头插和尾插会一个就行,因为尾插法创建的顺序和输入数组一样所以习惯了)

void CreateListB(LNode **L,int a[],int n) //尾插法创建单链表
{
    LNode *r,*s;
    *L=(LNode *)malloc(sizeof(LNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=a[i];
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

1.2.2 输出

void DispList(LNode *L) //输出单链表
{
    LNode *p=L;//p指向头节点
    while (p!=NULL)
    {
        printf("%d ",p->next->data);
        p=p->next;
    }
    printf("\n");
}

1.2.3 插入

void InsertList(LNode **L,int i,int e)//位置i插入元素e
{
    LNode *p,*s,*q;
    p=*L;//p指向头结点

    int j=0;
    while (p!=NULL && j<i-1)//p定位到第i-1节点
    {
        p=p->next;
        j++;
    }
    s=(LNode *)malloc(sizeof(LNode));
    s->data=e;

    q=p->next;
    s->next=q;
    p->next=s;
}

1.2.4 删除

void DelList(LNode **L,int i)//删除第i位置元素
{
    LNode *p=*L;//p指向头结点
    int j=0;
    while (p!=NULL && j<i-1)//p定位第i-1节点
    {
        p=p->next;
        j++;
    }
    p->next=p->next->next;
}

1.2.5 销毁

void DestoryList(LNode **L)//销毁链表
{
    LNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
    while (p!=NULL)
    {
        free(p);
        p=p2;
        p2=p2->next;
    }
}

1.3 完整代码

#include <iostream>

using namespace std;

struct LNode{
	int data;
	LNode *next;
};

//创建链表:尾插法
void CreateLNode(LNode **L,int a[],int n){
	LNode *s,*r;
	*L=(LNode *)malloc(sizeof(LNode));
	r=*L;
	for(int i=0;i<n;i++){
		s=(LNode *)malloc(sizeof(LNode));
		s->data=a[i];
		r->next=s;
		r=s;
	}
	r->next=NULL;
}

//输出链表
void DispLNode(LNode **L){
	LNode *p=*L;
	while(p->next!=NULL){
		cout<<p->next->data<<" ";
		p=p->next;
	}
	cout<<endl;
}

//插入
void InsertLNode(LNode **L,int n,int e){
	LNode *p=*L;
	for(int i=0;i<n-1 && p!=NULL;){
		p=p->next;
		i++;
	}
	if(p==NULL) cout<<"error!"<<endl;
	else{
		LNode *q,*s=(LNode *)malloc(sizeof(LNode));
		s->data=e;

		q=p->next;
		s->next=q;
		p->next=s;
	}
}
//删除
void DelLNode(LNode **L,int n){
	LNode *p=*L;
	for(int i=0;i<n-1 && p!=NULL;){
		p=p->next;
		i++;
	}
	if(p==NULL) cout<<"error!"<<endl;
	else{
		LNode *q=p->next;
		p->next=q->next;
	}
}

//查找
void Sreach(LNode **L,int e){
	LNode *p=*L;
	int count=0,flag=0;
	while(p->next!=NULL){
		if(p->next->data==e){
			flag=1;
			break;
		}
		else p=p->next;
		count++;
	}
	if(flag!=0){
		cout<<"查找成功,"<<e<<"位于第"<<count<<"位"<<endl;
	}
	else cout<<"查找失败"<<endl;
}

//销毁
void DesLNode(LNode **L){
	LNode *p=*L,*q=(*L)->next;
	while(p!=NULL){
		free(p);
		p=q;
		q=q->next;
	}
}

//判断是否为空串
void NullLNode(LNode **L){
    LNode *p=*L;
    if(p->next== NULL) cout<<"空串"<<endl;
    else cout<<"不是空串"<<endl;
}

int main(){
	LNode *L;
	int a[]={0,1,2,3,4,5,6,7,8,9,10};
	int n=sizeof(a)/sizeof(a[0]);

	CreateLNode(&L,a,n);
	DispLNode(&L);
    NullLNode(&L);

	//插入
	InsertLNode(&L,2,5);
	DispLNode(&L);
	
	//删除
	DelLNode(&L,2);
	DispLNode(&L);

	//查找
	Sreach(&L,5);

	//销毁
	DesLNode(&L);
	//NullLNode(&L);

	return 0;
}

2. 双链表

2.1 定义

typedef struct LNode//单链表定义
{
    int data;
    struct LNode *next;
}LNode;

2.2 常用操作

2.2.1 创建

尾插法创建:


void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
    DNode *r,*s;
    *L=(DNode *)malloc(sizeof(DNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=a[i];s->prior=r;//就这里不一样而已
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

2.2.2 输出

和单链表一样。

2.2.3 插入

void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
    DNode *p,*s,*q;
    p=*L;

    int j=0;
    while (p!=NULL && j<i-1)
    {
        p=p->next;
        j++;
    }
    s=(DNode *)malloc(sizeof(DNode));
    s->data=e;

    q=p->next;
    s->next=q;q->prior=s;//就这里和单链表不一样
    p->next=s;s->prior=p;//就这里和单链表不一样
}

2.2.4 删除

和单链表一样。

2.2.5 销毁

和单链表一样。

2.3 完整代码

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

typedef struct DNode
{
    int data;
    struct DNode *prior;
    struct DNode *next;
}DNode;

void CreateDListB(DNode **L,int a[],int n); //尾插法创建双链表
void DispDList(DNode *L); //输出双链表
void InsertDList(DNode **L,int i,int e);//位置i插入元素e
void DelDList(DNode **L,int i);//删除第i位置元素
void DestoryDList(DNode **L);//销毁链表


int main()
{
    DNode *L;
    int a[10]={0,1,2,3,4,5,6,7,8,9};
    CreateDListB(&L,a,10);
    InsertDList(&L,1,2);
    DelDList(&L,1);
    DispDList(L);

    DestoryDList(&L);
    DispDList(L);

    return 0;
}

void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
    DNode *r,*s;
    *L=(DNode *)malloc(sizeof(DNode));
    r=*L;
    for(int i=0;i<n;i++){
        s=(DNode *)malloc(sizeof(DNode));
        s->data=a[i];s->prior=r;
        r->next=s;
        r=s;
    }
    r->next=NULL;
}

void DispDList(DNode *L) //输出双链表
{
    DNode *p=L;//p指向头节点
    while (p!=NULL)
    {
        printf("%d ",p->next->data);
        p=p->next;
    }
    printf("\n");
}

void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
    DNode *p,*s,*q;
    p=*L;

    int j=0;
    while (p!=NULL && j<i-1)
    {
        p=p->next;
        j++;
    }
    s=(DNode *)malloc(sizeof(DNode));
    s->data=e;

    q=p->next;
    s->next=q;q->prior=s;//就这里不一样
    p->next=s;s->prior=p;//就这里不一样
}

void DelDList(DNode **L,int i)//删除第i位置元素
{
    DNode *p=*L;//p指向头结点
    int j=0;
    while (p!=NULL && j<i-1)//p定位第i-1节点
    {
        p=p->next;
        j++;
    }
    p->next=p->next->next;
}

void DestoryDList(DNode **L)//销毁链表
{
    DNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
    while (p!=NULL)
    {
        free(p);
        p=p2;
        p2=p2->next;
    }
}

3. 循环链表

3.1 定义

循环链表是一种链表数据结构,其特点是链表的尾节点指向链表中的头节点,形成一个循环。包括循环单链表和循环双链表。

循环单链表:每个节点包含一个next指针,并且尾节点的next指针指向头节点

循环双链表:每个节点包含next指针piror指针。尾节点的next指针指向头节点,头节点的piror指针指向尾节点

3.2 扫描链表结束的临界条件

p!=L  //while语句内,正常为p!=NULL

3.3 环形链表

环形链路: 环形链路指的是在链表中存在一个或多个节点形成了循环,其中一个节点指向先前的节点,导致链表永无止境地继续下去。是链表中的问题或异常情况

解决:判断链表是否为环形链表,通常可以使用两个指针(快慢指针)的方法,也称为弗洛伊德环检测算法。以下是判断环形链表的一般步骤:

  1. 使用两个指针,一个称为"慢指针",另一个称为"快指针",同时从链表的起始节点出发。

  2. 在每一步中,慢指针前进一步,快指针前进两步。如果链表中存在环,快指针最终会进入环中,并在某个时刻与慢指针相遇。如果链表不包含环,快指针将在某个时刻到达链表的末尾并未曾与慢指针相遇。

  3. 如果快慢指针相遇,那么链表包含环,可以判断为环形链表。


致读者

非知之难,行之为难;非行之难,终之斯难

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SarPro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值