单链表的各种操作——创建及遍历

说到链表,对于许多刚开始学习C语言的同学来说,的确还是有点不好理解(我就是这样的),查了好多有关链表的博客和文章,也有了自己的理解,才感觉吃透了。现在就把我学习链表这块儿的经历以及想法分享给大家吧。
对于初学链表,我们就要先与数组进行区分:
一·从逻辑属性区分:
1. 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
2. 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素
二·从内存分配上来看:
1. (静态)数组从栈中分配空间, 每个成员的地址都是连续的。(也就是说倘若数组的第一个元素在地址A,则数组第二个元素就在地址A+1。)
2. 链表从堆中分配空间, 每个节点没有相对固定的位置关系。(比如某个节点在地址A其后的节点不一定是A+1,而在内存的其他空闲区域,呈现一种随机的状态。)
接着, 就先从单链表的创建开始吧。单链表常用的创建可从有无头结点分类,而带头结点的创建方式又分为尾插法和头插法。
先讲一下不带头结点的吧,首先我们先得定义一个结构体类型,里面可以有数据域,但必须得有一个指针类型*next,它表示指向下一个结构体类型。我们现在就通过程序来讲解单链表的创建过程,以及注意的点。

    #include<stdio.h>
    #include<stdlib.h>
    struct Node
    {
        int date;//这里date 代表数据域,我们暂且在这当它为一个整型数据
        struct Node *next;//next表示指向本结构体类型的指针类型
    };
    int iCount;//定义一个全局变量,它表示结点个数
    struct Node *Create()//定义Creat函数创建链表
    {
        struct Node *pHead=NULL;//初始化链表,使头指针为空
        struct Node *pEnd,*pNew;//定义指针类型,使pEnd指向原来的结点,pNew指向新创建的结点
        iCount=0;//初始化链表长度,即结点个数为0
        pEnd=pNew=(struct Node *)malloc(sizeof(struct Node));//为pEnd和pNew分配内存空间
        scanf("%d",&date);
        while(pNew->date!=0)//用来终止循环结束的条件
        {
            iCount++;//每循环一次,结点自增
            if(iCount==1)//判断是否是第一个结点
            {
                pNew->next=pHead;//使新结点的指针指向为空
                pEnd=pNew;//pEnd即为pNew
                pHead=pNew;//头指针指向首节点,pNew最初指向首结点,也即是最后一个结点
            }   
            else
            {
                pNew->next=NULL;//新结点的指针指向为NULL
                pEnd->next=pNew;//将原来结点的指针指向为新结点
                pEnd=pNew;//将pEnd代替原来的pNew
            }
            pNew=(struct Node*)malloc(sizeof(struct Node)); 
            scanf("%d",&pNew->date);
        }       
        free(pNew);//释放结点
        return pHead;
    }
    void print(struct student *Node)
    {
        struct NOde *pTemp;//pTemp为循环所用而临时创建的
        pTemp=pHead;//临时指针指向首结点的地址
        while(pTemp!=NULL)
        {
            printf("%d\n",pTemp->date);
            pTemp=pTemp->next;
        }
    }
    void main()
    {
        struct Node *pHead;
        pHead=Create();
        print(pHead);
    }

这就是不带头结点的创建链表的方式,是不是感觉特别麻烦。所以,以后对于链表创建这块,我们还是使用带头结点的两种方式之一吧,上面的理解理解就行,实在理解不了,就放弃吧。
首先,还是直接看代码吧。
直接从创建开始吧,前面的结构体定义都和上面的一样。
这是头插法,我们需要注意的就是每次插入时,必须先连接后面的结点的指针指向,然后才连接前面的结点,还有就是遍历的时候它是逆置的,遍历时,它的头结点没有内容,需要从phead->next开始。

struct Node *creat()
{
    struct Node *h,*r,*p;
    h=(struct Node *)malloc(sizeof(struct Node));//创建头结点
    h->next=NULL;
    r=h;
    do{
        p=(struct Node *)malloc(sizeof(struct Node));
        scanf("%d",&p->date);
        p->next=r->next;//每次让让新结点的指针指向原来结点指针指向的结点
        r->next=p;//将原来结点指针指向为新结点
    }while(p->number!=0);//用来终止循环结束的条件
    return (h);
}

至于尾插法,程序代码和这个相似度太高,必须得注意区分。

struct Node *creat()
{
    struct Node *h,*r,*p;
    h=(struct Node *)malloc(sizeof(struct Node));//创建头结点
    h->next=NULL;
    r=h;
    do{
        p=(struct Node *)malloc(sizeof(struct Node));
        scanf("%d",&p->date);
        //p->next=r->next;
        r->next=p;//循环内部只是让最后一个结点指针指向连新创建的结点
        //r->next=p;
        r=p;//每次让指针r始终跟踪当前链表中的最后一个结点
    }while(p->number!=0);//用来终止循环结束的条件
    r->next =NULL;//最后才连接后面的结点
    return (h);
}

最后在拓展一点,用递归创建循环链表,附上代码,自己琢磨琢磨吧。
这只是为你们提供一点思想,就写得简单的,还是希望给你带来帮助。

#include<stdio.h>  
#include<stdlib.h>       
typedef struct node  
{  
    int data;  
    struct node *next;  
}Linklist; 
Linklist *creat(int arr[],int index,int index_cnt)
{
    Linklist *head=NULL;
    if(index_cnt <= index){
        head=(Linklist *)malloc(sizeof(Linklist));  
        head->data=arr[index_cnt];
        head->next=creat(arr,index,index_cnt+1);
    }
    return head;
 }
void print(Linklist *head)
{
    Linklist  *p=head;
    for(p=head;p!=NULL;p=p->next){
        printf("%d\n",p->data);
    }   
}
int main()  
{  
    Linklist *head;  
    int arr[5]={1,2,3,4,5};
    head=creat(arr,sizeof(arr)/sizeof(arr[0])-1,0);
    print(head);
    return 0;
}

这就是链表常见的创建方法,都是自己的理解,有不足和讲错的地方还请各位指点,最后还是希望这篇文章能给初学链表的各位带来一点收获,仅此而已。
戳我获取链表的删查和排序。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值