c语言数据结构(二)----线性表链式存储结构及运算实现(单链表篇)

上回我们学习了用数组构造顺序表,顺序表申请的一块数组地址是连续的,这次我们学习用指针构造链表,这时所构造的链表地址不一定是连续的,比较活用。

链表分为单链表循环链表双向链表

今天先来码单链表,链表可以带头结点,也可以不带头结点,带头结点更方便插入、删除的运算,所以我选择了带头结点。

  • 不带头结点的单链表

不带头结点的单链表

  • 带头结点的单链表

带头结点的单链表

/*头文件及宏定义*/
#include<stdio.h>
#include<stdlib.h>
typedef int datatype;
typedef struct node	    //定义结点
{
    datatype data;          //数据域
    struct node *next;      //指针域
}LNode;

链表的建立

链表建立可以用两种方法:头插法尾接法

  • 头插法:在链表头一个元素结点前插入新的元素,这样插入的链表元素是倒着 的顺序。

头插法

  • 尾接法:在链表的尾结点后新增元素,这样建立的链表是顺着的。
    尾接法
//头插法(往首元素前面插,倒着创建)
LNode *CreateLinkList1()
{
    LNode *head,*p;                         //head为链表的头指针,p为链表的暂存指针
    int x;
    head=(LNode *)malloc(sizeof(LNode));    //生成头结点
    head->next=NULL;
    printf("请输入单链表元素:");
    scanf("%d",&x);
    while(x!='\n')
    {
        p=(LNode*)malloc(sizeof(LNode));    //申请新结点
        p->data=x;                          
        p->next=head->next;                 //新结点指向头元素的结点
        head->next=p;                       //head指向新结点
        scanf("%d",&x);
    }
    return head;
}
//尾接法(在最后一个元素末尾插,顺着建立)
LNode *CreateLinkList2()
{
    LNode *head,*p,*q;                      //head为链表的头指针,p为链表的暂存指针,q为指向末尾结点的指针
    int x;
    head=(LNode *)malloc(sizeof(LNode));    //生成头结点
    head->next=NULL;
    q=head;                                 //在链表没有元素的情况下可以尾指针q可以看作是头指针
    printf("请输入单链表元素:");
    scanf("%d",&x);
    while(x!=-1)                            //输入-1则停止
    {
        p=(LNode*)malloc(sizeof(LNode));    //申请暂存结点空间
        p->data=x;
        p->next=NULL;
        q->next=p;                          //将链表连接起来
        q=p;                                //q指向最新的结点,即尾结点
        scanf("%d",&x);
    }
    return head;
}
//求表长
int Length_LinkList(LNode *head)
{
    LNode *p=head;
    int i=0;
    while(p->next!=NULL)
    {
        p=p->next;
        i++;
        //printf("%d %c\n",i,p->data);		用来测试
    }
    return i;
}

查找运算

查找头结点为head的链表第i个元素

//按位置查找
LNode *Get_LinkList(LNode *head,int i)
{
    LNode *p=head;
    int j=0;
    while(p->next!=0&&j<i)
    {
        j++;
        p=p->next;
    }
    if(i==j)                                 //若找到则返回指针值,否则返回空值
        return p;
    else
        return NULL;
}

插入运算

在第i个位置插入元素x

//插入运算
void Insert_LinkList(LNode *head,int i,int x)//head指向头结点,i为插入的位置,x为插入的值
{
    LNode *p,*q;
    q=Get_LinkList(head,i-1);               //找到要插入结点的前一个位置
    if(q==NULL)
        printf("位置非法,无法插入");
    else
    {
        p=(LNode*)malloc(sizeof(LNode));    //申请结点空间
        p->data=x;
        p->next=q->next;                    //插入结点
        q->next=p;
    }
}

删除运算

删除第i个元素,这里运用free()函数释放内存空间

//删除运算
void Del_LinkList(LNode *head,int i)        //head指向头结点,i为要删除的结点位置
{
    LNode *p,*q;
    q=Get_LinkList(head,i-1);               //找到要删除结点的前一个位置
    if(q==NULL)
        printf("删除位置非法!");
    else
    {
        p=q->next;                          //p指向要删除的i结点
        q->next=p->next;                    //从链表中删除第i个结点
        free(p);                            //释放第i个结点的存储空间
    }
}

调试代码

int main()
{
    /*建立链表并输出*/	 
    LNode *head,*p,*q;
    head=CreateLinkList2();
    p=head;					//使p指向头结点,以下操作皆是
    printf("表长为:%d\n",Length_LinkList(p));
    p=p->next;
    printf("链表元素为:");
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    /*查找*/
    int a;
    printf("请输入要查找的元素位置:");
    scanf("%d",&a);
    p=head;
    q=Get_LinkList(p,a);
    if(q!=NULL)
        printf("元素值为:%d",q->data);
    else
        printf("该元素不存在!");
    /*插入(用时取,不用则注释掉)*/
    p=head;
    int i;
    int x;
    printf("请输入要插入的位置:");
    scanf("%d",&i);
    printf("请输入要插入的元素值:");
    scanf("%d",&x);
    Insert_LinkList(p,i,x);
    /*删除(用时取,不用则注释掉)*/
    p=head;
    int i;
    int x;
    printf("请输入要删除的位置:");
    scanf("%d",&i);
    Del_LinkList(p,i);
    /*输出*/
    p=head;
    printf("表长为:%d\n",Length_LinkList(p));
    p=p->next;
    printf("链表元素为:");
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    return 0;
}

总结
这里的链表确实考验对指针的理解,我也是思考了很久才得出可以让自己信服的解释,指针指向一个结点,其中p=head我想了很久,其含义为p指针指向head指针指向的地址,这是和小伙伴讨论了许久才得到的答案。懂得了指针的含义,链表也就很容易地写出来了。

循环链表由于只是在单链表的基础上,将尾结点指向头结点,所以就不再多加赘述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章