单链表(1)

• 头指针
– 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
– 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
– 无论链表是否为空,头指针均不为空。
– 头指针是链表的必要元素。
• 头结点
– 头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)。
– 有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了。
– 头结点不一定是链表的必须要素。
-单链表常用到的核心算法思想就是工作指针后移,也是许多算法的核心思想。

GetElem
获得链表第i个数据的算法思路:
1.声明一个结点p指向链表第一个结点,初始化j从1开始;
2.当j<i时,就遍历链表,让p的指针向后移动,不断指向一下结点,j+1;
3.若到链表末尾p为空,则说明第i个元素不存在;
4.否则查找成功,返回结点p的数据。

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */

Status GetElem( LinkList L, int i, ElemType *e )
{
    int j;
    LinkList p;

    p = L->next;//初始化,p指向第一个结点,j为计数器
    j = 1;

    while( p && j<i )//顺指针向后查找,直到p指向第i个元素或p为空
    {
        p = p->next;//向后查找
        ++j;//遍历计数器
    }

    if( !p || j>i )//第i个元素不存在
    {
        return ERROR;
    }

    *e = p->data;//取第i个元素

    return OK;
}

插入ListIsert()
1.单链表第i个数据插入结点的算法思路:
2.声明一结点p指向链表头结点,初始化j从1开始;
3.当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
4.若到链表末尾p为空,则说明第i个元素不存在;
5.否则查找成功,在系统中生成一个空结点s;
6.将数据元素e赋值给s->data;
7.单链表的插入刚才两个标准语句;
8.返回成功。

在这里插入图片描述
我们思考后发觉根本用不着惊动其他结点,只需要让s->next和p->next的指针做一点改变。
s->next = p->next;
p->next = s;

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */

Status ListInsert(LinkList *L, int i, ElemType e)
{
    int j;
    LinkList p, s;

    p = *L;//初始化变量
    j = 1;//计数器

    while( p && j<i )   // 用于寻找第i-1个结点,通过遍历单链表来进行寻找,仍然是使用指针后移的思想
    {
        p = p->next;
        j++;
    }

    if( !p || j>i )//到了单链表的末尾或者找不到位置
    {
        return ERROR;
    }

    s = (LinkList)malloc(sizeof(Node));//生成新的节点
    s->data = e;//赋值操作


    s->next = p->next;//插入的经典操作
    p->next = s;

    return OK;
}

单链表的删除操作
在这里插入图片描述
假设元素a2的结点为q,要实现结点q删除单链表的操作,其实就是将它的前继结点的指针绕过指向后继结点即可。
那我们所要做的,实际上就是一步:
可以这样:p->next = p->next->next;
也可以是:q=p->next; p->next=q->next;

单链表第i个数据删除结点的算法思路:
-声明结点p指向链表第一个结点,初始化j=1;
-当j<i时,就遍历链表,让P的指针向后移动,不断指向下一个结点,j累加1;
-若到链表末尾p为空,则说明第i个元素不存在;
-否则查找成功,将欲删除结点p->next赋值给q;
-单链表的删除标准语句p->next = q->next;
-将q结点中的数据赋值给e,作为返回;
-释放q结点。
-返回成功

/* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度-1 */

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

    p = *L;//指向第一个结点
    j = 1;//计数器

    while( p->next && j<i )//遍历这个单链表寻找第i-1个结点
    {
        p = p->next;
        ++j;
    }

    if( !(p->next) || j>i )//找不到i元素的两种情况
    {
        return ERROR;
    }

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

    *e = q->data;//赋值给e
    free(q);//释放空间

    return OK;
}

至于

  if( !(p->next) || j>i )//找不到i元素的两种情况
    {
        return ERROR;
    }

下面把这两种情况列出来:
1.while循环的终止条件为:p为NULL或者j >= i。
2.链表空时p直接是NULL,返回ERROR。
3.链表非空,如果i <= 0,则 j > i,如果i > lenOf(LinkList),则p最终为NULL,这两种情况都应该返回ERROR。
4.if (!p || j >i)就是表示了第i个元素不存在的两种情况。
5.这种代码的确会让人困惑,且该函数不够健壮,应该在函数的开头处检查输入参数,比如L != NULL和i >= 1。

**我们发现无论是单链表插入还是删除算法,它们其实都是由两个部分组成:第一部分就是遍历查找第i个元素,第二部分就是实现插入和删除元素。**最终算法的时间效率为O(n)

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值