单链表C/C++实现

单链表C/C++实现

#include <stdio.h>
#include "malloc.h"

typedef int ElemType;

typedef struct LNode
{                       // 定义单链表节点类型
    ElemType data;      // 数据域
    struct LNode *next; // 指针域
} LNode, *LinkList;

// 初始化
//  无头结点
//  bool InitList(LinkList &L){
//      L = NULL;           //空表,暂时还没有人和结点
//      return true;
//  }
//  带头结点
bool InitList(LinkList &L)
{
    L = (LNode *)malloc(sizeof(LNode)); // 分配一个头结点
    if (L == NULL)                      // 内存不足,分配失败
        return false;
    L->next = NULL; // 头结点的指针域指向空值
    L->data = 0;    // 头结点的数据域用于存放结点个数 不存放也可以,我选择存放
    return true;
}

// 判断是否为空表
bool Empty(LinkList L)
{
    return L == NULL;
}

// 创建表
// 头插法
void List_HeadInsert(LinkList &L)
{
    int x, n;
    L = (LNode *)malloc(sizeof(LNode)); // 分配头结点
    LNode *s;                           // 临时结点用于存放数据
    L->next = NULL;                     // 初始为空链表
    printf("请输入你要创建单莲表结点的个数\n");
    scanf("%d", &n);
    printf("请输入你要插入的数据\n");
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &x); // 输入节点的值
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next;
        L->next = s;
    }
    L->data = n; // 用头结点存放结点个数 不存放也可以,我选择存放
}

//尾插法
void List_TailInsert(LinkList &L)
{
    int x, n;
    L = (LNode *)malloc(sizeof(LNode)); // 分配头结点
    LNode *s,*r=L;                           // s用于存放数据,r表示尾部结点
    printf("请输入你要创建单莲表结点的个数\n");
    scanf("%d", &n);
    printf("请输入你要插入的数据\n");
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &x); // 输入节点的值
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s;
    }
    r->next = NULL;
    L->data = n; // 用头结点存放结点个数 不存放也可以,我选择存放
}

// 展示表
bool PrintList(LinkList L)
{
    if (L->next == NULL||L->data == 0)
    {
        printf("表是空表\n");
        return false;
    }
    LNode *s;
    s = L->next;     // 将第一个数据结点赋值给s
    int n = L->data; // 定义n为结点个数
    for (int i = 0; i < n; i++)
    {
        printf("第%d个数据元素为%d\n", i + 1, s->data);
        s = s->next; // 遍历下一个结点
    }
    return true;
}

//按位查找
LNode *GetElem(LinkList L,int i){
    if(L==NULL||i<1)
        return NULL;
    LNode *p = L->next;     //从第一个节点开始找
    int j = 1;
    while(p!=NULL&&j<i){
        p = p->next;
        j++;
    }
    if(p==NULL)
        return NULL;
    return p;
}

//按值查找
LNode *LocateElem(LinkList L,ElemType e){
    if(L==NULL)
        return NULL;
    LNode *p = L->next;         //从第一个节点开始
    while(p!=NULL&&p->data!=e)
        p = p->next;
    if(p==NULL)
        return NULL;
    return p;
}

// 插入结点
// 在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L,int i,ElemType e){
    if(i<1)
        return false;
    LNode *p;   //指针p指向当前扫描到的结点
    int j = 0;  //当前p指向的是第几个结点,0表示头结点
    p = L;      //L指向头结点
    while(p!=NULL&&j<i-1){      //循环找到第i-1个结点
        p = p->next;
        j++;
    }
    if(p==NULL)     //i值不合法
        return false;
    LNode *s = (LNode*)malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    p->next = s;
    L->data += 1;
    return true;
}
//最好时间复杂度O(1),最坏、平均时间复杂度O(n)

//插入操作的子操作:前插操作和后插操作
//指定节点的后插操作
//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p,ElemType 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;
}
//可是这样的话L->data的数据长度就失效了。个人认为这个函数可有可无
//时间复杂度O(1)

//前插操作
//前插操作:在p结点之前插入元素e
bool InsertPriorNode(LNode *p,ElemType e){
    if(p==NULL)
        return false;
    LNode *s = (LNode*)malloc(sizeof(LNode));
    if(s==NULL)         //内存分配失败
        return false;
    //插入s到p后面
    s->next = p->next;
    p->next = s;
    //把p->data的值赋给s
    s->data = p->data;
    //p->data重新赋值为e
    p->data = e;
    return true;
}
//时间复杂度O(1)

//封装的插入操作
//结合按位查找和按值查找之后插入节点可以这么写
bool SimpleListInsert(LinkList &L,int i,ElemType e){
    if(i<1)
        return false;
    //找到第i-1的结点
    LNode *p = GetElem(L,i-1);      
    if(p==NULL)
        return false;
    //后插操作,在i-1后面,就是i的位置插入结点
    InsertNextNode(p,e);        //实际上可以直接return InsertNextNode(p,e);但是我有L->data += 1;所以不可以这么写
    L->data += 1;
    return true;
}

//删除结点
//删除第i个位置,并返回值到e(带头结点)
bool ListDelete(LinkList &L,int i,ElemType &e){
    if(i<1)
        return false;
    LNode *p=L,*q;
    int j = 0;
    while(p!=NULL&&j<i-1){
        p = p->next;
        j++;
    }
    q = p->next;
    e = q->data;
    p->next = q->next;
    free(q);        //释放结点的内存
    L->data -= 1;
    return true;
}
//最好时间复杂度O(1),最坏、平均时间复杂度O(n)

//删除节点的子操作
//指定节点删除
bool DeleteNode(LNode *p,ElemType &e){
    if(p==NULL)
        return false;
    LNode *q = p->next;
    e = p->data;
    //把p的下一个结点q的data值赋给p->data
    //注:因为被删除的是p结点,所以p结点的值被覆盖了也无所谓;或者说p的data值已经赋给了e
    p->data = q->data;
    //p指向下下一个结点,相当于删除q结点,但是把q结点的值赋给了p
    p->next = q->next;
    free(q);
    return true;
}
//如果删除节点是最后一个结点的话,会出现空指针错误。
//不能实现L->data同步

//封装的删除操作
//综合按位查找函数和删除指定结点函数可以这么写
bool SimpleListDelete(LinkList &L,int i,ElemType &e){
    if(i<1)
        return false;
    //找到第i个结点
    LNode *p = GetElem(L,i);        
    if(p==NULL)
        return false;
    //删除第i个结点
    DeleteNode(p,e);
    L->data -= 1;
    return true;
}

//求表长操作
int Length(LinkList L){
    return L->data;
}
//这是因为我用L->data存储表长了,当然可以遍历全表然后len++  return len;

//销毁操作
void Destroy(LinkList &L){
    L->data = 0;
    LNode *p = L;
    while (p!=NULL)
    {
        L = L->next;
        free(p);
        p = L;
    }
}

int main(int argc, char const *argv[])
{   
    ElemType e;
    LinkList L; // 声明一个指向单链表的指针
    // 初始化一个空表
    InitList(L);
    PrintList(L);
    printf("--------------------------------\n");
    // 头插法
    // List_HeadInsert(L);
    // PrintList(L);
    // 尾插法
    List_TailInsert(L);
    PrintList(L);
    printf("--------------------------------\n");
    // 插入操作
    // ListInsert(L,2,666);
    // PrintList(L);

    // 删除操作
    // ListDelete(L,2,e);
    // PrintList(L);
    // printf("e=%d",e);

    // 前插操作
    // InsertPriorNode(L->next->next,666);
    // // 相当于展示表
    // LNode *s;
    // s = L->next;     // 将第一个数据结点赋值给s
    // int n = L->data+1; // 定义n为结点个数  因为前插函数插入了一个数据,但是没有同步L.data
    // for (int i = 0; i < n; i++)
    // {
    //     printf("第%d个数据元素为%d\n", i + 1, s->data);
    //     s = s->next; // 遍历下一个结点
    // }

    // 按位查找
    // LNode *p = GetElem(L,3);
    // printf("p->data=%d",p->data);

    // 按值查找
    // LNode *p = LocateElem(L,5);
    // printf("p->next->next->data=%d",p->next->next->data);

    // 删除指定节点
    // DeleteNode(L->next->next,e);
    // //相当于展示表
    // LNode *s;
    // s = L->next;     // 将第一个数据结点赋值给s
    // int n = L->data-1; // 定义n为结点个数  因为删除指定节点函数删除了一个数据,但是没有同步L.data
    // for (int i = 0; i < n; i++)
    // {
    //     printf("第%d个数据元素为%d\n", i + 1, s->data);
    //     s = s->next; // 遍历下一个结点
    // }
    // printf("e=%d",e);

    // 封装后的插入操作
    // SimpleListInsert(L,3,666);
    // PrintList(L);
    
    // 封装后的删除操作
    // SimpleListDelete(L,3,e);
    // PrintList(L);
    // printf("e=%d",e);

    //销毁操作
    Destroy(L);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值