单链表的基础细节与常见功能实现

     对于很多人来讲链表的逻辑结构都能明白,可一到存储结构怎么代码实现就感到异常困难,打开IDE就懵,我自己也是从这个阶段心心念念跋山涉水终于搞懂,激动之下想写一帖给那些还在这个阶段徘徊的伙伴。

   本人才疏学浅 如有错误欢迎同行指出共同交流 本人感激不尽

本博客将彻底讲解

  1. 链表的基础知识和隐藏细节
  2. 彻底区分弄懂带头结点和不带头结点的单链表
  3. 十五个常见链表功能考研题目的实现
  4. 全部讲解均包含相应代码

目录包括

  • 基础讲解1
  • 基础讲解2
  • 手把手写代码
  • 其他功能实现

 

  一  基础知识讲解1(有基础可以跳过)

        首先对于链表的不理解大多可能还是指针的基础不熟,再第一部分对一些指针的地方我们会重点解释。我们知道一个结点包含一个数据域和一个指针域,那么我们怎么用一个结构去包含这个结点的信息呢,这里就需要用到结构体了。

typedef int ElemType;
typedef struct Node
{
    ElemType elem;
    struct Node *next;
}LinkNode,*Linklist;

 第一次读这段代码的时候有很多东西是不理解的,把这个结构体写的复杂一点,直观一点可以这样表示

typedef int ElemType;
struct Node
{
    ElemType elem;
    struct Node *next;
};

typedef struct Node LinkNode;
typedef struct Node* Linklist;

       首先在这个结构体中有一个指向自身结构体的指针初次看时可能会感到纳闷。到底是结构体先被定义还是指针先被定义?这结构体都没定义完整怎么在里面定义指针?内存分配顺序到底是怎样的?我最开始就陷入了这种类似先有鸡还是先有蛋的循环。其实结构体分配内存和指针分配内存本身就是两个不同的地方,互不干扰。即使不把结构体写完整 struct Node,struct Node *next也是完全可以的。其次就说到用typedef取别名。前者你可以直接认为是我们嫌struct Node写的太麻烦重新定义一个。可为什么还要定义一个指向结构体的指针呢。这样想我们得到了很多的结点,可怎么样把这些结点都串起来形成一个链表呢。这时候就需要指针。只需要提供一个头指针就可以把说有的结表串起来,头指针指向一个结点,这个结点的指针域指向下一个,在一直指向下去到最后为空就形成了一个链表。所以取名大家用的是list而不再是Node。其实严格来说一个头指针就是一个链表,所以你可以看到很多习题的传参都是(Linklist &head)。

        凡是涉及到指针全部都要给他初始化分配空间     

       所以一定记住以后一旦你定义了一个指针 Linklist newNode Linklist p全部要给newNode p分配空间用C语言的方式就是            p=(Linklist)malloc(LinkNode),C++为 p=new LinkNode;最后如果有需要回收(删除)结点 C free(p) C++ delete []p;

Q:怎么没见过程序中对结构体中的*next分配空间啊 A:结点的建立中就完成了指针的创建 p->next的赋值中对指向进行了交代

在链表中我们经常会构造一个辅助指针p=l->next 为什么要这样呢 ·简单来说一个结点就只有一个你对其的操作有时一下就改变了它接下来的指向等等 此时很多时候在想删除插入就很麻烦了因为next域改变后再想删除就不是同一个东西了 所以我们相当于需要一个备份,另外辅助指针也能方便我们的搜寻 遍历 和查找  下面这几段代码在链表中重复度比较高 希望不知道怎么写代码的同学先单独好好感受体会一下

//设置辅助指针p用来遍历 
Linklist p=l->next;
     int j=1;
     while(p&&j<i)
     {
         p=p->next;
         j++;
     }
//用先保留p->next的备份 直接free(p->next)是不行的 大家可以自己体会下
    Linklist q=p->next;
    p->next=q->next;
    *e=q->elem;
    free(q);
//要对p结点进行操作同时还要将p往后移动需要设置一个备份q
while(p)
    {
        Linklist q=p->next;
        p->next=l->next;
        l->next=p;
        p=q;
    }

二 基础知识讲解2(强烈建议阅读)

在整个链表的实现过程中还有一个特别隐蔽的地方:二级指针的运用 可以说很多教材和代码对这个问题都讲的不太透彻,作为一个力求精简并深刻理解每行代码有点洁癖的我在这里想了好久好久。。。  

  程杰 大话数据结构单链表创建 void CreateListHead(Linklist *L,int n) { .......  }

  严奶奶 数据结构(C语言版)      void CreaList_L(Linklist &L,int n) {......}

指针和C++中的引用是一个性质这里不详谈 注意到Linklist 好像本身就是一个指针了啊 在来个*不就是指向指针的指针么 这么复杂的么?好像他们很多别的函数也用到了Linklist *L啊 完了这个函数又没用* Linklist L 就解决了 到底怎么回事啊??

首先更正一点严奶奶的教材中并没有说这个是否为引用 原书在P8对&进行了解释(说是引用参数 我们老师说这个符号只表示这个值发生了变化不代表引用 具体怎样大家见仁见智 我个人认为教材还是比较严谨的吧)

       那么到底这个二级指针什么回事呢什么时候要用的 ?我查找了很多有的地方还是没有理解 觉得有时候用二级是多余的 最后 实践出真知  敲就完事了 下面这段代码是测验过得最简便的一种哪里该用二级指针都非常清楚

#include <stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct Node
{
    ElemType date;
    struct Node *next;
}*Linklist;
void Initiate(Linklist *l)
{
    *l=(Linklist)malloc(sizeof(Node));
    (*l)->next=NULL;
    (*l)->date=NULL;
}
void Append(Linklist l,int n)
{
    int i;
    for(i=n;i>0;--i)
    {
       Linklist p=(Linklist)malloc(sizeof(Node));
       scanf("%d",&p->date);
       p->next=l->next;
       l->next=p;
    }
}
int Insert(Linklist l,int i,ElemType x)
{
    Linklist p=l;
    int j=0;
    while(p&&j<i-1)
    {
        p=p->next;
        j++;
    }
    if(!p||j>i-1) return -1;
    Linklist pnew=(Linklist)malloc(sizeof(Node));
    pnew->date=x;
    pnew->next=p->next;
    p->next=pnew;
    return 1;
}
int Delete(Linklist l,int i,ElemType *e)
{
    Linklist p=l;
    int j=0;
    while(p->next&&j<i-1)
    {
        p=p->next;
        j++;
    }
    if(!(p->next)||j>i-1)  return -1;
    Linklist q=p->next;
    p->next=q->next;
   *e=q->date;
    free(q);
}
void Print(Linklist l)
{
    Linklist p=l->next;
    if(p==NULL)  printf("empty link\n");
else{
        while(p)
    {
        printf("%d->",p->date);
        p=p->next;
    }
    printf("\n");
    }
}
void Destroy(Linklist *l)
{
    Linklist p,q;
    p=(*l)->next;
    while(p)
    {
        q=p->next;
        free(p);
        p=q;
    }
    free(*l);
}
int main()
{
  Linklist l;
  Initiate(&l);
  Append(l,3);
  Print(l);
  Insert(l,4,47);
  Print(l);
  int x=31;
  Delete(l,1,&x);
  printf("%d\n",x);
  Print(l);
  Destroy(&l);
  return 0;
}

    其实只有链表的初始化创建和销毁才需要用到二级指针具体讲解我自认为没下文讲的清楚和明白

http://www.cnblogs.com/tianzeng/p/9689516.html     //一级指针与二级指针在动态链表中的应用   确保看完很重要

在我复习的过程中翻看了大一上C语言搭建链表中的例子也再次辅佐很印证了我对这方面的理解和思考 只能深深的感觉到教材真TM是个好东西 话不多说 直接上教材上的代码   教材名字:C语言程序设计教程(第二版) 清华大学出版社

#include<bits/stdc++.h>
typedef struct Node
{
    int score;
    struct Node *next;
}Node;
Node* Create()
{
    Node *head ,*tail,*pnew;
    int score;
    head=(Node *)malloc(sizeof(Node));
    if(head==NULL)
    {
        printf("no  enough memory!\n");
        return(NULL);
    }
    head->next=NULL;
    tail=head;
    printf("input the date of students:\n");
    while(1)
    {
        scanf("
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值