2.2.3 线性表的链式存储-单链表(王道考研)

学习目标:

  • 链表
    • 单链表
      • 定义
      • 基本操作的实现
    • 双链表
    • 循环链表
    • 静态链表

两种线性表的存储方式的优缺点

  1. 顺序表
    优点: 可以随机存取,存储密度高
    缺点: 要求大片的连续空间,改变容量不方便
  2. 单链表
    优点: 不要求大片连续存储空间,改变容量方便
    缺点: 不可随机存取,要耗费一定的空间存放指针

用代码定义单链表

/**
 * 传统定义方法
 */
//struct LNode{
//    int data;
//    struct LNode *next;
//};
//typedef struct LNode LNode;
//typedef struct LNode *LinkList;
/**
 * 课本上的定义方法
 */
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode, *LinkList;

获取节点

LNode *getElem(LinkList L, int i) {
    int j = i;
    LNode *p = L->next;
    if (i==0){
        return L;
    }
    if (i<1){
        return NULL;
    }
    while (p!= NULL && j <i){
        p = p->next;
        j++;
    }
    return p;
}

初始化一个不带头指针的单链表

/**
 * 初始化一个空的单链表
 */
 bool InitList(LinkList &L){
     L = NULL;              //空表,暂时没有任何节点-->防止脏数据
    return true;    
 }

void test(){
     LinkList L;
    InitList(L);
 }

初始化一个带头指针的单链表


 /**
  * 初始化一个带头节点的单链表
  * @param L
  * @return
  */
 bool InitList1(LinkList &L){
     L = (LNode *) malloc(sizeof (LNode));
     if (L == NULL){
         return false;
     }
     L->next = NULL;
     return true;
 }

 void test1(){
     LinkList L;
     InitList1(L);
 }

不带头节点 V.S. 带头结点

不带头节点的代码操作更加复杂,处理第一个数据节点和后续节点的处理需要不同的代码逻辑。

LinkList — 强调这是一个单链表
Lnode*  — 强调这是一个节点

单链表的插入和删除

  • 插入
    • 按位序插入
    • 指定节点的后插操作
      • 带头节点
      • 不带头节点
    • 指定节点的前插操作
  • 删除
    • 按位序删除
    • 指定节点的删除

按位序插入(带头结点)

ListInsert(&L, i, e)

/**
 * 在带头结点的单链表中插入元素
 * 在第i个位置插入元素e
 * @param L 
 * @param i 
 * @param e 
 * @return 
 */
bool ListInsertDaiTouJieDian(LinkList &L, int i, int e) {
    if (i < 1) {
        return false;
    }
    LNode *p;
    int j = 0;
    p = L;
    while (p != NULL && j < i-1){
        p = p->next;
        j++;
    }
    if (p==NULL){
        return false;
    }
    LNode *s = (LNode *) malloc(sizeof (LNode));
    s->data = e;
    s->next = p->next;
    p->next=s;
    return true;
}

时间复杂度 O(n)

按位序插入(不带头节点)


/**
 * 笔袋头节点的单链表,按位序插入
 * ps:如果不带头结点,删除第一个元素时,需要改变头指针L;
 * @param L 
 * @param i 
 * @param e 
 * @return 
 */
bool ListInsertBuDaiTouJieDian(LinkList &L, int i, int e) {
    if (i < 1) {
        return false;
    }
    if (i == 1) {
        LNode *s = (LNode *) malloc(sizeof(LNode));
        s->data = e;
        s->next = L;
        L = s;
        return true;
    }

    LNode *p;
    int j = 1;
    p = L;
    while(p!= NULL && j < i-1){
        p = p->next;
        j++;
    }
    if (p == NULL){
        return false;
    }
    LNode *s = (LNode * ) malloc(sizeof (LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}

指定节点后的后插操作


/**
 * 后插操作
 * 在第i个位置后进行插入操作
 * @param L 
 * @param i 
 * @param e 
 * @return 
 */
bool ListInsertAnWeiZhi(LinkList &L, int i, int e) {
    if (i < 1) {
        return false;
    }
    LNode *p;
    int j = 0;
    p = L;
    while (p != NULL && j < i - 1) {
        p = p->next;
        j++;
    }
    if (p == NULL) {
        return false;
    }
    LNode *s = (LNode *) malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}

/**
 * 后插操作
 * 在指定节点后进行操作
 * @param p 
 * @param e 
 * @return 
 */
bool ListInsertNextNode(LNode *p, int e) {
    if (p == NULL) {
        return false;
    }
    LNode *s = (LNode *) malloc(sizeof (LNode));
    if (s == NULL){         //内存分配失败
        return false;
    }
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}

指定节点的前插操作


/**
 * 前插操作
 * 偷天换日
 * 由于不知道指定节点的前方节点,只能在后方节点后插入,然后更换数据
 * @param p
 * @param e
 * @return
 */
bool ListInsertPriorNode(LNode *p, LNode *s){
    if (p==NULL){
        return false;
    }
//    LNode *s = (LNode *) malloc(sizeof (LNode));
//    if (s== NULL){
//        return false;
//    }
    s->next = p->next;
    p->next = s;
    int temp = p->data;
    p->data = s->data;
    p->data = temp;
    return true;
}

按位序删除(带头结点)


/**
 * 删除带头节点的单链表中的节点
 * @param L 
 * @param i 
 * @param e 
 * @return 
 */
bool ListDeleteDaiTouJieDian(LinkList &L, int i, int &e) {
    if (i < i) {
        return false;
    }
    LNode *p;
    int j = 0;
    p = L;
    while (p != NULL && j < i - 1) {
        p = p->next;
        j++;
    }
    if (p==NULL){
        return false;
    }
    if(p->next == NULL){
        return false;
    }
    LNode *q = p->next;
    e = q->data;
    p->next = q->next;
    free(q);
    return true;
}

时间复杂度O(n)

指定节点的和删除

/**
 *删除指定节点
 * 需要注意的是不知道前驱节点,可以知道后继节点
 * 采用更改数据域的内容删除该节点
 * @param p 
 * @return 
 */
bool DeleteNode(LNode *p) {
    if (p == NULL) {
        return false;
    }
    LNode *q = p->next;
    p->data = p->next->data;
    p->next = q->next;
    free(q);
    return true;
}

时间复杂度 O(1)

单链表的局限性

无法逆向检索

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jupyter Aaron

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值