(C/C++)数据结构一:单链表、双向循环链表基本操作(详细注释)

这篇博客详细介绍了C/C++中单链表的基本操作,包括初始化、取值、查找、插入和删除等,并提供了相应的伪代码。同时,对比了顺序表和链表在空间利用、存储密度、存取速度和插入删除效率上的差异,帮助读者理解两种数据结构的特点和适用场景。
摘要由CSDN通过智能技术生成

c/c++伪代码形式:


//单链表基本操作


#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status
typedef char ElemType
//单链表

typedef struct  Lnode{
    ElemType data;
    struct Lnode*next;
}LNode,*LinkList;
//单链表重要操作

p = L;//p指向头结点
s = L->next;//s指向首元结点
p = p->next;//p指向下一结点

//初始化

Status InitList_L(LinkList L){
    L = (LinkList)malloc(sizeof(LNode));//L是头指针是头结点的地址
    L->next = NULL;//下面没有结点(无首元结点)
    return OK;
}
//取值:取单链表中第i个元素的内容
/*1、从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p处置p=L->next
  2、j做计数器,累计当前扫描过的结点数,j初值为1
  3、当p指向扫描到的下一结点时,计数器j加1
  4、当j==i时,p所指的结点就是要找的第i个结点*/

Status GetElem_L(LinkList L, int i,ElemType &e){//获取线性表L中的某给数据元素的内容,通过e(引用)返回
    p = L->next;//初始化从首元元素触发
    j = 1;//计数器
    while(p&&j<i){//向后扫描,直到p指向第i个元素或p为空
        p = p->next;
        ++j;
    }
    if(!p||j>i)return ERROR;//第i个元素不存在
    e = p->data;
    return OK;
}
//查找:按值查找:根据指定数据获取该数据所在的位置(该数据的地址
      //按值查找:根据指定数据获取该数据所在的位置序号(时第几个数据元素)
/*1、从第一个结点起,依次和e相比较
  2、如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”或地址
  3、如果查遍整个链表都没有找到其值和e相等的元素,则返回0或“NULL”*/

LNode *LocateELem_L(LinkList L,Elemtype e){
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    p = L->next;
    while(p&&p->data!=e)
        p = p ->next;
    return p;
}

int LocateElem_L(LinkList L,Elemtype e){
    //返回L中值为e的数据元素的位置序号,查找失败返回0
    p = L->next;
    j = 1;
    while(p&&p->data!=e){
        p = p->next;
        j++;
    }
    if(p) return j;
    else return 0;
}
//插入:在第i个结点前插入值为e的新结点
/*1、首先找到ai-1的存储位置p
  2、生成一个数据域为e的新结点s
  3、插入新结点:(1)新结点的指针域指向结点ai
                (2)结点ai-1的指针域指向新结点*/
//在L中第i个元素之前插入数据元素e
Status Listlnsert_L(LinkList *L,int i,ElemType e){
    p = L;//从头结点出发
    j = 0;//计数器
    while ((p&&j<i-1)){//寻找第i-1个结点,p指向i-1结点
        p = p->next;
        ++j;
    }
    if(!p||j>i-1)return ERROR;//i大于表长+1或者小于1,插入位置非法
    s = (LinkList)malloc(sizeof(LNode));//生成新节点s,将结点s的数据域置为e
    s->data =e;
    s -> next =  p -> next
    p -> next = s;
    return OK;
}
//删除:删除第i个结点
/*1、先找到ai-1的存储位置p,保存要删除的ai的值
  2、令p->next指向ai+1
  3、释放结点ai的空间*/

Status ListDelete_L(LinkList*L,int i,ElemType &e){
    p = L;
    j = 0;
    while(p->next&&j<i-1){
        p = p->next;
        ++j;//寻找第i个结点,并令p指向其前驱
    }
    if(!(p->next)||j>i-1)return ERROR;//删除位置不合理
    q = p->next //临时保存被删结点的地址以备释放
    p ->next = q ->next;//改变删除结点前驱结点的指针域p ->next = p ->next -》next
    e = q->data//保存删除结点的数据域
    free(q);//释放删除结点的空间
    return OK;
}
    
    


//销毁链表
Status DestroyList_L(LinkList L){
    LNode *p//或LinkList p;
    while(L){
        p = L;//清除p,L去找下一个,如果直接清除L就找不到下一个了
        L = L->next;
        free(p);
    }
}
//清空链表(销毁是毁坏整个链表,清空会保留链表(L存在)但元素为0
Status clearlist(LinkList L){//将L重置为空表
    LNode *p,*q;//或Linklist p,q
    p = L->next;
    while(p){//没到表尾
        q = p->next;//p,q两个哨兵,q去找p的下一个
        free(p);
        p = q 
    }
    L ->next = NULL;//头结点指针域为空
    return OK;
}
//求单链表表长

int ListLength_L(LinkList L){
    LNode *p;
    p = L->next//p指向首元结点
    int i = 0;
    while(p){
        i++;
        p = p->next;
    }
    return i;
}
//建立单链表:
//头插法——元素插入在链表头部,也叫前插法
/*1、从一个空表开始,重复读入数据;
  2、生成新结点,将读入数据存放到新结点的数据域中
  3、从最后一个结点开始,依次将各结点插入到链表的前端*/
void CreateList_H(LinkList L,int n){
    L = (LinkList)malloc(sizeof(LNode));
    L->next = NULL;//创建头结点
    for(i = n;i > 0;--i){
        p = (LinkList)malloc(sizeof(LNode));//建立第一个结点
        scanf("%d",&p->data);//插入结点
        p ->next = L ->next;//新结点指向头结点之后的结点
        L ->next = p;
    }
}
//尾插法——元素插入在链表尾部,也叫后插法
/*1、从一个空表L开始,将新结点逐个插入到链表的尾部,尾
     指针R指向链表的尾结点
  2、初始时,R同L均指向头结点,每读入一个数据元素则申请一个新结点,
     将新结点插入到尾结点后,R指向新结点*/
void CreateList_R(LinkList *L,int n){
    L = (LinkList)malloc(sizeof(LNode));
    LinkList *R = L;
    for(int i = 0;i < n;i++){
        p = (LinkList)malloc(sizeof(LNode));
        p ->next = NULL;
        R ->next = p;
        R = p;
    }
}
//双向链表
typedef struct DuLNode{
    Elemtype data;
    struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
//双向链表的插入

Status Listlnsert_DuL(DuLinkList &L,int i, ElemType e){
    if(!(p = GetElemP_DuL(L,i)))return ERROR;
    s = (DuLNode*)malloc(sizeof(DuLNode));
    s -> data = e;
    s -> prior = p -> prior;
    p -> prior ->next = s;
    s -> next = p;
    p -> prior = s; 
    return OK;
}

//双向链表的删除

void ListDelete_DuL(DuLink &L,int i,ElemType &e){
    //删除带头结点的双向循环链表L的第i个元素,并用e返回
    if(!(p= GetElemP_DuL(L,i)))return ERROR;
    e = p->data;//保存被删元素
    p -> prior -> next = p -> next;
    p -> next -> prior = p -> prior;
    free(p);
    return OK;
}

顺序表和链表的比较:

顺序表链表
空间存储空间预先分配,会导致空间闲置或溢出动态分配,不会出现空间闲置或溢出
存储密度不用为表示结点间的逻辑关系而增加额外的存储开销,存储密度等于1需要借助指针来体现元素间的逻辑关系,存储密度小于1
时间存取元素随机存取,按位置访问元素的时间复杂度为O(1)顺序存取,按位置访问元素时间复杂度为O(n)
插入、删除平均移动约表中一半元素,时间复杂度为O(n)不需移动元素,确定插入、删除位置后、时间复杂度为O(1)
适用情况

1、表长变化不大,且能事先确定变化的范围

2、很少进行插入或删除操作、经常按照元素位置序号访问数据元素

1、长度变化较大

2、频繁进行插入或删除操作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值