数据结构之C语言实现单链表

定义

什么是单链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwWuWTPk-1652428304786)(https://secure2.wostatic.cn/static/daqpKL7WpQHTCSjLmooNZG/image.png)]

  • 优点:不要求大片不连续空间,改变容量方便
  • 缺点:不可随机存取,要耗费一定空间存放指针

用代码定义一个单链表

struct LNode{
  ElementType data;
  struct LNode *next;
}


struct LNode* p=(struct LNode*)malloc(sizeof(struct LNode));

要表示一个单链表时,只需声明一个头指针L,指向单链表的第一个节点

typedef使用

// typedef 关键字--数据类型重命名
typedef <数据类型> <别名>
typedef int zhengshu;
typedef int *zhengshuzhizhen;
type struct LNode LNode;
LNode *p=(LNode*)malloc(sizeof(LNode));

typedef stuct LNode{    //定义单链表结点类型
  ElemType data;        //每个结点存放一个数据元素
  struct LNode *next;   //指针指向下一个节点
}LNode,*LinkList;
struct LNode{
  ElemType data;
  struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode *LinkList;

/**
 * LNode *强调这是返回一个结点指针
 * LinkList 强调这是一个单链表
 */
LNode *GetElem(LinkList l,int i){
  int j=1;
  LNode *p=l->next;
  if(i==0){
    return l;
  }
  if(i<1){
    return NULL;
  }
  where(p!=NULL&&j<i){
    p=p->next;
    j++;
  }
  return p;
}

不带头节点

typedef struct LNode{
  ElemType data;
  struct LNode *next;
}LNode,*LinkList;

//初始化一个空的单链表
bool initList(LinkList &l){
  //空表,暂时还没有任何结点,防止脏数据
  l=NULL;
  return true;
}
//判断单链表是否为空
bool Empty(LinkList l){
  return l==NULL;
}

带头结点

typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;
//初始化一个单链表(带头结点)
bool initList(LinkList &L){
  L=(LNode*)malloc(sizeof(LNode));
  if(L==NULL){
    return false;
  }
  L->next=NULL;
  return true;
}
//判断单链表是否为空
bool Empty(LinkList L){
  return L->next==NULL;
}

基本操作的实现

插入删除操作

  1. 插入
    1. 按位序插入
      1. 带头结点
      2. 不带头节点
    2. 指定节点的后插操作
    3. 指定节点的前插操作
  2. 删除
    1. 按位序删除
    2. 指定节点的删除
/**
 * LNode *强调这是返回一个结点指针
 * LinkList 强调这是一个单链表
 * @author five-five
 * @created 2022/5/9
 *
 */
#include "stdlib.h"
#include "stdio.h"
#include "stdbool.h"

typedef struct LNode {
    int data;
    struct LNode *next;
} *LinkList, LNode;

/**
 * 初始化单链链表
 * @param linkList 链表指针
 * @return 0:初始失败?初始成功
 */
bool initList(LinkList *linkList) {
    *linkList = (LinkList) malloc(sizeof(LNode));
    if (NULL == *linkList) {
        return false;
    }
    (*linkList)->data = 0;
    (*linkList)->next = NULL;
    return true;
}

/**
 *
 * @param linkList 单链链表
 * @return 0:初始失败?初始成功
 */
bool empty(LinkList linkList) {
    return linkList == NULL;
}

/**
 * 按照顺序插入
 * @param linkList 单链链表
 * @param i 插入的位置
 * @param e 元素值
 * @return
 */
bool listInsert(LinkList linkList, int i, int e) {
    //下标不合法,默认不允许插入到第一位
    if (i < 1) {
        return false;
    }
    LNode *cur = NULL;
    cur = linkList;
    int curIndex = 0;
    //把下标以后的结点都后移一个,并且将目标指针的上个指征保留下来,i-1即可
    while (cur != NULL && curIndex < i - 1) {
        cur = cur->next;
        curIndex++;
    }
    //判断当前结点是否有效,下标可能指向了空结点,看上面的while循环
    if (NULL == cur) {
        return false;
    }
    //创建结点,赋值插入
    LNode *targetNode = (LNode *) malloc(sizeof(LNode));
    //内存失败
    if (NULL == targetNode) {
        return false;
    }
    targetNode->data = e;
    //插入,目标结点的一些指针为当前结点的下一节点,当前结点的下一指针为目标结点
    targetNode->next = cur->next;
    cur->next = targetNode;
    return true;
}

/**
 * 后插操作:在p结点之后插入元素e
 * @param p 要参考的指针 
 * @param e 元素
 * @return true?成功:失败
 */
bool insertNextNode(LNode *p, int e) {
    if (NULL == p) {
        return false;
    }
    //创建指针,分配空间
    LNode *targetNode = (LNode *) malloc(sizeof(LNode));
    if (NULL == targetNode) {
        return false;
    }
    targetNode->data = e;
    targetNode->next = p->next;
    p->next = targetNode;
    return true;
}

/**
 * 前插操作:在p前插入一个节点
 * 重点: 修改两个结点元素值
 * @param p 目标结点
 * @param e 要插入结点
 * @return true?成功:失败
 */
bool insertPriorNode(LNode *p, int e) {
    if (NULL == p) {
        return false;
    }
    //创建指针,分配空间
    LNode *targetNode = (LNode *) malloc(sizeof(LNode));
    if (NULL == targetNode) {
        return false;
    }
    targetNode->next = p->next;
    p->next = targetNode;
    //修改元素值
    targetNode->data = p->data;
    p->data = e;
    return true;
}
/**
 * 按位序删除结点
 * @param linkList 单链链表
 * @param i 下标
 * @param e 接收删除元素值得指针
 * @return true?成功:失败
 */
bool listDelete(LinkList linkList, int i, int *e) {
    //不可删除头结点
    if (i < 1) {
        return false;
    }
    LNode *cur = NULL;
    cur = linkList;
    int curIndex = 0;
    while (NULL != cur && curIndex < i - 1) {
        cur = cur->next;
        curIndex++;
    }
    if (NULL == cur) {
        return false;
    }
    LNode *targetLNode = cur->next;
    //返回值赋值
    *e = targetLNode->data;
    //跳过结点
    cur->next = targetLNode->next;
    //释放内存
    free(targetLNode);
    return true;
}

/**
 * 删除指定结点
 * bug:删除最后节点时,有问题
 * @param p 要删除结点
 * @return true?成功:失败
 */
bool deleteNode(LNode *p) {
    if (NULL == p) {
        return false;
    }
    LNode *nextNode = p->next;
    if (NULL!=nextNode) {
        p->data = nextNode->data;
        p->next = nextNode->next;
        //释放内存
        free(nextNode);
    } else {
        p = NULL;
    }
    return true;
}

/**
 *
 * @return 0为程序运行成功,1为运行不成功
 */
int main() {
    LinkList linkList = NULL;
    initList(linkList);
    printf("%llu\n", sizeof(*linkList));
    printf("%llu\n", sizeof(*(linkList->next)));
    return 0;
}

查找操作

  1. 按位查找

  2. 按值查找

  3. 求单链表长度

  4. 三种基本操作的时间复杂度都是O_(n)

  5. 如何写循环扫描各个节点的代码逻辑

  6. 注意边界条件处理

/**
 * 按位查找
 * @param linkList 单链链表
 * @param i 下标
 * @return 返回结点指针
 */
LNode *getElem(LinkList linkList, int i) {
    LNode *node = NULL;
    if (NULL == linkList) {
        return node;
    }
    node = linkList;
    while (node != NULL && i > 0) {
        node = node->next;
        i--;
    }
    return node;
}

/**
 * 按值查找
 * @param linkList 单链链表
 * @param e 要查询的值
 * @return 返回结点指针
 */
LNode *locateElem(LinkList linkList, int e) {
    LNode *node = NULL;
    node = linkList;
    while (node != NULL) {
        if (node->data == e) {
            return node;
        }
        node = node->next;
    }
    return node;
}

/**
 * 返回链表的长度
 * @param linkList 单链链表
 * @return 链表的长度
 */
int length(LinkList linkList) {
    int length = 0;
    LNode *node = linkList;
    //有next才加1(初始化头结点不算入进来)
    while (node->next != NULL) {
        node = node->next;
        length++;
    }
    return length;
}


单链表的建立

步骤:

  1. 初始化一个单链表
  2. 每次取一个数据元素,插入到表尾或表头
/**
 * 正向创建单链表(尾插法)
 * @param linkList 单链表(指针的指针)
 * @param data 头结点的data
 * @return
 */
LinkList listTailInsert(LinkList *linkList, int data) {
    int x;
    *linkList = (LinkList) malloc(sizeof(LNode));
    (*linkList)->data = data;
    (*linkList)->next = NULL;
    LNode *s = NULL;
    LNode *r = *linkList;
    //下列写法s不会赋值
    //LNode *s,*r = *linkList;
    scanf("%d", &x);
    while (x != -1) {
        s = (LinkList) malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    r->next = NULL;
    return *linkList;
}

/**
 * 头插法创建单链表
 * @param linkList 单链表(指针的指针)
 * @param data 头结点的data
 * @return
 */
LinkList listHeadInsert(LinkList *linkList, int data) {
    int x = 0;
    *linkList = (LinkList) malloc(sizeof(LNode));
    (*linkList)->data = data;
    (*linkList)->next = NULL;
    LNode *s;
    scanf("%d", &x);
    while (x != -1) {
        s = (LinkList) malloc(sizeof(LNode));
        s->data = x;
        s->next = (*linkList)->next;
        (*linkList)->next = s;
        scanf("%d", &x);
    }
    return *linkList;
}

头插法,尾插法:核心就是初始化操作、指定节点的后插操作

tips:尾插法注意设置一个尾指针tip

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

five-five

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

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

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

打赏作者

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

抵扣说明:

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

余额充值