数据结构2-链表

单链表结构

将线性表L={a0..........an-1}中各元素分布在存储器的不同存储块,称为结点,通过地址或指针建立他们之间的联系,这种存储结构为链表结构

通过下面这个例子来理解吧

结点类型描述

我们的结点类型主要由两种类型构成,所以我们要使用结构体来进行定义,首先是存放元素的数据域和存放下个结点地址的指针域。为了方便,我们还是使用了typedef来取别名

typedef int data_t;
//构造链表节点类型
typedef struct node_t{
    data_t data;//数据域
    struct node_t *next;//保存下一个节点的指针域
}linklist;

随后我们就可以创建链表,和链表表头,随后利用malloc函数为表头申请存储空间,并初始化。

//创建链表:创建链表头节点
linklist* create_linklist()
{
    linklist *head=(linklist*)malloc(sizeof(linklist));//给头节点开辟空间
    if(NULL==head)
        return NULL;
    head->data=-1;//初始化
    head->next=NULL;//初始化
    return head;
}

 创建了头结点后方便我们用它去指向其他的结点!!!

单向链表的插入

算法思路:如下图

 首先定义一个指针指向头结点,将我们需要插入的new结点开辟空间,在这个例子中我们要将新元素插入到位置pos为1的位置,假如我们要插入的位置为pos,那么就需要将指针p指向pos位置的前驱,此刻p->next里保存的是下一个结点的地址值,将其赋值给new->next,以免后一位地址丢失,随后将new结点的地址赋值给p->next,(此时p->next在等号左边表示当前结点的指针域空间,在等号右边就表示存储的下一个结点的地址值),相当于断开原来的连接,重新链接到new结点位置,如图所示便完成了元素的插入。

我们来看看代码

//按位置插入数据
int insert_by_pos(linklist *head,int pos,data_t val)
{
    if(NULL==head)
        return -1;
    //判断位置合法性
    int len=get_length_linklist(head);
    if(pos<0 || pos>len)
        return -1;
    //准备新结点和辅助指针P
    linklist *p=head;
    //给新结点开辟空间
    linklist* new=(linklist*)malloc(sizeof(linklist));
    //给新结点初始化
    new->data=val;
    new->next=NULL;
    //找到目标位置前一个位置
    while(pos--) p=p->next;
    //插入数据
    new->next=p->next;//把目标后的结点位置接到新结点的指针域防止丢失
    p->next=new;//把目标前一个的NEXT接到新结点
}

单向链表的删除

算法思路如下图:

 代码如下:

//删除
int del_by_pos(linklist *head,int pos)
{
    if(NULL==head) return -1;//判断空间是否开辟成功
    if(1==linklist_is_empty(head))//判断链表是否为空
        return -1;
    int len=get_length_linklist(head);
    if(pos <0 || pos>len-1)
        return -1;
    linklist *p=head;//将指针P指向头结点
    linklist *q;//定义一个辅助指针
    while(pos--) p=p->next;//将P指针指向要删除结点的前驱
    q=p->next;//先把要删除结点的地址保存下来以免丢失
    p->next=q->next;//将要删除结点的后继结点地址赋值给前驱的指针域
    free(q);//释放删除结点的空间
    q=NULL;
    return 0;
}

单向链表的修改和查找

实现起来都很简单通过指针遍历即可:

//改变
int change_by_pos(linklist* head,int pos,data_t new_val)
{
    //判断HEAD
    if(NULL==head) return -1;
    //判断位置合法性
    int len=get_length_linklist(head);
    if(pos<0 || pos>len-1) return -1;
   //准备指针P遍历到POS-1的位置
    linklist *p=head;
    while(pos--) p=p->next;
    //将POS位置值修改
    p->next->data=new_val;
    return 0;
}
//按位置查找
data_t find_by_pos(linklist* head,int pos)
{
   //判断HEAD
    if(NULL==head) return -1;
    //判断位置合法性
    int len=get_length_linklist(head);
    if(pos<0 || pos>len-1) return -1;
   //准备指针P遍历到POS-1的位置
    linklist *p=head;
    while(pos--) p=p->next;
    //返回值
    return p->next->data;
}

头插法以及链表的倒置

先插入的排到后面,我们只需要关心头结点即可!

算法思路如图:

 代码:

//头插法
int insert_by_head(linklist *head,data_t val)
{
    if(NULL==head) return -1;
    linklist* new=(linklist*)malloc(sizeof(linklist));
    new->data=val;
    new->next=head->next;
    head->next=new;
    return 0;
}
//头插法实现逆序
int reverse_order_by_head(linklist* head)
{
    if(NULL==head) return -1;
    linklist *p;
    linklist *q;
    p=head->next;//将P指针指向第一个结点的地址,相当于备份
    head->next=NULL;//将原链表清空
    while(p!=NULL)//遍历链表直到末尾
    {
        q=p;//将当前要插入的结点地址保存到q
        p=p->next;//将P指向下一个结点,以防后面数据丢失
        q->next=head->next;//将当前结点插入到头结点之后
        head->next=q;
    }
    return 0;
}

链表的打印

int show_linklist(linklist* head)
{
    if(NULL==head) return -1;
    if(1==linklist_is_empty(head))
        return -1;
    linklist *p=head->next;
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    puts("");
}

单向循环链表

双向链表

我们先来看看双向链表的结点类型构造把:

//构造双向链表节点类型
typedef int data_t;
typedef struct d_node_t{
    data_t data;
   struct d_node_t *prior;
    struct d_node_t *next;
}dlinklist;

双向链表的删除和插入算法如下图所示:

 看看代码如何实现吧

//删除数据
int del_by_pos(dlinklist* head,int pos)
{
    if(NULL==head) return -1;//判断head
    int len=get_length(head);
    if(1==dlinklist_is_empty(head)) return -1;
    if(pos<0 ||pos>len-1)//判断位置合法性
        return -1;
    dlinklist *p=head;
    dlinklist *q=NULL;//初始化
    while(pos--)
        p=p->next;
    q=p->next;
    if(q->next!=NULL)//判断删除数据位置是中间还是末尾
    {
        p->next=q->next;
        q->next->prior=p;//删除中间
        free(q);
        q=NULL;
    }
    else//删除末尾
    {
        p->next=NULL;
        free(q);
        q=NULL;
    }
    return 0;

}

双向链表的插入:

 代码如下:

//插入数据
int insert_by_pos(dlinklist* head,int pos,data_t val)
{
    if(NULL==head) return -1;
    //判断位置合法性
    int len=get_length(head);
    if(pos<0 || pos>len) return -1;
    //准备指针P找到POS-1的位置
    dlinklist* p=head;
    while(pos--)
        p=p->next;
    //准备新结点
    dlinklist* new=(dlinklist*)malloc(sizeof(dlinklist));
    new->next=NULL;
    new->prior=NULL;
    new->data=val;
    //判断插入位置在末尾还是在中间
    if(p->next!=NULL)//p->next==NULL表示在末尾插入
    {
        new->next=p->next;//中间插入的核心代码
        new->next->prior=new;
        new->prior=p;
        p->next=new;
    }
    else
    {
        new->prior=p;//末尾插入的代码
        p->next=new;
    }


}

双向循环链表
线性表的应用:Joseph问题

 

 思路:先建立单向循环链表,从1-n依次赋值,定义一个指针L指向第一个节点位置,方便构成循环链表,利用循环遍历找到第k个节点位置,将r指针指向要删除结点的前驱,将p指向被删除的结点,以免丢失地址,删除结点,将r指向新一轮起点,重复只剩下最后一个。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值