线性表-动态链表

one-to-one

经典单链表(动态链表)具体实现方案:给每一个元素配置一个指针,每个指针都指向相邻的下一个元素。(“链”字的由来)

单链表可以由什么组成?
一个结点(节点)的构成:数据域指针域(Next 域)
头指针:类型跟指针一样,但是它的特点是永远指向链表中的第一个结点
结点们头结点:有时为了方便操作,特意在链表开头留一个空结点(代表它数据域不正常利用)
首元结点:特指链表开头第一个存有了数据的结点
其它结点:链表中其它的结点
#include <stdio.h>
#include <stdlib.h>

#define ElemType char
//typedef char ElemType

/*结点结构体*/
typedef struct Node{
    ElemType data; //数据域
    struct Node* next; //指针域
}Node, *PNode; 
/*单链表结构体*/
typedef struct LinkList {
    PNode first; //指向头结点
    PNode last; //指向尾结点
    size_t size; //记录有效结点数
}LinkList;
/*初始化*/
void init(LinkList* ll) 
{
    ll->first = ll->last = (Node*)malloc(sizeof(Node)); //为ll的头、尾结点申请内存空间
    if (ll->first == NULL)
        return;
    ll->first->next = NULL; //初始化头结点的指针域
    ll->size = 0; //初始化ll的结点数
}
/*判空*/
int empty(LinkList* ll)
{
    if (ll->size == 0)
        return 1;
    else
        return 0;
}
/*长度*/
size_t size(LinkList* ll)
{
    return ll->size;
}
/*尾插*/
void push_back(LinkList* ll, ElemType value)
{
    PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
    if (p == NULL)
        return;
    p->data = value; //设置值
    p->next = NULL; //初始化next域

    ll->last->next = p; //将结点放到链表尾部
    ll->last = p; //包裹指针下移
    ll->size++; //增加结点数
}
/*头插*/
void push_front(LinkList* ll, ElemType value)
{
    PNode p = (Node*)malloc(sizeof(Node)); //申请内存空间
    if (p == NULL)
        return;
    p->data = value; //设置值

    p->next = ll->first->next;
    ll->first->next = p;
    if (ll->size == 0)
    {
        ll->last = p;
    }
    ll->size++; //增加结点数
}
/*尾删*/
void pop_back(LinkList* ll)
{
    if (ll->size == 0) //判断链表是否还有结点?
        return; //直接退出
    PNode p = ll->first; //缓冲变量p
    while (p->next != ll->last) //先找到倒数第二个结点,再代替
        p = p->next; //下移指针
    free(ll->last); //释放内存
    ll->last = p; //取代
    ll->last->next = NULL; //初始化尾结点的指针域
    ll->size--; //减少结点数
}
/*头删*/
void pop_front(LinkList* ll)
{
    if (ll->size == 0)
        return;
    PNode p = ll->first->next; //临时保存首元结点的地址
    ll->first->next = p->next; //解除连接
    free(p); //释放内存
    if (ll->size == 1)
        ll->last = ll->first;
    ll->size--;
}
/*值查找*/
Node* find(LinkList* ll, ElemType key)
{
    PNode p = ll->first->next;
    while (p!=NULL && p->data!=key)
        p = p->next;
    return p;
}
/*输出*/
void display(LinkList* ll)
{
    PNode p = ll->first->next;
    while (p != NULL)
    {
        printf("%c -> ", p->data);
        p = p->next;
    }
    printf("Nul.\n");
}
/*反转:beg、mid、end*/
LinkList* iteration_reverse(LinkList* ll)
{
    if (ll->first == NULL || ll->first->next == NULL)
    {
        return ll;
    }
    else
    {
        Node* beg = NULL;
        Node* mid = ll->first->next;
        Node* temp = ll->first->next;
        Node* end = temp->next;
        while (1)
        {
            mid->next = beg; //指针域换向
            if (end == NULL) 
            {
                //push_back(&ll, NULL);
                break;
            }
            beg = mid;
            mid = end;
            end = end->next;
        }
        ll->first->next = mid;
        return ll;
    }
}
int main() {
    LinkList ll = { NULL, NULL, 0 };
    init(&ll);
    printf("此单链表是否为空?(1代表true,0代表false)\n%d\n", empty(&ll));
    push_back(&ll, 'h');
    push_back(&ll, 'e');
    push_back(&ll, 'l');
    push_back(&ll, 'l');
    push_back(&ll, 'o');
    printf("插入hello的单链表的长度:%d\n", size(&ll));
    display(&ll);
    LinkList* list = iteration_reverse(&ll);
    display(list);
    printf("插入hello的单链表的长度:%d\n", size(list));
    //free(ll.first); //释放
    //free(ll.last); //释放
}

链表和顺序表的区分

链表顺序表
内存申请时机需要提前申请什么时候存储,什么时候申请
复用性一次开辟,永久使用(除了动态数组)随用随开
空间利用率链表 < 顺序表(链表明显容易产生空间碎片)
时间复杂度链表适合操作元素,顺序表适合访问查找元素

单链表结构2:

#include <stdio.h>
#include <stdlib.h>

//链表中节点的结构
typedef struct link {
    int  elem;
    struct link* next;
}Link;
/*初始化*/
Link* initLink() 
{
    int i; //缓冲变量i
    //1、创建头指针
    Link* p = NULL;
    //2、创建头结点
    Link* temp = (Link*)malloc(sizeof(Link));
    temp->elem = 0;
    temp->next = NULL;
    //头指针指向头结点
    p = temp;
    //3、每创建一个结点,都令其直接前驱结点的指针指向它
    for (i = 1; i < 5; i++) {
        //创建一个结点
        Link* a = (Link*)malloc(sizeof(Link));
        a->elem = i;
        a->next = NULL;
        //每次 temp 指向的结点就是 a 的直接前驱结点
        temp->next = a;
        //temp指向下一个结点(也就是a),为下次添加结点做准备
        temp = temp->next;
    }
    return p;
}
/*插入*/
void insertElem(Link* p, int elem, int add) 
{
    int i;
    Link* c = NULL; 
    Link* temp = p;//创建临时结点temp
    //首先找到要插入位置的上一个结点
    for (i = 1; i < add; i++) {
        temp = temp->next;
        if (temp == NULL) {
            printf("插入位置无效\n");
            return;
        }
    }
    //创建插入结点c
    c = (Link*)malloc(sizeof(Link));
    c->elem = elem;
    //① 将新结点的 next 指针指向插入位置后的结点
    c->next = temp->next;
    //② 将插入位置前结点的 next 指针指向插入结点;
    temp->next = c;
}
/*删除*/
int delElem(Link** p, int elem) //p为原链表,elem 为要删除的目标元素
{
    Link* del = NULL, *temp = *p;
    //删除首元结点需要单独考虑
    if (temp->elem == elem) {
        (*p) = (*p)->next;
        free(temp);
        return 1;
    }
    else
    {
        int find = 0;
        //1、找到目标元素的直接前驱结点
        while (temp->next) {
            if (temp->next->elem == elem) {
                find = 1;
                break;
            }
            temp = temp->next;
        }
        if (find == 0) {
            return -1;//删除失败
        }
        else
        {
            //标记要删除的结点
            del = temp->next;
            //2、将目标结点从链表上摘除
            temp->next = temp->next->next;
            //3、释放目标结点
            free(del);
            return 1;
        }
    }
}
/*更新*/
int amendElem(Link* p, int oldElem, int newElem) //p 为有头结点的链表,oldElem 为旧元素,newElem 为新元素
{ 
    p = p->next;
    while (p) {
        if (p->elem == oldElem) {
            p->elem = newElem;
            return 1;
        }
        p = p->next;
    }
    return -1;
}
//p为原链表,elem表示被查找元素
int selectElem(Link* p, int elem) {
    int i = 1;
    //带头结点,p 指向首元结点
    p = p->next;
    while (p) {
        if (p->elem == elem) {
            return i;
        }
        p = p->next;
        i++;
    }
    return -1;//返回-1,表示未找到
}
//输出链表中各个结点的元素
void display(Link* p) {
    p = p->next;
    while (p) {
        printf("%d ", p->elem);
        p = p->next;
    }
    printf("\n");
}
//释放链表
void Link_free(Link* p) {
    Link* fr = NULL;
    while (p->next)
    {
        fr = p->next;
        p->next = p->next->next;
        free(fr);
    }
    free(p);
}
int main() {
    Link* p = initLink();
    printf("初始化链表为:\n");
    display(p);
    printf("在第 3 的位置上添加元素 6:\n");
    insertElem(p, 6, 3);
    display(p);
    printf("删除元素4:\n");
    delElem(p, 4);
    display(p);
    printf("查找元素 2:\n");
    printf("元素 2 的位置为:%d\n", selectElem(p, 2));
    printf("更改元素 1 的值为 6:\n");
    amendElem(p, 1, 6);
    display(p);
    Link_free(p);
    return 0;
}

单链表的反转

即头尾颠倒,方向翻转

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值