【数据结构】——双向链表

速通回忆

如果你之前学过链表,可以直接看我下面的这个项目,实现了下面专题中所有内容;

如果你是个新手建议先不要看下面这个项目,先好好看一下下面每一个部分的代码思路,并尝试自己去写一下

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

/* 学生数据结构体 */
typedef struct{
    char name[20];
    int age;
}stu_demo;

/* 创建双向链表 */
typedef struct stu_l{
    stu_demo stu;
    struct stu_l *pre;
    struct stu_l *next;
}stu_line,*LinkList;

/* 初始化双向链表 */
LinkList LinkListInit(stu_demo demo){
    /* 创建新的节点动态分配存储空间 */
    LinkList temp = (LinkList)malloc(sizeof(stu_line));
    /* 赋值 */
    temp->pre = NULL;
    temp->next = NULL;
    strcpy(temp->stu.name, demo.name);
    temp->stu.age = demo.age;

    return temp;
}

/* 双链表的头插法,将头节点下一位换位新插入的节点 */
/* 双链表的头插法,返回新的头节点 */
LinkList LinkLListCreateH(LinkList l){
    /* 动态分配新节点的空间 */
    LinkList n = (LinkList)malloc(sizeof(stu_line));
    /* 读取要输入的学生信息 */
    printf("头插法\n");
    printf("输入添加的学生姓名:");
    scanf("%s", n->stu.name);
    printf("输入学生年龄:");
    scanf("%d", &n->stu.age);
    /* 将新节点插入到链表中 */
    n->next = l;
    if (l != NULL) {
        l->pre = n;
    }
    n->pre = NULL;
    /* 返回新的头节点 */
    return n;
}

/* 双链表的尾插法,将最后一个节点换为新插入的节点 */
void LinkLListCreateL(LinkList l){
    /* 动态分配节点空间 */
    LinkList n = (LinkList)malloc(sizeof(stu_line));
    LinkList point = l;
    /* 读取要存入的学生信息 */
    printf("尾插法\n");
    n->next = NULL;
    printf("输入添加的学生姓名:");
    scanf("%s",n->stu.name);
    printf("输入学生年龄:");
    scanf("%d",&n->stu.age);
    /* 链表指针指向链表的最后一个节点 */
    while(point->next != NULL)  
        point = point->next;
    /* 将节点放到链表的最后一个位置 */
    point->next = n;
    n->pre = point;
}

/* 遍历显示双链表的所有节点数据 */
void LinkListShow(LinkList l){
    LinkList point = l;
    printf("|\t姓名\t|\t年龄\t|\n");
    while(point!=NULL){
        printf("|\t%s\t|\t%d\t|\n",point->stu.name,point->stu.age);
        point = point->next;
    }
}

/* 遍历显示双链表的所有节点数据 */
void LinkListInser(LinkList l, char *stu_name){
    /* 动态分配节点空间 */
    LinkList n = (LinkList)malloc(sizeof(stu_line));
    LinkList point = l;
    /* 读取要存入的学生信息 */
    n->next = NULL;
    printf("输入添加的学生姓名:");
    scanf("%s",n->stu.name);
    printf("输入学生年龄:");
    scanf("%d",&n->stu.age);
    /* 遍历找到与name名字相符的节点位置 */
    while(point != NULL){
        if(strcmp(point->stu.name, stu_name) == 0){
            n->next = point->next;
            point->next = n;
            point->next->pre = n;
            n->pre = point;
            return ;
        }
        point = point->next;
    }
    printf("查询无结果\n");
}

void LinkListReplace(LinkList l, char *stu_name){
    LinkList point = l;
    /* 遍历找到与name名字相符的节点位置 */
    while(point != NULL){
        if(strcmp(point->stu.name, stu_name) == 0){
            printf("请输入对%s同学,修改后的年龄:",stu_name);
            scanf("%d",&point->stu.age);
            return ;
        }
        point = point->next;
    }
    printf("查询无结果\n");
}

void LinkListDelete(LinkList l, char *stu_name){
    LinkList point = l;
    LinkList temp = (LinkList)malloc(sizeof(stu_line));
    /* 遍历找到与name名字相符的节点位置 */
    while(point != NULL){
        if(strcmp(point->stu.name, stu_name) == 0){
            temp = point;
            point->pre->next = point->next;
            point->next->pre = point->pre;
            free(temp);
            return ;
        }
        point = point->next;
    }
    printf("查询无结果\n");
}

int main(){
    /* 获取双向链表的第一个学生信息(双向链表第一个节点的数据段不为空) */
    stu_demo demo;
    printf("请输入学生姓名:");
    scanf("%s",demo.name);
    printf("请输入学生年龄:");
    scanf("%d",&demo.age);
    /* 初始化双向链表 */
    LinkList stu_data = LinkListInit(demo);
    /* 添加数据到双向链表 */
    stu_data = LinkLListCreateH(stu_data);
    LinkLListCreateL(stu_data);
    /* 遍历输出节点 */
    LinkListShow(stu_data);
    /* 在某个节点后插入一个节点 */
    LinkListInser(stu_data, "liu");
    /* 遍历输出节点 */
    LinkListShow(stu_data);
    /* 遍历替换到与名字相同的节点的信息 */
    LinkListReplace(stu_data, "liu");
    /* 遍历输出节点 */
    LinkListShow(stu_data);
    /* 遍历删除节点 */
    LinkListDelete(stu_data, "liu");
    /* 遍历输出节点 */
    LinkListShow(stu_data);
    free(stu_data);
    return 0;
}

双向链表的概念

双向链表的存在是为了解决单链表的缺点,让链表更加优秀。
单链表是结点中只有一个指向其后继的指针,具有单向性,当需要搜索大量数据时候,就必须要多次进行从头开始的遍历了,这样不是很便利

对此在单链表的基础上,产生了双向链表的概念,即: 在单链表的基础上,对于每一个结点设计一个前驱结点,前驱结点与前一个结点相互连接,构成一个链表

一个完整的双向链表应该是头结点的pre指针指为空,尾结点的next指针指向空,其余结点前后相链

双向链表的结点设计

pre代表的是前驱指针,它永远指向当前结点的前一个结点
注意,如果当前结点是头结点,则pre指针为空

next代表的是后继指针,它永远指向当前结点的下一个结点
注意,如果当前结点是尾结点,则next指针为空

typedef struct line{
    int data;           //data
    struct line *pre;   //pre node
    struct line *next;  //next node
}line,*a;
//分别表示该结点的前驱(pre),后继(next),以及当前数据(data)

双链表的创建

双向链表的头结点是有数据元素的,也就是头结点的data域中是存有数据的,这与一般的单链表是不同的

//创建双链表
line* initLine(line * head){
    int number,pos=1,input_data;
    //三个变量分别代表结点数量,当前位置,输入的数据
    printf("请输入创建结点的大小\n");
    scanf("%d",&number);
    if(number<1){return NULL;} //输入非法直接结束
    //////头结点创建///////
    head=(line*)malloc(sizeof(line));
    head->pre=NULL;
    head->next=NULL;
    printf("输入第%d个数据\n",pos++);
    scanf("%d",&input_data);
    head->data=input_data;
  
    line * list=head;
    while (pos<=number) {
        line * body=(line*)malloc(sizeof(line));
        body->pre=NULL;
        body->next=NULL;
        printf("输入第%d个数据\n",pos++);
        scanf("%d",&input_data);
        body->data=input_data;
        
        list->next=body;
        body->pre=list;
        list=list->next;
    }
    return head;
}

双向链表的插入操作

//插入数据
line * insertLine(line * head,int data,int add){
    //三个参数分别为:进行此操作的双链表,插入的数据,插入的位置
    //新建数据域为data的结点
    line * temp=(line*)malloc(sizeof(line));
    temp->data=data;
    temp->pre=NULL;
    temp->next=NULL;
    //插入到链表头,要特殊考虑
    if (add==1) {
        temp->next=head;
        head->pre=temp;
        head=temp;
    }else{
        line * body=head;
        //找到要插入位置的前一个结点
        for (int i=1; i<add-1; i++) {
            body=body->next;
        }
        //判断条件为真,说明插入位置为链表尾
        if (body->next==NULL) {
            body->next=temp;
            temp->pre=body;
        }else{
            body->next->pre=temp;
            temp->next=body->next;
            body->next=temp;
            temp->pre=body;
        }
    }
    return head;
}

双向链表的删除操作

//删除元素
line * deleteLine(line * head,int data){
    //输入的参数分别为进行此操作的双链表,需要删除的数据
    line * list=head;
    //遍历链表
    while (list) {
        //判断是否与此元素相等
        //删除该点方法为将该结点前一结点的next指向该节点后一结点
        //同时将该结点的后一结点的pre指向该节点的前一结点
        if (list->data==data) {
            list->pre->next=list->next;
            list->next->pre=list->pre;
            free(list);
            printf("--删除成功--\n");
            return head;
        }
        list=list->next;
    }
    printf("Error:没有找到该元素,没有产生删除\n");
    return head;
}

双向链表的遍历

利用next指针逐步向后进行索引即可,注意判断这里,我们既可以用while(list)的操作直接判断是否链表为空,也可以使用while(list->next)的操作判断该链表是否为空

//遍历双链表,同时打印元素数据
void printLine(line *head){
    line *list = head;
    int pos=1;
    while(list){
        printf("第%d个数据是:%d\n",pos++,list->data);
        list=list->next;
    }
}
如果您觉得这篇文章不错,且手里较为宽裕,可以支持一下博主,一分也是缘分😊
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇