单链表的基本操作

单链表的初始化、销毁、清空、求表长、取值、查找、插入、删除。

首元结点:是指链表中存储第1个数据元素a1的结点 


 代码采用的是C语言和一点C++语法,只展示了功能代码(有部分注释),main()就不再展示。

#include <stdio.h>
#include <stdlib.h>    // 给表分配内存,所需该头文件

#define TRUE 1
#define FALSE 0
#define OK 1         
#define ERROR 0      // 错误
#define INFEASIBLE -1  // 不可行
#define OVERFLOW -2  // 溢出

typedef int Status;  // Status是函数的类型,其值是函数返回结果的类型,此处定义为int型(后面用来返回地址下标)
typedef char ElemType;  // 定义线性表中存放数据的类型为 char型
// 单链表的存储结构
typedef struct LNode    // 定义结构体类型,命名为LNode
{  
    ElemType data;    // 定义char型的数据域
    struct LNode *next;    // 定义结点型的指针域,即指针域将存放下一个结点的地址
}LNode,*LinkList;    // LNode的类型是指针类型,*LinkList指向结构体(为了提高程序的可读性)

定义链表L:LinkList L;

定义结点指针P:LNode *P;  或  LinkList P;

(两种创建结点P的方式,是为了好辨别创建的是个结点还是链表,其本质是相同的)

// 单链表的初始化(构造空表)
status InitList_L (LinkList &L)    // LinkList就是单链表结构体类型,LinkList &L即声明L的类型
{    
    L = new LNode;  // C++方法,new LNode即生成新结点作为头结点,L = new LNode即用头指针L指向头结点
    // L = (LinkList)malloc(sizeof(LNode));    C方法
    L->next = NULL;    // 将头结点的指针域置空
    return OK;    // 创建成功,返回真值
}

单链表是由头指针唯一确定,因此单链表可以用头指针的名字来命名 

// 判断单链表是否为空    空表:无元素,头指针和头结点仍存在
int isEmpty(LinkList L)    // 同样声明L的类型
{    
    if(L->next)    // L->next为空,返回0,非空,返回1
        return 0;    // 0表示空表
    else
        return 1;    // 1表示非空表
}
// 销毁单链表(从头结点开始,一次释放所有结点)
Status DestroyList(LinkList &L)
{    
    LNode *p;   // 创建一个用于记录的结点
    while(L)    // 结束条件 L==NULL 
    {       
        p = L;   // p指向待删除的结点,指谁删谁    
        L = L->next;  // 待删除的结点已被记录,即可指向链表中的下一个结点
        delete p; // 删除结点
    }    
    return OK;
}
// 删除指针,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身
// 清空单链表(无元素,头指针头结点仍存在)
Status ClearList(LinkList &L)
{    
    LNode *p, *q;  // 创建两个工具结点,p相当于销毁算法中的L,q记录待删除的结点
    p = L->next;  // p指向首元结点,从首元结点依次向尾部删除   
    while(p)    // 循环条件:p指针没有移到表尾   
    {       
        q = p->next; // 循环部分相当于销毁算法       
        delete p;        
        p = q;    
    }    
    L->next = NULL;    // 头结点指针域置空
    return OK;
}

销毁与清空比较: 

        销毁的L一直跟着p移动,边移动边释放内存(可理解为过河拆桥,最终,L相当于指向了最后一个元素的指针域,也为空)

        清空的L不动,看着p和q过河拆桥,最终,p和q都为空,不存在,L也直接置空

// 返回单链表的表长(从首元结点开始,依次计数所有结点)
int ListLength_L(LinkList L)
{
    LNode *p;
    p = L->next;  // p指向第一个结点
    int i = 0;
    while(p)    // 只要p不为空,就累加结点数量
    {
        i++;
        p = p->next;  // 依次往后移
    }
    return i;
}
// 单链表取值————取单链表中第i个元素的内容
Status GetElem(LinkList L, int i, ElemType &e)  // i要找的位置,&e即通过e返回
{
    p = L->next;    // 初始化,p指向首元结点
    int j = 1;  // 计数器
    while(p && j<i)  // 当j==i时,p所指向的节点既是第i个结点
    {
        p = p->next;    // p指向下一个结点
        ++j;
    }
    if(!p && j>i) return ERROR; //第i个元素不存在,!p即超过了链表,j>i即计数的值超过了i值
    e = p->data;    // 取值
    return OK;
}
// 单链表按值查找————根据指定数据获取该数据所在的位置(地址)
LNode *LocateElem(LinkList L, ElemType e)   // 在带头结点的单链表L中查找值为e的元素
{
    p = L->next;    // 初始化,p指向首元结点
    while(p && p->data != e)    // 结束条件:链表扫描完了或找到了指定数据
        p = p->next;    // 没找到继续往后扫描
    return p;   // 返回p。若查找成功,p此时即为结点的地址值,若查找失败,p的值即为NULL
}
// 按值查找————根据指定数据获取该数据位置序号
int LocateElem(LinkList L, ElemType e) 
{
    p = L->next;
    int j = 1;
    while(p && p->data != e)    // ①找到p!=NULL,有j值;②未找到,p==NULL,无j值
    {
        p = p->next;
        j++;
    }
    if(p) return j; // 如果没有if(p) j的情况:①返回正确的值;②返回链表长度
    else return 0;
}
// 插入————在第i个结点前插入值为e的新结点
Status ListInsert(LinkList &L, int i, ElemType e)   // 在带头结点的单链表L中第i个位置插入值为e的新结点
{
    p = L;
    int j = 0;
    while(p && j<i-1)    // ①
    {
        p = p->next;    // 查找第i-1个结点,p指向该(i-1)结点
        j++;
    }
    if(!p || j>i-1) return ERROR;   // i>n+1 或者 i<1 --> i大于表长+1 或者 小于1,位置非法
    s = new LNode;  // ②生成新结点*s
    s->data = e;    // ③将结点*s的数据域置为e
    s->next = p->next;  // ④将结点*s的指针域指向p的后面一个结点
    p->next = s;    // ⑤将结点*p的指针域指向结点*s
    return OK;
}

// 删除————删除第i个结点
Status ListDelete(LinkList &L, int i) //在带头结点的单链表L中,删除第i个元素
{
    p = L;
    int j = 0;
    while(p->next && j<i-1)    //①
    {
        p = p->next;    // 寻找第i个结点,并令p指向其前驱
        ++j;
    }
    if(!p->next || j>i-1) return ERROR; // 删除位置不合理
    q = p->next;    // ②临时保存被删结点,以备释放
    p->next = q->next;  // ③改变删除结点前驱结点的指针域
    delete q;   // ④释放删除结点的空间
    return OK;
}

时间效率分析:

  • 查找O(n)
  • 插入删除O(1)
  • 如果要在单链表中进行前插或删除操作,就要从头查找前驱结点,所耗时间复杂度为O(n) 
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值