链表1(数据结构与算法基础)

目录

单链表

单链表(带头结点)

定义结点

单链表的初始化

判断链表是否为空

单链表的销毁

单链表的清空

单链表的表长

取单链表中第i个元素

单链表的查找

单链表的插入

删除单链表中第i个值

建立单链表

头插法

尾插法


单链表

线性表的链式存储结构

  • 链式存储结构

    • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻

    • 结点中有 数据域(存放数据)和 指针域(存放地址),n个结点由指针链组成链表。

  • 线性表的链式表示又称为 非顺序映像链式映像

链表特点:

  • 用一组 物理位置任意的存储单元 来存放线性表的数据元素。

  • 这组存储单元既可以是 连续 的,也可以是 不连续 的,甚至是零散分布在内存中的任意位置上的。

  • 链表中元素的 逻辑次序 和 物理次序 不一定相同

  • 访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。因此,这种存取元素的方法被称为 顺序存取法。(顺序表是随机存取)

链表有单链表,双链表,循环链表(之后再细说):

  1. 单链表:结点只有一个指针域的链表,称为单链表。

  2. 双链表:结点有两个指针域的链表,称为双链表。

  3. 首尾相接的链表称为循环链表。

头指针、头结点、首元结点:

  1. 头指针:是指向链表中第一个结点的指针。

  2. 首元结点:是指链表中存储第一个数据元素a1的结点。

  3. 头结点:是在链表的首元结点之前附设的一个结点。(一般都带头结点,因为方便操作,之后细说)

所以链表可分为 带头结点的链表不带头结点 的链表。

头结点不计入表长!

头结点的好处:

  1. 便于 首元结点 的处理

    首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无须进行特殊处理。

  2. 便于 空表和非空表 的统一处理

    当没有头结点 且 为空表时,头指针指向NULL,有头结点时无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。

单链表(带头结点)

  • 单链表是由表头唯一确定,因此单链表通常用头指针的名字来命名,若头指针名为L,则称链表为表L。

定义结点

 typedef struct Lnode {      //声明结点的类型和指向结点的指针类型
     ElemType data;          //结点的数据域
     struct Lnode *next;     //结点的指针域
 }Lnode, *LinkList;          //LinkList为指向结构体Lnode的指针类型

定义链表L:LinkList L;

定义结点指针:LNode *p;(虽然LinkList p和LNode *p一样,但为了语义,还是分开定义)

单链表的初始化

  • 即构造一个空表

算法思路:

  1. 生成新结点作头结点,用头指针L指向头结点。

  2. 将头结点的指针域置空。

算法描述:

 Status InitList_L(LinkList &L) {
     L = new LNode;      //c语言描述为 L = (LinkList)malloc(sizeof(LNode));
     L->next = NULL;
     return 0;
 }

判断链表是否为空

空表:链表中无元素,头结点指针域指向NULL(头指针和头结点仍然在)

 int ListEmpty(LinkList L) {     //若L为空表,则返回1,否则返回0
     if(L->next)     //非空
         return 0;
     else
         return 1;
 }

单链表的销毁

算法思路:

  • 从头指针开始,依次释放所有结点。

算法描述:

 Status DestroyList_L(LinkList &L) {
     Lnode *p;               //LinkList p;
     while(L) {
         p = L;
         L = L->next;
         delete p;           //c语言描述:free(p);
     }
 }

单链表的清空

  • 链表仍然存在,但链表中无元素,成为空链表(头指针和头结点仍然存在)

算法思路:

  • 依次释放所有结点,并将头结点指针域设置为空。

算法描述:

 Status ClearList(LinkList &L) {
     LNode *p, *q;
     p = L->next;
     while(p) {
         q = p->next;
         delete p;
         p = q;
     }
     L->next = NULL;
     return OK;
 }

单链表的表长

算法思路:

  • 从首元结点开始,依次计数所有结点

算法描述:

 int ListLength_L(LinkList L) {
     LinkList p;
     p = L->next;                //p指向第一个结点
     i = 0;
     while(p) {                  //遍历单链表计数
        i++;
        p = p->next;
     }
     return i;
 }

取单链表中第i个元素

算法思路:

  • 从链表头指针出发,顺着链域next逐个结点往下搜索,直到搜索到第i个结点为止。(所以链表不是随机存取结构)

  1. 从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = L->next。

  2. j做计数器,累计当前扫描过的结点数,j初值为1。

  3. 当p指向扫描到的下一结点时,计数器j加1。

  4. 当j == i时,p所指的结点就是要找的第i个结点。

算法描述:

 Status GetElem_L(LinkList L, int i, ElemType &e) { //通过e返回那个内容
     p = L->next;j=1;        //初始化
     while(P && j<i) {       //向后扫描,直到p指向第i个元素或p为空
         p = p->next;++j;
     }
     if(!p || j > i) return ERROR;//第i个元素不存在
     e = P->data;                //取第i个元素
     return OK;
 }

单链表的查找

算法思路:

  1. 从第一个结点起,依次和e相比较。

  2. 如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”或地址。

  3. 如果遍历整个链表都没有找到其值和e相等的元素,则返回0或“NULL”。

算法描述:

  • 返回地址

     Lnode *LocateElem_L(LinkList L, Elemtype e) {
         //在线性表L中查找值为e的数据元素
         //找到,则返回L中其值为e的数据元素的地址,失败则返回NULL
         p = L->next;
         while(p && p->data != e)
             p = p->next;
         return p;
     }

  • 返回位置序号

    int LocateElem_L(LinkList L, Elemtype e) {
        p = L->next;j = 1;
        while(p && p->data != e) {
            p = p->next;j++;
        }
        if(p) return j;
        else return 0;		//查找失败
    }

单链表的插入

算法思路:

  1. 首先找到插入位置前一个结点的位置p。

  2. 生成一个数据域为e的新结点s。

  3. 插入新结点。

算法描述:

Status ListInsert_L(LinkList &L, int i, ElemType e) {
    p = L;j = 0;
    //寻找第i-1个结点,p指向i-1结点
    while(p && j<i-1) {
        p = p->next;
        ++j;
    }
    if(!p || j>i-1) return ERROR;	//i大于表长+1或者小于1,插入位置非法
    s = new LNode;					//生成新结点s,将结点s的数据域设置为e
    s->data = e;		
    s->next = p->next;				//将结点s插入L中
    p->next = s;
    return OK;
}

删除单链表中第i个值

算法思路:

  1. 首先找到删除位置前一个结点的位置p,保存要删除的a的值。(也可以不保存)

  2. 令p->next指向要删除位置的后一个结点。

  3. 删除

算法描述:

Status ListDelete_L(LinkList &L, int i, ElemType &e) {
    p = L; j = 0;
    while(p->next && j<i-1) {			//寻找第i个结点,并令p指向其前驱
        p=p->next; ++j;
    }
    if(!(p->next) && j>i-1) return ERROR;//删除位置不合理
    q = p->next;						//临时保存被删结点的地址以备释放
    p->next = q->next;					//改变删除结点的前驱指针域			
    e = q->data;						//保存删除结点的数据域,也可以不保存
    delete q;							//释放
    return OK;
}

建立单链表

头插法

  • 元素插入在链表头部,也叫前插法。

  • 时间复杂度 O(n).

算法描述:

//倒位序输入n个值,建立带表头结点的单链表L
void CreateList_H(LinkList &L, int n) {
    L = new LNode;			//c:(LinkList)malloc(sizeof(LinkList));
    L->next = NULL;			//线建立一个带头结点的单链表
    for(i=n;i>0;i++) {
        p = new LNode;		//生成新节点
        cin>>p->data;		//输入元素值 c:scanf(&p->data);
        p->next = L->next;	//插入到表头
        L->next = p;
    }
}

尾插法

  • 元素插入在链表尾部,也叫后插法。

  • 时间复杂度 O(n)。

算法描述:

//正位序输入n个值,建立带表头结点的单链表L
void CreateList_R(LinkList &L, int n) {
    L = new LNode;
    L->next = NULL;
    r = L;		//尾指针r指向头结点
    for(i=0;i<n;++i) {
        p = new LNode;
        cin>>p->data;		//生成新节点并输入元素值
        p->next = NULL;
        r->next = p;	//插入到表尾
        r = p;	//r指向新的尾结点
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值