C - 数据结构 - 线性表 - 链式实现

上一章节我们研究和学习了线性表的顺序存储结构
特点是逻辑关系上相邻的两个元素在物理位置上也相邻
因此可以随机存取表中任一元素,它的存储位置可用一个简单、直观的公式来表示。

弱点: 在作插入或删除操作时,需移动大量元素。
SO: 我们学习另一种表示方法 链式存储结构。

  1. 链式存储结构不要求逻辑上相邻的元素在物理位置上也相邻
  2. 它没有顺序存储结构的弱点
  3. 但同时也失去了顺序表可随机存取的优点

单链表

对链表的访问要通过顺序读取从头部(头指针)开始。
包含两个域,一个信息域和一个指针域(指针或链)。这个链指向列表中的下一个结点,而最后一个结点则指向一个空(NULL)。

这里写图片描述

// 单链表可由头指针唯一确定,在C语言中可用“结构指针”来描述
// 假设L是LinkList型的变量,则L为单链表的头指针,它指向表中第一个结点
// 头结点的数据域可以不存储任何信息,也可存储如线性表的长度等类的附加信息
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode, *LinkList;

插入

假设我们要在线性表的两个数据元素a和b之间插入一个数据元素x,已知p为其单链表存储结构中指向结点a的指针

  • 生成一个数据域为x的结点
  • 修改结点a中的指针域,令其指向结点x
  • x中的指针域应指向结点b

这里写图片描述

代码片段

Status ListInster(LinkList L , int i, ElemType e){
    int j = 0;
    LinkList p,s;
    p = L;

    while (p && j < i-1) { // 寻找第i-1个结点
        p = p->next; // p指向其前驱(即i-1(a)结点)
        j++; // 计数器
    }

    if(!p || j>i-1)
        return ERROR; // i小于1或者大于表长加1
    s = (LinkList)malloc(sizeof(struct LNode)); // 生成新结点
    s->data=e; // 插入数据
    s->next=p->next;// s为指向结点i的指针
    p->next = s;// p为指向s结点的指针
    return OK;
}

删除

删除元素b时

  • 修改结点a中的指针域即可
  • 执行free(b),回收后的空间可以再次生成结点时用

这里写图片描述

假设p为指向结点a的指针,则修改指针的语句为

p->next = p->next->next;

Status ListDelete_L(LinkList L, int i, ElemType *e){
    int j = 0;
    LinkList p, q;
    p = L;

    while (p->next && j < i-1) { // 找到第i-1个结点
        p = p->next; // p指向其前驱(i-1(a)结点)
        j++;
    }

    if(!p->next||j>i-1)
        return ERROR; // 删除位置不合理

    // 删除并释放结点
    q = p->next; // q为指向p结点的指针(即b结点)
    p->next=q->next; // p为指向q结点的指针
    *e = q->data;
    free(q);// 释放q(即b结点)
    return OK;
}

实例代码

// linklist.h
//
//  linked.h
//  LinkList
//
//  Created by liuxinming on 16/9/29.
//  Copyright © 2016年 liuxinming. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include "string.h"

#ifndef linked_h
#define linked_h



// 函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK  1
#define ERROR 0
#define INFEASIBLE -1
#define OVERLOW -2

// Status 是函数的类型, 其值是函数结果状态代码
typedef int Status;
typedef int ElemType;

// 线性表的单链表存储结构
typedef struct LNode
{
    ElemType data;
    struct LNode * next;
}LNode,* LinkList;

// 操作结果:构造一个空的线性表
Status InitList(LinkList *L){
    // 产生头结点,并使L指向此结点
    *L = (LinkList)malloc(sizeof(struct LNode));
    if(!*L)
        exit(OVERLOW); // 存储分配失败
    (*L)->next=NULL; // 指针域为空
    return OK;
}

// 在带头结点的单链线性表L中第i个位置之前插入元素e
Status ListInster(LinkList L , int i, ElemType e){
    int j = 0;
    LinkList p,s;
    p = L;

    while (p && j < i-1) { // 寻找第i-1个结点
        p = p->next; // p指向其后续(即i-1结点)
        j++; // 计数器
    }

    if(!p || j>i-1)
        return ERROR; // i小于1或者大于表长加1
    s = (LinkList)malloc(sizeof(struct LNode)); // 生成新结点
    s->data=e; // 插入数据
    s->next=p->next;// s为指向结点i的指针
    p->next = s;// p为指向s结点的指针
    return OK;
}

// L为带头结点的单链表的头指针。当第i个元素存在时,其赋值给e并返回OK,否则返回ERROR
Status GetElem(LinkList L, int i, ElemType *e){
    int j = 1;
    LinkList p = L->next; // p指向第一个结点
    while (p && j<i) {
        p=p->next;
        j++;
    }

    if (!p||j>i) // 第i个元素不存在
        return ERROR;
    *e = p->data; // 取第i个元素
    return OK;
}

// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
Status ListDelete_L(LinkList L, int i, ElemType *e){
    int j = 0;
    LinkList p, q;
    p = L;

    while (p->next && j < i-1) { // 找到第i-1个结点
        p = p->next; // p指向其后续(i-1结点)
        j++;
    }

    if(!p->next||j>i-1)
        return ERROR;// 删除位置不合理

    // 删除并释放结点
    q = p->next;
    p->next=q->next;
    *e = q->data;
    free(q);
    return OK;
}

// 已知单链线性表La 和 Lb 的元素按值非递减排列
void MergeList(LinkList La, LinkList *Lb, LinkList *Lc){
    // 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列

    // 定义三个指针,分布指向La,Lb中当前待比较插入的结点
    // pc指向Lc表中档最后一个结点
    LinkList pa,pb,pc;

    pa = La->next; pb = (*Lb)->next;
    *Lc = pc = La;//用La的头结点作为Lc的头结点

    while (pa && pb) {
        // pa,pb皆非空,当其中一个为空时,说明有一个表的元素已归并完,则只需将另一个表的剩余段链接在pc所指的结点之后即可
        if(pa->data <= pb->data)
        {
            pc->next = pa; // 将pa所指结点链接到pc所指结点之后
            pc=pa;
            pa=pa->next;

        }
        else
        {
            pc->next = pb; // 将pb所指结点链接到pc所指结点之后
            pc=pb;
            pb=pb->next;
        }
    }

    // 插入剩余段
    pc->next = pa ? pa : pb;
    // 释放Lb的头结点
    free(*Lb);
    Lb = NULL;

}// MergeList

// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L
void CreateList(LinkList *L, int n){
    int i;
    LinkList p;
    *L = (LinkList)malloc(sizeof(struct LNode));
    (*L)->next = NULL ; // 先建立一个带头结点的单链表
    printf("请输入%d个数据\n",n);
    for (i=n;i>0;--i){
        p = (LinkList)malloc(sizeof(struct LNode)); // 生成新结点
        scanf("%d",&p->data); // 插入元素值
        p->next = (*L)->next; // 插入到表头
        (*L)->next = p;
    }
}

// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表
void CreateList_2(LinkList *L, int n){
    int i;
    LinkList p, q;
    *L = (LinkList)malloc(sizeof(struct LNode));
    (*L)->next = NULL;
    q = *L;

    printf("请输入%d个数据\n",n);
    for (i = 1;i<=n; i++){
        p = (LinkList)malloc(sizeof(struct LNode)); // 生成新结点
        scanf("%d",&p->data);
        q->next = p;
        q = q->next;
        p->next = NULL;
    }


}

// 初始条件:线性表L已存在
// 操作结果:将L重置为空表
Status ClearList(LinkList L){ // 不改变L
    LinkList p, q;
    p = L->next; // p指向第一个结点
    while (p) {
        q = p->next;
        free(q);
        p = q;
    }
    L->next=NULL; // 头结点指针域为空
    return OK;
}

// 初始条件:线性表L已存在
// 操作结果:销毁线性表L
Status DestroyList(LinkList *L)
{
    LinkList q;
    if(*L == NULL) return ERROR;
    while (*L) {
        q = (*L)->next;
        free(*L);
        *L=q;
    }
    return OK;
}

// 初始条件:线性表L已存在
// 操作结果:若L为空表,则返回TRUE,否则返回FALSE
Status ListEmpty(LinkList L){
    if (L->next) // 非空
        return FALSE;
    else
        return TRUE;
}

// 初始条件:线性表L已存在
// 操作结果:返回L中的数据元素个数
Status ListLenght(LinkList L)
{
    int i = 0;
    LinkList p = L->next; // p指向第一个结点
    while (p) { // 没有到表尾
        i++;
        p = p->next;
    }
    return i;
}

// 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则0)
// 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。若这样的数据元素不存在,则返回值0
int LocateElem(LinkList L, ElemType e, Status (*compare)(ElemType,ElemType))
{
    int i = 0;
    LinkList p = L->next;
    while (p) {
        i++;
        if(compare(p->data,e)) // 找到这样的元素
            return  i;
        p = p->next;
    }

    return 0;
}

// 初始条件:线性表L已存在
// 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回OK,否则操作失败,pre_e无定义,返回INFEASIBLE
Status PriorElem(LinkList L,ElemType cur_e, ElemType *pre_e){
    LinkList q, p = L->next;
    while (p->next) { // p指向结点有后续
        q = p->next; // q为p的后续
        if (q->data == cur_e)
        {
           *pre_e = p->data;
            return OK;
        }
        p = q; // p向后移

    }

    return INFEASIBLE;
}

// 初始条件:线性表L已存在
// 操作结果:若cur_e是L的数据元素,切不是最后一个,则用next_e返回它的后续,返回OK,否则操作失败,next_e无定义,返回INFEASIBLE
Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e){
    LinkList p = L->next;
    while (p->next) { // p所指结点有后续
        if(p->data == cur_e)
        {
            *next_e = p->next->data;
            return OK;
        }
        p = p->next;
    }
    return INFEASIBLE;
}

// 初始条件:线性表L已存在
// 操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
Status ListTraverse(LinkList L, void(*vi)(ElemType))
{
    LinkList p = L->next;
    while (p) {
        vi(p->data);
        p=p->next;
    }
    printf("\n");
    return OK;
}
#endif /* linked_h */

DEMO测试

//
//  main.c
//  LinkList
//
//  Created by liuxinming on 16/9/25.
//  Copyright © 2016年 liuxinming. All rights reserved.
//

#include <stdio.h>
#include "linklist.h"

// ListTraverse()调用的函数(类型要一致)
void visit(ElemType c)
{
    printf("%d ",c);
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Testing start!\n");
    LinkList L,La,Lb,Lc;
    int n = 5;

    printf("按非递减顺序:\n");
    CreateList_2(&La, n);
    // 输出链表La的内容
    printf("La=");
    ListTraverse(La,visit);

    printf("按非递增顺序: ");
    CreateList(&Lb, n);
    printf("Lb=");
    ListTraverse(Lb, visit);

    // 按非递减顺序归并La和Lb,得到新表Lc
    MergeList(La,&Lb,&Lc);
    printf("Lc="); /* 输出链表Lc的内容 */
    ListTraverse(Lc,visit);
    return 0;
}

循环链表

循环链表是另一种形式的链表存储结构

  • 表中最后一个节点的指针指向头节点
  • 整个链表形成一个还
  • 差别仅在于算法中的循环条件不是p或p->next是否为空,而是他们是否等于头指针。
  • 若在循环链表中设立尾指针而不设头指针,可使某些操作简化

双向链表

双向链表的节点中有两个指针域
一个指向直接后续
一个指向直接前趋

// 线性表的双向链表存储结构
typedef struct DuLNode(){
    ElemType  data;
    struct DuLNode *prior;
    struct DuLNode *next;
}DuLNode, *DuLinkList;

插入

借张图说明下
这里写图片描述

  1. 为链表结点x动态分配内存
  2. 为新生成结点x 赋值
  3. 使x前驱指向p的前驱
  4. 使x后驱指向p
  5. 修改原p之前的结点(即i-1)的后驱为x
  6. 修改原p的前驱为x
DuLinkList GetElemP(DuLinkList L,int i)
{
    DuLinkList p;
    //初始化,p指向第一个结点,j为计数器
    p = p->next;
    int j = 1;
    while (p != L && j < i) {
        // 顺指针向后查找,直到p指向第i个元素或p为空
        p = p->next;
        ++j;
    }

    if (p == L && j < 1)
        return NULL; // 第i个元素不存在
    else
        return p;
}
// 在带头结点的双循环线性表L中第i个位置之前插入元素e
// i的合法值为1<=i<=表长+1
Status ListInsertBefore(DuLinkList L, int i, ElemType e){
    DuLinkList p, x;
    if (i<1)
        return ERROR;
    if (!(p = GetElemP(L, i))) // 在L中确认第i个元素的位置指针p
        return ERROR;//p = NULL, 即第i个元素不存在
    x = (DuLinkList)malloc(sizeof(DuLNode));
    if (!x)
        return OVERLOW;

    x->data = e; // 将元素e的值赋给新生成结点x
    x->prior = p->prior; // 使s的前驱指向p的前驱(即i-1)
    x->next = p; // 使x的后驱指向i的结点

    p->prior->next = x; // 原p前驱结点(即i-1)的后驱指向x
    p->prior = x; // 原p前驱指向x
    return OK;
}

删除

// 删除带头结点的双链循环线性表L的第i个元素
Status ListDelete(DuLinkList L, int i, ElemType *e){
    DuLinkList p;
    p = GetElemP(L, i); // 在L中确认第i个元素的位置指针p
    if (!p)
        return ERROR;//p = NULL, 即第i个元素不存在
    *e = p->data;

    p->prior->next = p->next;
    p->next->prior = p->prior;

    free(p);
    return OK;
}// ListDelete
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值