链表定义 头插法 尾插法 按位序查找 按值查找 插入 删除 双链表 循环链表 静态链表 链表逆置 C语言实现

单链表的定义

typedef struct LNode{
	ElemType data;//数据域
    struct LNode *next;//指针域
}LNode,*LinkList;

说明(上面两种重命名方式)

作用是相同的,理解上不同

  1. LNode:强调这是一个结点,指向一个结点的指针就代表这个结点(在创建一个新的结点时会用到)

    LNode s = (LNode *)malloc(sizeof(LNode));
    
  2. * LinkList:强调这是一个链表,一般指向一个链表的首结点,就代表了这个链表,(在创建一个链表时会用到)

    LinkList L = (LinkList)malloc(sizeof(LNode));
    

头插法

LinkList List_HeadInsert(LinkList &L){
    LNode *s;
    int x;
    L = (LinkList) malloc(sizeof(LNode));//创建头结点
    L->next=NULL;//初始化为空链表,防止脏数据
    scanf("%d",&x);
    while(x!=9999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        s->next=L->next;//头插法
        L->next=s;
        scanf("%d",&x);
    }
    return L;
}

头插法经常用于链表逆置

尾插法

LinkList List_TailInsert(LinkList &L){
    int x;
    LNode *s,*r=L;//r为表尾指针
    scanf("%d",&x);
    while(x!=9999){
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;//尾插法
        r=s;
        scanf("%d",&x);
    }
    r->next=NULL;//尾结点置空,防止脏数据
    retuen L;
}

按序号查找

LNode *GetElem(LinkList L,int i){
    int j=1;
    LNode *p=L->next;
    if(i==0){
        return L;
    }
    if(i<1){
        return NuLL;
    }
    while(p && j<i){//表长之内寻找第i个结点
        p=p->next;
        j++;
    }
    return p;
}

按值查找

LNode &GetElem(LinkList L,ElemType e){
    LNode *p = L->next;
    while(p!=NULL && p->data!=e){
        p=p->next;
    }
    retuen p;
}

按位序插入

带头结点

//在第i个位置插入元素e
bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1)//位序从1开始
        return false;
    LNode *p;//指向当前扫描的结点
    int j=0;//当前p指向的是第几个结点
    p=L;//p指向头结点,头结点是第0个数据
    while(p!=NULL && j<i-1){//循环找到i-1个结点
        p=p->next;
        j++;
    }
    if(p==NULL){//i值不合法
        return false;
    }
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p-next;
    p->next=s;
    return true;
}

不带头结点

这里就可以理解添加头结点的作用了,操作不需要移动头指针

if(i==1){//插入第一个节点的操作不同,需要移动头指针
    LNode *s = (LNode *)malloc(sizeof(LNode));
    s->data = e;
    s->next = L;
    L = s;//移动头指针指向第一个
    return true;
}

按位序删除

bool ListDelete(Linklist &L,int i,ElemType &e){//用e返回第i个位置删除的结点
    if(i<1){
        return false;
    }
    LNode *p;
    int j=0;//当前p指向的是第几个结点
    p = L;//头结点是第0个结点
    while(p!=NULL && j<i-1){//循环找到第i-1个结点
        p=p->next;
        j++:
    }
    if(p==NULL){//i值不合法
        return false;
    }
    if(p->next==NULL){//i-1个结点之后没有结点
        return false;
    }
    LNode *q = p->next;//q指向被删除的结点
    e = q->data;
    p->next = q->next;
    free(q);//释放空间
    return true;
}

双链表

结点中有两个指针分别指向其前驱和后继节点

typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;//前驱和后继指针
}DNode,*DLinkList;

//初始化
bool InitDLinkList(DLinkList &L){
    L = (DNode *)malloc(sizeof(DNode));
    if(L==NULL)//内存不足
        return false;
    L->prior = NULL;//头结点的prior永远指向NULL
    L->next = NULL;
}
typedef struct DNode{
    ElemType data;
    struct DNode *prior,*next;//前驱和后继指针
}DNode,*DLinkList;

//初始化
bool InitDLinkList(DLinkList &L){
    L = (DNode *)malloc(sizeof(DNode));
    if(L==NULL)//内存不足
        return false;
    L->prior = NULL;//头结点的prior永远指向NULL
    L->next = NULL;
}

循环链表

最后一个结点的指针指向第一个结点

//初始化一个循环单链表
bool InitList(LinkList &L){
    L (LNode *)malloc(sizeof(LNode));
    if(L==NULL)
        return false;
    L->next = L;
    return true;
}
  • 循环单链表是一个“环”,因此在任何一个位置上插入和删除的操作都是等价的,无需判断是否是表尾
  • 若设头指针,则对表尾操作的时间复杂度是O(n)
  • 若设尾指针rr->next即是头指针,则对表头表尾进行操作都只需要O(1)

静态链表

借助数组来描述线性表的链式存储结构

  • 数据域data,指针域next,这里的指针是结点的相对地址(数组下标),游标

  • 需要预先分配一块连续签的内存空间

//第一种
#define MaxSize 50
typedef struct{
    ElemType data;
    int next;
}SLinkList[MaxSize];

//第二种
#define MaxSize 50
struct Node{
    EmelType data;
    int next;
};
typedef struct Node SLinkList[MaxSize];
//两种方式等价

//声明一个静态链表
SLinkList b;//50个结点类型大小的数组

链表逆置

单链表就地逆置,辅助空间复杂度O(1)

//方法一:将头结点摘下,然后从第一结点开始,依次插入到头结点的后面(头插法)
LinkList Reverse_1(LinkList L){
    LNode *p,*r;
    p=L->next;
    L->next=NULL;//尾指针为NULL,脏数据原因
    while(p!=NULL){
        r=p->next;//暂存p的后继,防止断链
        p->next=L->next;//头插法
        L->next=p;
        p=r;//p后移
    }
}
//方法二:思想就是将next指针指向其前驱结点,结点指针反转
LinkList Reverse_2(LinkList L){
    LNode *pre,*p=L->next,*r=p->next;//使用3个连续指针
    p->next=NULL;//第一个结点反转之后成了最后一个结点
    while(r!=NULL){//r为空,说明p为最后一个结点
        pre=p;
        p=r;
        r=r->next;
        p->next=pre;//指针反转
    }
    L->next=p//最后一个结点
    return L;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值