数据结构---链表 & 栈

文章目录


链表

概念:链表是逻辑上连续,物理储存结构上非连续、非顺序的储存结构。

数据元素的逻辑连续是通过额外的指针链接次序实现并保持的。

如图,展示的是一个不带头结点的双向循环链表,head是一个结点指针,指向了链表的第一个元素,当然这只是逻辑地址,真实在内存中的物理地址其实是随意分配的,只是通过指针能够找到对应的位置

代码实现

本次代码实现的是一个双向不循环不带头结点的链表

定义结点

c 复制代码
typedef struct node
{
    //数据域
    void* data;
    //上一个节点 指针
    struct node *prev;
    //下一个节点 指针
    struct node *next;
}node;

数据域仍是采用void指针类型,可以代表任意类型的指针

定义链表

c 复制代码
typedef struct linked_list
{
   //头节点
   node *head;
   //尾节点
   node *tail;
   //链表中节点的数量
   int size;
}linked_list;

双向链表的结构体关键就是在于用两个结点指针去指向对应的头结点和尾结点

创建结点

c 复制代码
node* create_note(void *data){
    node *new_node =(node*)calloc(1,sizeof(node)); 
    if (new_node==NULL)
    {
        printf("创建节点失败!\n");
        return NULL;
    }
    //节点中数组赋值
    new_node->data=data;
    //新节点 上/下 为NULL
    new_node->prev=NULL;
    new_node->next=NULL;
    return new_node;
}

创建链表

c 复制代码
linked_list* create_linked_list(){
    linked_list *new_list =(linked_list*)calloc(1,sizeof(linked_list)); 
    if (new_list==NULL)
    {
        printf("创建双向链表失败!\n");
        return NULL;
    }
    //空双向链表
    new_list->head=NULL;
    new_list->tail=NULL;
    new_list->size=0;
    return new_list;
}

头插

c 复制代码
void push_front(linked_list *list,void *data){
    //创建新节点
    node *new_node=create_note(data);
    //判断链表是否为空
    if (list->head==NULL)
    {
        //头尾节点都为新节点
        list->head=new_node;
        list->tail=new_node;
    }else
    {   
        //新节点的next为旧头节点
        new_node->next=list->head;
        //旧头节点的prev为新节点
        list->head->prev=new_node;
        //链表更新头节点为新节点
        list->head=new_node;
    }
    //链表节点数量+1
    list->size++;
}

1.链表为空,新结点既是头结点,也是尾结点

2.链表不为空,执行头插

3.新结点的next为旧的头结点

4.旧的头结点的prev为新节点

5.更新链表的头节点为新节点

6.结点数量+1

尾插

c 复制代码
void push_back(linked_list *list,void *data){
    //创建新节点
    node *new_node=create_note(data);
    //判断链表是否为空
    if (list->tail==NULL)
    {
        //头尾节点都为新节点
        list->head=new_node;
        list->tail=new_node;
    }else
    {   
        //新节点的prev为旧尾节点
        new_node->prev=list->tail;
        //旧尾节点的next为新节点
        list->tail->next=new_node;
        //链表更新尾节点为新节点
        list->tail=new_node;
    }
    //链表节点数量+1
    list->size++;
}

1.链表为空,新节点是头结点也是尾结点

2.链表不为空,执行尾插

3.新节点的prev为旧的尾节点

旧的尾节点的next为新节点

5.更新链表的尾节点为新节点

6.节点数量+1

中间插

c 复制代码
void insert_after(linked_list *list,node *prev_node,void *data){
    //判断是否能从中间插入
    if (prev_node==NULL)
    {
        printf("上一个节点不能尾NULL!\n");
        return;
    }
    //创建新节点
    node *new_node=create_note(data);
    //设置新节点的前驱和后继指针
    new_node->prev=prev_node;
    new_node->next=prev_node->next;
    //判断后继节点是否为NULL
    if (prev_node->next!=NULL)
    {
        //设置后继节点的prev为新节点
        prev_node->next->prev=new_node;
    }else
    {
        //执行尾插
        list->tail=new_node;
    }
    //前驱节点的next为新节点
    prev_node->next=new_node;
    //节点数量+1
    list->size++;
}

中间插入的函数还要传递上一个结点指针.

搜索

c 复制代码
//先定义一个比较函数
int compare_int(void *a,void *b){
    return *(int*)a-*(int*)b;//0相同,非0不同
}
//8.2搜索节点
node* search_node(linked_list *list,void *data,int (*cmp)(void*,void*)){
    //从链表头部开始变量
    node* current=list->head;
    //遍历比较,通过数据找到节点
    while (current!=NULL)
    {
        if (cmp(current->data,data)==0)
        {
            //找到对应的节点
            return current;
        }
        //不相同,继续比较下一个
        current=current->next;
    }
    //未找到匹配的节点,返回NULL
    return NULL;
}

定义了一个比较函数compare(),在函数search_node()中使用函数指针调用比较函数compare();

释放

c 复制代码
//last.释放链表
void free_linked_list(linked_list *list){
    if (list==NULL)
    {
        printf("链表为NULL,无法释放!\n");
        return;
    }
    //先释放双向链表中的所有节点
    //获取头节点
    node *current=list->head;
    while (current!=NULL)
    {
        //节点不为NULL
        node *tmp=current;//当前节点
        current=current->next;
        //释放节点
        free(tmp);
    }
    //在释放链表
    list->head=NULL;
    list->tail=NULL;
    list->size=0;
    free(list);
}

栈的特点就是先进后出(First in Last out)

这里我们趁热打铁,使用链表来完成一个栈

牢记栈的特性,如果 按栈的特性来严格要求代码,就不是 一个

定义结点

c 复制代码
typedef struct node{
    void* data;
    struct node* next;
}node;

定义栈

c 复制代码
typedef struct stack{
    node* top; 
    int size;
}stack;

这里的链表其实就是一个单向链表 ,所以使用一个top指针就行,top指针始终指向栈顶元素,这个栈只有一个node指针的指针,和一个size,就能表示一个栈,细品

创建结点

c 复制代码
//创建结点
node* create_node(void* data){
    node* n = (node*)calloc(1,sizeof(node));
    n->next = NULL;
    n->data = data;
    return n;
}

创建栈

c 复制代码
//创建栈
stack* create_stack(){
    stack* new_stack =(stack*)calloc(1,sizeof(stack));
    if(new_stack==NULL)
    {
        printf("create fail\n");
        exit(0);
    }
    new_stack->top = NULL;
    new_stack->size = 0;
    return new_stack;
}

压栈

c 复制代码
}
//压栈
void push(stack* s,void* a)
{
    node* n = create_node(a);
    n->next = s->top;
    s->top = n;
}

出栈

c 复制代码
//出栈
void* pop(stack* s)
{
    if (s->top==NULL)
    {
        printf("there is no node\n");
        return NULL;
    }
    node* n = s->top;
    void* data = n->data;
    s->top=s->top->next;
    s->size--;
    return data;
}

查看栈顶

c 复制代码
void* peek(stack* s)
{
    void* a = s->top->data;
    return a;
}

释放栈

c 复制代码
//释放栈
void free_stack(stack* s){
    if (s == NULL)
    {
        printf("stack is NULL");
        return;
    }
    //从栈顶开始获取所有节点,释放所有节点
    node* current=s->top;
    while (current!=NULL)
    {
        node *tmp=current;
        current=current->next;
        free(tmp);
    }
    s->top=NULL;
    s->size=0;
    //释放栈
    free_stack(s);
}
相关推荐
漫随流水2 小时前
leetcode回溯算法(39.组合总和)
数据结构·算法·leetcode·回溯算法
独自破碎E2 小时前
【从下至上+动态规划】矩阵的最小路径和
算法·矩阵·动态规划
运筹说2 小时前
运筹说 第149期 粒子群算法入门:从鸟群觅食到优化求解,2大应用案例+MATLAB代码实战!
算法
lxw20230271162 小时前
python第一次作业
算法
苦藤新鸡2 小时前
20.右旋转图片
数据结构·算法·leetcode·力扣
ChoSeitaku2 小时前
28.C++进阶:map和set封装|insert|迭代器|[]
java·c++·算法
月挽清风2 小时前
代码随想录第六天:哈希表
算法·leetcode
爱编码的傅同学2 小时前
【今日算法】LeetCode 5.最长回文子串 和 287.寻找重复数
算法·leetcode·代理模式
思茂信息2 小时前
CST仿真实例:手机Type-C接口ESD仿真
c语言·开发语言·单片机·嵌入式硬件·智能手机·cst·电磁仿真