数据结构-单链表与双链表

一. 单链表

1.1 单链表查找中心节点

快慢指针法

cpp 复制代码
//查找返回链表的中间节点
//快慢指针
Node_t *find_mid_node(Link_t *plink)
{
    if(plink->phead == NULL)
        return NULL;
    Node_t *slow = plink->phead, *fast = plink->phead;
    while(fast && fast->pnext)
    {
        slow = slow->pnext;
        fast = fast->pnext->pnext;
    }
    return slow;
}

1.2 单链表查找并且修改

cpp 复制代码
//链表查找
Node_t *find_link(Link_t *plink, DATATYPE_T target)
{
    if(plink->phead == NULL)
        return NULL;

    Node_t *cur = plink->phead;
    while(cur)
    {
        if(cur->data == target)
            return cur;
        cur = cur->pnext;
    }
    return NULL;
}
//修改链表指定节点
int modify_link(Link_t *plink, DATATYPE_T olddata ,DATATYPE_T newdata)
{
    Node_t *ptarget = find_link(plink, olddata);
    if(ptarget == NULL)
        return -1;
    else
        ptarget->data = newdata;
    return 0;
}

1.3 单链表查找倒数第k个节点

cpp 复制代码
//查找链表倒数第k个节点
Node_t *find_last_k_node(Link_t *plink, int k)
{
    assert(plink);
    if(is_empty_link(plink) || k <= 0)
    {
        return NULL;
    }

    Node_t *pfast = plink->phead;
    Node_t *pslow = pfast;
    while(k--)
    {
        if(pfast == NULL)
        {
            return NULL;
        }
        pfast = pfast->pnext;
    }
    while(pfast)
    {
        pfast = pfast->pnext;
        pslow = pslow->pnext;
    }
    return pslow;
}

1.4 单链表反转

方法一:头插法

cpp 复制代码
//反转链表1
//头插法
int reverse_link1(Link_t *plink)
{
    assert(plink);
    if(is_empty_link(plink))
    {
        return -1;
    }
    Node_t *pinsert = plink->phead;
    Node_t *ptemp = pinsert;
    plink->phead = NULL;
    while(pinsert)
    {
        ptemp = ptemp->pnext;
        pinsert->pnext=plink->phead;
        plink->phead = pinsert;
        pinsert = ptemp;
    }
    return 0;
}

方法二:三指针法

cpp 复制代码
//反转链表2
//三指针法
int reverse_link2(Link_t *plink)
{
    assert(plink);
    if(is_empty_link(plink))
    {
        return -1;
    }
    Node_t *prev = NULL, *cur = plink->phead;
    Node_t *next = NULL;
    while(cur)
    {
        next = cur->pnext;
        cur->pnext = prev;
        prev = cur;
        cur = next;
    }
    plink->phead = prev;
    return 0;
}

方法三:迭代法

cpp 复制代码
//反转链表3
//递归
Node_t *reverse_link3(Node_t *phead)
{
    if(phead == NULL || phead->pnext ==NULL)
        return phead;
    else
    {
        Node_t *newhead = reverse_link3(phead->pnext);
        phead->pnext->pnext = phead;
        phead->pnext = NULL;
        return newhead;
    }
}

1.5 单链表的插入排序

cpp 复制代码
void sort_link_insert(Link_t *plink)
{
    //空链表或者只有一个节点则返回
    if(is_empty_link(plink) || plink->phead->pnext == NULL)
        return;
    Node_t *pinsert = NULL;
    Node_t *ptemp = plink->phead->pnext;
    plink->phead->pnext = NULL;//断开第一个节点
    while(ptemp)
    {
        pinsert = ptemp;
        ptemp = ptemp->pnext;
        if(pinsert->data <= plink->phead->data)//如果比头节点小则头插,并且成为新的头
        {
            pinsert->pnext = plink->phead;
            plink->phead = pinsert;
        }
        else
        {
            Node_t *p = plink->phead;
            while(p->pnext && pinsert->data > p->pnext->data)//找到要插入的位置
            {
                p = p->pnext;
            }
            pinsert->pnext = p->pnext;
            p->pnext = pinsert;
        }
    }
}

数组的插入排序:

cpp 复制代码
void insert_sort(int *arr, int size)
{
    int i = 0, j = 0;
    for(i = 1; i < size; i++){
        j = i;
        int temp = arr[j];
        while(j > 0 && arr[j - 1] > temp){
            arr[j] = arr[j - 1];
            j--;
        }
    arr[j] = temp;
    }
}

1.6 单链表判断有环

cpp 复制代码
//判断有环链表
//快慢指针法
int is_loop_link(Link_t *plink)
{
    if(is_empty_link(plink) || plink->phead->pnext == NULL)
        return 0;
    Node_t *fast = plink->phead;
    Node_t *slow = fast;

    while(fast && fast->pnext)
    {
        fast = fast->pnext->pnext;
        slow = slow->pnext;
        if(fast == slow)
            return 1;
    }
    return 0;
}

二. 双链表

2.1 双链表结构

2.2 双链表的实现

2.2.1 声明

cpp 复制代码
//类型重命名
typedef struct stu{
    int id;
    int score;
    char name[20];
}DataType_t;

//双链表节点结构体
typedef struct doulinknode{
    DataType_t data;
    struct doulinknode *pnext;
    struct doulinknode *ppre;
}DNode_t;

//双链表对象结构体
typedef struct doulink{
    DNode_t *phead;
    int len;
}DLink_t;

2.2.2 双链表对象的创建

cpp 复制代码
//创建双链表对象
DLink_t *creat_dlink()
{
   DLink_t *pdlink = malloc(sizeof(DLink_t));
   if(pdlink == NULL){
       printf("malloc error\n");
       return NULL;
   }
   pdlink->phead = NULL;
   pdlink->len = 0;
   return pdlink;
}

2.2.3 双链表节点的创建与判空

cpp 复制代码
//创建双链表新节点
DNode_t *creat_dnode(DataType_t data)
{
    DNode_t *pdnode = malloc(sizeof(DNode_t));
    if(pdnode == NULL){
        printf("malloc error\n");
        return NULL;
    }
    pdnode->data = data;
    pdnode->pnext = NULL;
    pdnode->ppre  = NULL;
    return pdnode;
}  
//判空
bool is_empty_dlink(DLink_t *pdlink)
{
    return pdlink->len == 0;
}

2.2.4 双链表头插

cpp 复制代码
//链表头插
int insert_dlink_head(DLink_t *pdlink, DataType_t data)
{
    DNode_t *pnewpnode = creat_dnode(data);
    if(pnewpnode == NULL){
        printf("creat_dnode error\n");
        return 0;
    }
    if(is_empty_dlink(pdlink)){
        pdlink->phead = pnewpnode;
    }
    else{
        pnewpnode->pnext = pdlink->phead;
        pdlink->phead->ppre = pnewpnode;
        pdlink->phead = pnewpnode;
    }
    pdlink->len++;
    return 1;
}

2.2.5 双链表头删

cpp 复制代码
//双链表头删
int delete_dlink_head(DLink_t *pdlink)
{
    if(is_empty_dlink(pdlink)){
        return 0;
    }
    DNode_t *pfree = pdlink->phead;
    //只有一个节点的情况
    if(pdlink->len == 1){
        free(pfree);
        pfree = NULL;
        pdlink->phead = NULL;
    }
    else{
        pfree->pnext->ppre = NULL;
        pdlink->phead = pfree->pnext;
        free(pfree);
        pfree = NULL;
    }
    pdlink->len--;
   

2.2.6 双链表尾插

cpp 复制代码
//双链表尾插
int insert_dlink_tail(DLink_t *pdlink, DataType_t data)
{
    //空链表,头插即可
    if(is_empty_dlink(pdlink)){
        insert_dlink_head(pdlink, data);
        return 1;
    }
    DNode_t *pnewnode = creat_dnode(data);
    DNode_t *ptail = pdlink->phead;
    while(ptail->pnext){
        ptail = ptail->pnext;
    }
    ptail->pnext = pnewnode;
    pnewnode->ppre = ptail;
    pdlink->len++;
    return 1;
}

2.2.7 双链表尾删

cpp 复制代码
//双链表尾删
int delete_dlink_tail(DLink_t *pdlink)
{
    if(is_empty_dlink(pdlink)){
        return 0;
    }
    //只有一个节点情况
    if(pdlink->phead->pnext == NULL){
        delete_dlink_head(pdlink);
    }
    else{
        DNode_t *tail = pdlink->phead;
        while(tail->pnext){
            tail = tail ->pnext;
        }
        tail->ppre->pnext = NULL;
        free(tail);
        tail = NULL;
        pdlink->len--;
        return 1;
    }
}

2.2.8 双链表销毁

cpp 复制代码
//销毁双链表
void destroy_dlink(DLink_t *pdlink)
{
    while(!is_empty_dlink(pdlink)){
        delete_dlink_head(pdlink);
    }
    free(pdlink);
    pdlink = NULL;
}

2.2.9 双链表查找

cpp 复制代码
//双链表查找
DNode_t *find_dlink(DLink_t *pdlink, int id)
{
    if(is_empty_dlink(pdlink)){
        return NULL;
    }
    DNode_t *ptarget = pdlink->phead;
    while(ptarget){
        if(ptarget->data.id == id){
            break;
        }
        ptarget = ptarget->pnext;
    }
    return ptarget;
}

2.2.10 双链表修改

cpp 复制代码
//双链表修改
//成绩
void modify_dlink(DLink_t *pdlink, int id, int score)
{
    DNode_t *ptarget = find_dlink(pdlink, id);
    if(ptarget == NULL){
        printf("找不到");
        return;
    }
    ptarget->data.score = score;
    printf("修改成功\n");
}
//名字
void modify_dlink(DLink_t *pdlink, int id, char *str)
{
    DNode_t *ptarget = find_dlink(pdlink, id);
    if(ptarget == NULL){
        printf("找不到");
        return;
    }
    strcpy(ptarget->data.name,str);
    printf("修改成功\n");
}

2.2.11 双链表插入排序

cpp 复制代码
//双链表插入排序
void sort_dlink_insert(DLink_t *pdlink)
{
    if(is_empty_dlink(pdlink) || pdlink->len == 1){
        return;
    }
    DNode_t *ptemp = pdlink->phead->pnext;
    DNode_t *pinsert = NULL;
    //断开链表
    pdlink->phead->pnext = NULL;
    ptemp->ppre = NULL;

    while(ptemp){
        pinsert = ptemp;
        ptemp = ptemp->pnext;
       // ptemp->ppre = NULL;//注意不能有这句,当上一步使ptemp为NULL时,这一步会段错误

        if(pinsert->data.score <= pdlink->phead->data.score){
            pinsert->pnext = pdlink->phead;
            pdlink->phead->ppre = pinsert;
            pinsert->ppre = NULL;
            pdlink->phead = pinsert;
        }
        else{
            DNode_t *p = pdlink->phead;
            while(p->pnext && p->pnext->data.score < pinsert->data.score){
                p = p->pnext;
            }
            pinsert->pnext = p->pnext;
            //这个判断也一定要有,不然当p指向尾节点时, p->pnext->ppre这样访问NULL会报段错误
            if(p->pnext){
                p->pnext->ppre = pinsert;
            }
            p->pnext = pinsert;
            pinsert->ppre = p;
        }
    }
}
相关推荐
小龙报16 小时前
【优选算法】双指针专项:1.移动零 2. 复写零 3.快乐数
java·c语言·数据结构·c++·python·算法·面试
sukioe17 小时前
深入理解 MySQL 索引:底层数据结构与 B+ 树设计原理
数据结构·mysql·oracle
小+不通文墨17 小时前
在树莓派中用*C语言*实现MQTT通信
c语言·经验分享·笔记·嵌入式硬件·学习
TDengine (老段)17 小时前
TDengine MemTable 深度解析 — 内存写入缓冲区的数据结构与生命周期
大数据·数据结构·数据库·物联网·时序数据库·tdengine·涛思数据
-To be number.wan17 小时前
算法日记 | C++ 结构体
数据结构·学习·算法
CS创新实验室17 小时前
数据结构和算法:摊还分析
java·数据结构·算法
curry____30317 小时前
邻接矩阵 和 领接表 和 链式前向星对比
数据结构·c++·算法
he___H18 小时前
leetcode100-合并区间
java·数据结构·算法
神仙别闹18 小时前
基于C语言来实现图形界面画板的功能
c语言·开发语言·单片机