考研数据结构笔记——2.线性表的链式表示(单链表)

线性表的链式表示

单链表的定义

线性表的链式存储称为单链表;
每个链表节点,除存放元素自身的信息外,还需要存放一个指向其后继结点的指针;
data为数据域,存放数据;next为指针域,存放指针

单链表中结点类型的描述

typedef struct LNode{
    ElemType data;      //数据域
    struct LNode *next;     //指针域
}LNode,*Linklist;
  • 顺序表需要大量连续存储空间,单链表克服了这一缺点,即存储空间不一定要连续
  • 但是单链表相对于顺序表多了指针域,因此有浪费空间的缺点
  • 单链表的元素是离散地分布在存储空间的,所以无法实现随机存取,寻找特定的结点必须从表头开始遍历,依次查找

头指针与头节点

通常用头指针来标识一个单链表,如单链表L;特别地,头指针为NULL时表示一个空表

为了操作上的方便,在单链表主体的第一个结点前加一个结点,称为头结点;头结点的数据域可以记录表长等信息,也可以不记录任何信息

头指针和头节点的区别

  • 头指针始终指向链表的第一个结点,无论头结点是否存在
  • 头结点是带头结点链表的第一个结点

引入头结点带来的优点

  • 由于开始结点的位置被存放在头结点的指针域中,所以链表第一个位置的操作和其他位置相同(链表的第一个有效位置实际上是第二个结点)
  • 引入头结点后,无论链表是否为空,头指针都指向头结点的非空指针;否则空链表与非空链表的处理不同

单链表上基本操作的实现

单链表上的主要操作主要有建立(头插法、尾插法)、查找(按序号、按值)、插入结点、删除结点、求表长

采用头插法建立单链表

LinkList List_HeadInsert(LinkList &L){
    LNode *s;int x;
    L = (Linklist)malloc(sizeof(LinkList));  //创建头结点
    L->next = NULL;  //头结点指针为空,初始化为空链表
    scanf("d",&x);  输入(第一个)结点的值
    while(x != 9999){   //结束条件(如输入9999)
        s = (LinkList)malloc(sizeof(LinkList)); //创建一个新结点
        s->data = x;
        s->next = L->next;
        L->next = s;        //插入过程
        scanf("d",&x);
    }
    return L;
}
  • 头插法建立单链表时,读入数据的顺序和链表中元素的顺序是相反的
  • 每个结点插入的时间为O(1)
  • 设单链表长为n,则头插法生成单链表的总时间复杂度为O(n)

采用尾插法建立单链表

头插法虽然简单,但是输入顺序和生成链表中结点的顺序相反;尾插法将新结点插入当前链表的表尾,为此必须增加一个尾指针,使其始终指向当前链表的尾结点;时间复杂度与头插法相同

Linklist List_TailInsert(Linklist &L){
    int x;  //设元素类型为整型
    L = (LinkList)malloc(sizeof(LNode));
    LNode *s,*r = L;    //r为表尾指针
    scanf("%d",x);  //输入结点的data域值
    while(x != 9999){   //结束条件(如输入9999)
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;    //r指向下一个表尾结点
        r = s;  //r再次成为表尾指针
        scanf("%d",x);
    }
    r->next = NULL; //尾结点指针为空
    return L;
}

按序号查找结点值

LNode *GetElem(LinkList L,int i){
    int j = 1;  //计数器,初始化为1
    LNode *p = L->next; //将头结点的指针赋值给p
    if(i == 0)
        return L;
    if(i <= 0)
        return NULL;
    while(p&&j < i){    //从第1个结点开始,查找到第i个结点
        p = p->next;
        j++;
    }
    return p;   //返回第i个结点的指针
}
  • 按序号查找结点的时间复杂度为O(n)

按值查找表结点

从单链表第一个结点开始,依次比较各个结点中数据域的值;如果链表中某结点数据域的值等于给定值e,则返回该结点的指针;若没有这样的结点,则返回NULL

LNode *LocateElem(LinkList L,ElemType e){
    LNode *p = L->next;
    while(p != NULL&&p->data != e)  //p后面无结点或该结点的data域等于被查找对象时跳出循环
        p = p->next;
    return p;   //找到后返回p,否则返回NULL
}

插入结点操作

插入节点操作将值为x的新结点插入到单链表的第i个位置上;先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新结点

  1. 调用序号查找算法GetElem(L,i-1),查找第i-1个结点

  2. 假设返回的第i-1个结点为 *p,令新结点 *s的指针域指向 *p的后继结点

  3. 令 *p的指针域指向新插入的结点 *s

1 p = GetElem(L,i-1);
2 s->next = p->next;
3 p->next = s;
  • 2句和3句的顺序不能颠倒,否则s将指向自己,显然是错误的
  • 算法的主要时间开销在查找第i-1个元素,算法时间复杂度为O(n),插入操作的时间复杂度仅为O(1)

对某一结点实现前插操作

以上面的算法为例,前插操作可以转化为后插操作,但需要调用GetElem函数找到第i-1个结点,而查找第i-1个元素的时间复杂度为O(n)

对于已知结点,可以采取另外一种方式进行前插转换为后插的操作;设待插入结点为 *s,将其插入到 *p的前面,我们仍然可以将其插入到 *p的后面,将p->data与s->data交换,这样既满足了逻辑关系,又能使算法时间复杂度降为O(1)

s->next = p->next;
p->next = s;
temp = p->data;
p->data = s->data;
s->data = temp;

删除结点操作

删除结点操作是将单链表的第i个节点删除;假设结点 *p为被删除结点 *q的前驱节点,仅需修改 *p的指针域,将 *p的指针域next指向 *q的下一结点

p = GetElem(L,i-1);
q = p->next;
p->next = q->next;
free(q);
  • 和插入算法一样,该算法的时间主要耗费在查找上,因此算法的时间复杂度为O(n)

删除结点*p

删除结点 *p,通常的做法是用GetElem找到 *p的前驱节点,然后再执行删除操作,时间复杂度为O(n)

实际上,也可以通过删除 *p的后继结点的方法实现;将其后继结点的值赋给它,并且删除后继结点,可以使算法的时间复杂度降为O(1)

q = p->next;
p->data = q->next->data;
p->next = q->next;
free(q);

求表长操作

求表长操作就是计算单链表中数据结点(不带头结点)的个数;遍历+计数器,算法的时间复杂度为O(n);不带头结点和带头结点的单链表处理不同;对不带头结点的单链表,当表为空时要单独处理

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容