实验四双向链表

合集下载

双向链表01

双向链表01

双向链表01
双向链表:在单链表的每个结点⾥再增加⼀个指向其前驱的指针域 prior,这样链表中就形成了有两个⽅向不同的链,故称其为“双向链表”。

双向链表的结构定义:
typedef struct DuLnode{
Elemtype data;
struct DuLNode *prior, *next;
} DuLNode, *DuLinkList;
双向循环链表:和单链的循环表类似,双向链表也可以有循环表:
①让头结点的前驱指针指向链表的最后⼀个结点;
②让最后⼀个结点的后继指针指向头结点。

双向链表结构的对称性(设指针 p 指向某⼀节点)
p -> proior ->next = p = p -> next -> prior
意思是指针 p 指向结点的前⼀个结点的后边那个节点,就是指针 p 指向的结点本⾝;
⽽ “p -> next -> prior” 意思则是,指针 p 所指向结点的后⼀个结点的前⼀个结点,⾃然也是指针 p 所指向结点。

在双向链表中有些操作(如:ListLength,GetElem等),因仅涉及⼀个⽅向的指针,故它们的算法与线性链表的相同。

但在插⼊、删除时,则需同时修改两个⽅向上的指针,两者操作的时间服早读均为 O(n)。

图文详解双向链表原理

图文详解双向链表原理

图⽂详解双向链表原理
双向链表的主要优点是对于任意给的结点,都可以很轻易的获取其前结点和后结点,其主要缺点是每个结点需要保存next和prev两个属性,因此需要更多的空间开销,同时结点的插⼊与删除操作也将更加耗时,因为需要操作更多的指向操作。

双向链表单个节点结构:
双向链表节点
双向链表的数据结构:
双向链表数据结构
双向链表的插⼊操作
插⼊数据到链表尾部
链表尾部插⼊数据
插⼊数据到链表中间
链表中部插⼊数据
双向列表删除操作
删除链表尾部数据
删除尾部数据
删除链表中间数据
删除中间数据
循环双向列表设计
循环双向链表是在普通双向链表基础上进化得到的。

在普通的双向链表中,如果我们要获取最后⼀个节点的时候,我们只能从头开始遍历,⼀直遍历到最后才能够拿到最后⼀个节点的数据。

⽽循环双向链表会把header的prev指向最后⼀个节点,最后⼀个节点next指向header。

其数据结构如图所⽰:
循环双向链表
循环链表的添加、删除和普通的双向链表是⼀模⼀样的,这⾥就不再赘述。

双向链表的算法设计与实现实验报告

双向链表的算法设计与实现实验报告

数学与计算科学学院实验报告
实验项目名称双向链表的算法设计与实现
所属课程名称__数据结构A
实验类型设计型
实验日期__
班级信计1402
学号201453100214
姓名俞凯烨
成绩
【实验小结】(收获体会)
附录1:源程序
附录2:实验报告填写说明
1.实验项目名称:要求与实验教学大纲一致。

2.实验目的:目的要明确,要抓住重点,符合实验教学大纲要求。

3.实验原理:简要说明本实验项目所涉及的理论知识。

4.实验环境:实验用的软、硬件环境。

5.实验方案(思路、步骤和方法等):这是实验报告极其重要的内容。

概括整个实验过程。

对于验证性实验,要写明依据何种原理、操作方法进行实验,要写明需要经过哪几个步骤来实现其操作。

对于设计性和综合性实验,在上述内容基础上还应该画出流程图、设计思路和设计方法,再配以相应的文字说明。

对于创新性实验,还应注明其创新点、特色。

6.实验过程(实验中涉及的记录、数据、分析):写明具体实验方案的具体实施步骤,包括实验过程中的记录、数据和相应的分析。

7.实验结论(结果):根据实验过程中得到的结果,做出结论。

8.实验小结:本次实验心得体会、思考和建议。

9.指导教师评语及成绩:指导教师依据学生的实际报告内容,给出本次实验报告的评价。

实验四 链表

实验四 链表

实验二链表的基本操作一、实验目的掌握链表的基本概念、结构的定义,通过设计程序掌握链表上的基本操作:建立、插入、删除、查找以及链表合并等,并理解线性表的两种存储结构:顺序表和链表的区别。

二、实验准备1. 复习C语言中指针的用法,特别是结构体的指针的用法。

2. 了解链表(含带头结点的链表、循环链表)的概念,链表的结构定义方法。

单链表是线性表的链式存储表示,是用一组任意的存储单元依次存储线性表的数据元素。

因此,为了表示每个数据元素a i与其直接后继元素a i+1之间的逻辑关系,对数据元素a i来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置),而这部分就是用指针来完成的。

3. 掌握线性表在链式存储结构上实现基本操作:建立、查找、插入、删除等算法。

在实现这些算法的时候,要注意判断输入数据的合法性,除此之外还要要注意以下内容:✧在实现查找的时候,首先要判断该顺序表是否为空,其次要判断查找后的结果(查到时输出查到的数据,未查到时给出相关提示)。

✧在实现插入的时候,由于是链式存储,它可以随机产生和回收存储空间,所以它不要判断线性表是否为满,但仍需判断要插入的位置是否合法,原因同实验一,其次要注意插入的时候语句的顺序不可颠倒,否则出错。

例如:ps所指向结点要插入在p所指向的结点之后,则:正确形式:s->next=p->next; p->next=s;错误形式:p->next=s;s->next=p->next(因为此时p->next已经指向s了)在实现删除的时候,首先要判断线性表是否为空,为空则不能删除;其次在删除后要回收空间。

例如:删除如上图所示s所指向的结点p->next=p->next->next;free(s);4. 链表部分相关操作代码:⑴单链表的结构定义:#include <stdio.h>typedef int elemtype;typedef struct lnode{ elemtype data;struct lnode *next;}*linklist;⑵建立单链表的算法int n; /*n作为整个程序的全局变量*/linklist *creat(void){ linklist *head, *p1, *p2;n=0;p1=p2=(linklist *)malloc(sizeof(linklist));scanf(“%d”,&p1->data);head=null;while(p1->data!=0){ n=n+1;if(n==1) head=p1;else p2->next=p1;p2=p1;p1=(linklist *)malloc(sizeof(linklist));scanf(“%d”,&p1->data);}p2->next=null;return(head);}⑶单链表的插入算法int insert(linklist *head, int i,elemtype e) { linklist *p, *s;int j;p=head; j=0;while(p && j<i-1){ p=p->next;++j;}if(!p||j>i-1){ printf(“无法插入”);return 0;}s=(linklist *)malloc(sizeof(lnode));s->data=e;s->next=p->next;p->next=s;return 1;}⑷单链表的删除算法int deltree(linklist *head,int i,elemtype e){ linklist *p, *q;int j;lp=head; j=0;while(p->next && j<i-1){ p=p->next;++j;}if(!(p->next)||j>i-1){ printf(“无法删除”);return 0;}q=p->next;p->next=q->next;e=q->data;free(q);return 1;}三、实验内容1. /*函数link()的功能是将带头结点的单链表l2链接到l1的后面,程序中存在几处错误,请改正并调试运行*/#include "linklist.h"void link(linklist l1,linklist l2){linklist p,q;p=l1;while (p->next)p=q->next;q=l2;p->next=q;free(l2);}void main(){ linklist l1,l2;l1=creat2(); /*生成带头结点的单链表l1*/print(l1); /*输出单链表l1*/l2=creat2(); /*生成带头结点的单链表l2*/print(l2); /*输出单链表l2*/link(l1,l2); /*将单链表l2链接到l1的后面*/print(l1); /*输出单链表l1*/}2./* 编写一个函数perm,将带头结点单链表中的所有值为奇数的结点集中到链表的左边,值为偶数的结点集中到链表的右边*/#include "linklist.h"linklist perm(linklist head){linklist pre,p;pre=head;p=head->next;while (p && p->data%2==1){ pre= p ;p= p->next ;}while (p){ if (p->data%2==1){ pre->next=p->next;p->next=head->next;head->next=p;p=pre->next;}else{ pre=p;p=p->next;}}}/*主函数,请勿改动,请将perm中的函数补充完整*/int main(){ linklist head;head=creat2(); /*尾插法建立单链表*/print(head); /*输出单链表head*/perm(head);print(head);delList(head);return 0;}3.设计程序:/*建立一个带头结点的单链表,然后将该链表进行倒置。

实现双向链表的基本操作

实现双向链表的基本操作

一、什么是双向链表双向链表,也称双向链式线性表,是一种常见的数据结构,它由多个节点组成,每个节点都包含指向前一个节点和后一个节点的指针。

与单向链表不同的是,双向链表的每个节点都有两个指针,这样可以在不遍历整个链表的情况下,方便地访问任何一个节点。

二、基本操作1. 初始化初始化双向链表需要定义一个头节点,该节点不存储实际数据,只用于标记链表的开始位置。

头节点的前驱指针和后继指针都指向NULL ,表示链表为空。

2. 插入节点插入节点是双向链表最常见的操作,它分为三种情况:在链表头部插入、在链表尾部插入和在链表中间插入。

头部插入:先创建一个新节点,并将该节点的后继指针指向当前头节点,将头节点的前驱指针指向新节点,再将链表的头节点指向新节点,完成插入。

尾部插入:先找到链表的尾节点,再创建一个新节点,并将新节点的前驱指针指向当前尾节点,将尾节点的后继指针指向新节点,再将新节点的后继指针指向 NULL ,完成插入。

中间插入:先找到要插入节点的位置,即该节点前一个节点的后继指针和后一个节点的前驱指针,分别指向新节点。

新节点的前驱指针指向前一个节点,后继指针指向后一个节点,完成插入。

3. 删除节点删除节点也分为三种情况:删除头节点、删除尾节点和删除中间节点。

头节点删除:将头节点的后继节点作为新的头节点,将新头节点的前驱指针指向 NULL ,完成删除。

尾节点删除:将尾节点的前驱节点作为新的尾节点,将新尾节点的后继指针指向 NULL ,完成删除。

中间节点删除:先找到要删除节点的位置,即该节点前一个节点的后继指针和后一个节点的前驱指针,分别指向该节点的前一个节点和后一个节点,完成删除。

4. 查找节点查找节点可以通过遍历链表来实现,在双向链表中,由于每个节点都有前驱和后继指针,因此可以从前向后或从后向前进行查找。

需要注意的是,由于双向链表的查找操作需要遍历整个链表,因此效率较低,不适用于大规模数据的查找。

5. 输出链表输出链表可以通过遍历链表来实现,从头节点开始,依次输出每个节点的数据。

双向链表报告

双向链表报告

数据结构实验报告实验名称:双向链表1.实验要求根据线性表的抽象数据类型定义,选择下面任意种链式结构实现线性表,并完成线性表基本功能。

双向链表1.构造:使用头插法尾插法两种方式。

2.插入:要求建立的链表按照关键字从小到大有序3.删除4.查找5.获取链表长度6.销毁7.编写main()函数测试线性表的正确性。

2. 程序分析2.1 存储结构存储结构:双向链表结构2.2 关键算法分析1.关键算法:(1)构造函数:首先运用冒泡排序法进行排序,操作将输入的数据排序,然后逐一将各个数据存放到各个节点之中。

(2)查找操作:形参为查找位数,从头遍历链表进行逐个节点查找,//p=p->next;最后返回值为所查找的节点的地址。

(3)插入操作:建立新节点,运用后插操作,根据所插入的数据的大小自动找到所需插入的位置,并进行后插操作,将该节点插入到查找数字之后。

(4)删除:查找得到被删除节点的前一个节点将该节点的next指针跳过该节点指向被删除节点的下一个节点2.算法步骤:1.插入操作:s->data=x;s->next=p->next;s->prior=p;p->next=s;p->next->prior=s后插(1)将数据值赋给节点数据域(2)将s节点的next指向p节点的下一个节点(3)s的前驱指针指向p(4)p的next指针指向s(5)p的下一个节点的前驱指针指向s2.删除操作:p=Get(i-1);Node<T>*q=p->next;p->next=q->next;q->next->prior=p;T x=q->data;(1)查找得到所删除节点的前一个节点(2)创建新的节点指向将被删除的节点(3)查找所的节点的next指针指向被删除节点的下一个节点(4)将被删除节点的下一个节点的前驱指针指向查找所得节点3. 程序运行结果1.主函数流程2.(1)插入时:首先自动的判断所需输入数字的位置然后建立新的节点,然后进行后插入操作。

双向链表

双向链表

第8讲 双向链表● 循环单链表的出现,虽然能够实现从任一结点出发沿着链能找到其前趋结点,但时间耗费是O (n) 。

● 如果希望从表中快速确定某一个结点的前趋,另一个解决方法就是在单链表的每个结点里再增加一个指向其前趋的指针域prior 。

这样形成的链表中就有两条方向不同的链,我们称之为双向链表。

● 双向链表的结构定义如下:typedef struct DNode{ ElemType data ;struct DNode *prior ,*next ;}DNode, * DoubleList ;● 双向链表的结点结构如图所示。

图:双链表的结点结构注:● 双向链表也是由头指针唯一确定的,● 增加头结点能使双链表的某些运算变得方便● 由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。

● 设指针p 指向双链表中某一结点,则有下式成立:p->prior->next = p = p->next->prior●在双向链表中,那些只涉及后继指针的算法,如求表长度、取元素、元素定位等,与单链表中相应的算法相同,● 但对于前插和删除操作则涉及到前驱和后继两个方向的指针变化,因此与单链表中的算法不同。

1、 双向链表的前插操作【算法思想】欲在双向链表第i 个结点之前插入一个的新的结点,则指针的变化情况如图所示:… p …s->prior=p->prior; ①p->prior->next=s;②s->next=p; ③p->prior=s;④【算法描述】int DlinkIns(DoubleList L,int i,ElemType e){DNode *s,*p;… /*先检查待插入的位置i是否合法(实现方法同单链表的前插操作)*/… /*若位置i合法,则找到第i个结点并让指针p指向它*/s=(DNode*)malloc(sizeof(DNode));if (s){ s->data=e;s->prior=p->prior; ①p->prior->next=s; ②s->next=p; ③p->prior=s; ④r eturn TRUE;}else return FALSE;}2、双向链表的删除操作【算法思想】欲删除双向链表中的第i个结点,则指针的变化情况如图所示:p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);【算法描述】int DlinkDel(DoubleList L,int i,ElemType *e){DNode *p;… /*先检查待插入的位置i 是否合法(实现方法同单链表的删除操作)*/… /*若位置i 合法,则找到第i 个结点并让指针p 指向它*/*e=p->data;p->prior->next=p->next; ①p->next->prior=p->prior; ②free(p);return TRUE;}3、 双向循环链表双向链表可以有循环表,称为双向循环链表。

双向链表排序实验报告

双向链表排序实验报告

双向链表排序实验报告陈祎智实验报告<2> 1. 问题描述: 双向链表的排序。

要求:输入一个双向链表,显示些双向链表并对此双向链表排序2.课题分析(结构图):3.数据结构的设计: typedef struct node { int info;struct node *llink,*rlink;}NODE; 双向链表的排序双向链表存储结构快速排序定义输入数据结点4.流程图5.源程序:#include<iostream.h> #include<stdlib.h> #include <stdio.h> 开始创建链表初始化链表从中间分成两部排序链表插入 10 个值输出排序链表终止typedef struct Link/*双向链表结构体*/ {int data;struct Link *lift;struct Link *right; }linkx,*linky; linky Init();/*建立双向链表*/ void PrLink(linky p);/*输出双向链表*/ linky Sort(linky head);/*对双向链表排序*/ linky S head,linky one,linky two);/*任意交换双向链表两个结点的地址*/ void main(void) {linky head;head=Init();head=Sort(head);PrLink(head); } linky (Init())/*建立链表*/ {linky p,q,head;int n=0;head=p=q=(linky)malloc(sizeof(linkx));printf(“排序前的链表: “);scanf(“%d“,&amp;p->data);/*输入数据*/head->lift=NULL;n++;while(n!=10)/*一直输入到规定的数字个数停止*/ {q=p;p=(linky)malloc(sizeof(linkx));scanf(“%d“,&amp;p->data);/*输入数据*/q->right=p;p->lift=q;n++; }p->right=NULL;return(head); } linky S head,linky one,linky two)/*任意交换两个结点*/ {linky temp;if(one->lift==NULL&amp;&amp;two->right==NULL)/*首和尾巴的交换*/{if(one->right==two)/*只有两个结点的情况下*/{two->right=one;two->lift=NULL;one->lift=two;one->right=NULL;head=two;}else/*有间隔的首尾交换*/{one->right->lift=two;two->lift->right=one;two->right=one->right;one->lift=two->lift;two->lift=one->right=NULL;head=two;/*尾结点成为头结点*/}}else if(two->right==NULL)/*尾和任意一个交换*/ {if(one->right==two)/*交换最后两个结点*/{one->lift->right=two;two->lift=one->lift;two->right=one;one->lift=two;one->right=NULL;}else/*和前面其他结点交换*/{temp=two->lift;temp->right=one;one->lift->right=two;one->right->lift=two;two->lift=one->lift;two->right=one->right;one->lift=temp;one->right=NULL;}}else if(one->lift==NULL)/*头和任意一个交换*/ {if(one->right==two)/*交换头两个结点*/{two->right->lift=one;one->right=two->right;one->lift=two;two->right=one;two->lift=NULL;head=two;}else/*头结点和后面其他结点交换*/{temp=one->right;temp->lift=two;one->lift=two->lift;one->right=two->right;two->lift->right=one;two->right->lift=one;two->right=temp;two->lift=NULL;head=two;/*交换的结点成为头结点*/}}else/*当中的任意两个交换*/{if(one->right==two)/*交换连在一起的两个结点*/ {temp=one->lift;one->lift->right=two;one->right->lift=two;one->lift=two;one->right=two->right;two->right->lift=one;two->right=one;two->lift=temp;else/*交换隔开的两个结点*/{one->lift->right=two;one->right->lift=two;one->lift=two->lift;temp=one->right;one->right=two->right;two->lift->right=one;two->right->lift=one;two->right=temp;two->lift=one->lift;}}return(head); } linky Sort(linky head)/*对链表排序*/ {linky i,j,t,p;int max;p=head;for(i=p;i->right!=NULL;i=i->right)/*用选择法的思想对这些结点排序*/max=i->data;for(j=i->right;j!=NULL;j=j->right)if(j->data<max){max=j->data;t=j;}if(max!=i->data)/*假如没有找到比 i 小的结点*/{head=S);/*因为最终返回的是头结点,而头结点又有可能变化,所以每次头结点返回*/i=t;}}return(head); } void PrLink(linky p)/*输出链表*/ { linky q;printf(“排序后: “);do{q=p;printf(“%d “,p->data);p=p->right;free(q);/*释放输出结点*/}while(p!=NULL);}6.调试记录:第一次输入 136 134 158 123 197 124 156 170 103 101 实现排序第二次调试输入 12367 15842 12564 13729 49875 1546 15423 15794 54612 15437.软件说明程序调试运行成功后,排序前随机输入十个不同的数值,快速排序后将由小到大输出这十个数值的排序。

双向链表及扩展

双向链表及扩展
{
PTail->next=p->prior;
delete p;
PTail->next->next=NULL;
}
}
else if(n==1&&Lsize!=1)
{
p=PHead->next;
PHead->next=p->next ;
delete p;
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
#include<iostream>
using namespace std;
#define NULL 0
#define flag PHead->next==NULL
class Node
{
Node *p;
p=GetAt(i);
p->value=j;
}
void CList::RemoveAt(int i)
{
Node *p;
int n=0;
if(i<=0||i>Lsize)
cout<<"DO NOT EXIST!"<<endl;
else
{
p=PHead;
{
public:
int value;
Node *prior;
Node *next;
Node(int i,Node *p,Node *n)
{
value=i;
prior=p;
next=n;
}
~Node()

双向链表

双向链表

准备:动态内存分配一、为什么用动态内存分配但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。

比如说我们要存储一个班级学生的某科分数,总是定义一个float型(存在0.5分)数组:float score[30];但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。

这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。

即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。

这种分配固定大小的内存分配方法称之为静态内存分配。

但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。

那么有没有其它的方法来解决这样的外呢体呢?有,那就是动态内存分配。

所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。

动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:1、不需要预先分配存储空间;2、分配的空间可以根据程序的需要扩大或缩小。

二、如何实现动态内存分配及其管理要实现根据程序的需要动态分配存储空间,就必须用到以下几个函数1、malloc函数malloc函数的原型为:void *malloc (unsigned int size)其作用是在内存的动态存储区中分配一个长度为size的连续空间。

其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。

还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。

双向链表的基本操作

双向链表的基本操作

双向链表的基本操作前⾯学习了如何创建⼀个,本节学习有关双向的⼀些基本操作,即如何在双向链表中添加、删除、查找或更改数据元素。

本节知识基于已熟练掌握双向链表创建过程的基础上,我们继续上节所创建的双向链表来学习本节内容,创建好的双向链表如图 1 所⽰:图 1 双向链表⽰意图双向链表添加节点根据数据添加到双向链表中的位置不同,可细分为以下 3 种情况:添加⾄表头将新数据元素添加到表头,只需要将该元素与表头元素建⽴双层逻辑关系即可。

换句话说,假设新元素节点为 temp,表头节点为 head,则需要做以下 2 步操作即可:1. temp->next=head; head->prior=temp;2. 将 head 移⾄ temp,重新指向新的表头;例如,将新元素 7 添加⾄双链表的表头,则实现过程如图 2 所⽰:图 2 添加元素⾄双向链表的表头添加⾄表的中间位置同添加数据类似,双向链表中间位置添加数据需要经过以下 2 个步骤,如图 3 所⽰:1. 新节点先与其直接后继节点建⽴双层逻辑关系;2. 新节点的直接前驱节点与之建⽴双层逻辑关系;图 3 双向链表中间位置添加数据元素添加⾄表尾与添加到表头是⼀个道理,实现过程如下(如图 4 所⽰):1. 找到双链表中最后⼀个节点;2. 让新节点与最后⼀个节点进⾏双层逻辑关系;图 4 双向链表尾部添加数据元素因此,我们可以试着编写双向链表添加数据的 C 语⾔代码,参考代码如下:1. line * insertLine(line * head,int data,int add){2. //新建数据域为data的结点3. line * temp=(line*)malloc(sizeof(line));4. temp->data=data;5. temp->prior=NULL;6. temp->next=NULL;7. //插⼊到链表头,要特殊考虑8. if (add==1) {9. temp->next=head;10. head->prior=temp;11. head=temp;12. }else{13. line * body=head;14. //找到要插⼊位置的前⼀个结点15. for (int i=1; i<add-1; i++) {16. body=body->next;17. }18. //判断条件为真,说明插⼊位置为链表尾19. if (body->next==NULL) {20. body->next=temp;21. temp->prior=body;22. }else{23. body->next->prior=temp;24. temp->next=body->next;25. body->next=temp;26. temp->prior=body;27. }28. }29. return head;30. }双向链表删除节点双链表删除结点时,只需遍历链表找到要删除的结点,然后将该节点从表中摘除即可。

双向链表——精选推荐

双向链表——精选推荐

双向链表在计算机科学中, ⼀个双向链表(doubly linked list) 是由⼀组称为节点的顺序链接记录组成的链接数据结构。

每个节点包含两个字段,称为链接,它们是对节点序列中上⼀个节点和下⼀个节点的引⽤。

开始节点和结束节点的上⼀个链接和下⼀个链接分别指向某种终⽌节点,通常是前哨节点或null,以⽅便遍历列表。

如果只有⼀个前哨节点,则列表通过前哨节点循环链接。

它可以被概念化为两个由相同数据项组成的单链表,但顺序相反。

两个节点链接允许在任⼀⽅向上遍历列表。

在双向链表中进⾏添加或者删除节点时,需做的链接更改要⽐单向链表复杂得多。

这种操作在单向链表中更简单⾼效,因为不需要关注⼀个节点(除第⼀个和最后⼀个节点以外的节点)的两个链接,⽽只需要关注⼀个链接即可。

基础操作的伪代码插⼊Add(value)Pre: value is the value to add to the listPost: value has been placed at the tail of the listn ← node(value)if head = øhead ← ntail ← nelsen.previous ← tailtail.next ← ntail ← nend ifend Add下⾯是注释:Add(value)Pre: value is the value to add to the listPost: value has been placed at the tail of the list//value是要被添加到双向链表中的值//value会被添加到双向链表的尾部n ← node(value)//value转换为节点对象赋值给nif head = ø//如果头结点是空节点,说明双向链表没有节点,那么头指针和尾指针都指向nhead ← ntail ← nelse//如果双向链表不是空链表,n的上⼀个节点指向尾节点,尾节点的下⼀个节点指向n,尾指针指向nn.previous ← tailtail.next ← ntail ← nend ifend Add删除Remove(head, value)Pre: head is the head node in the listvalue is the value to remove from the listPost: value is removed from the list, true; otherwise falseif head = øreturn falseend ifif value = head.valueif head = tailhead ← øtail ← øelsehead ← head.Nexthead.previous ← øend ifreturn trueend ifn ← head.nextwhile n = !ø and value != n.valuen ← n.nextend whileif n = tailtail ← tail.previoustail.next ← øreturn trueelse if n != øn.previous.next ← n.nextn.next.previous ← n.previousreturn truereturn falseend Remove下⾯是注释:Remove(head, value)Pre: head is the head node in the listvalue is the value to remove from the listPost: value is removed from the list, true; otherwise false//head是双向链表的头结点,value是需要被删除的值//value从双向链表中被删除,返回true,否则返回falseif head = ø//如果头结点是空节点,返回falsereturn falseend ifif value = head.value//如果头结点的值和value相等if head = tail//如果头尾相等,说明只有⼀个节点,头尾指针都置空head ← øtail ← øelse//否则头指针指向头节点的下⼀个节点,新的头节点的前⼀个节点是空节点head ← head.Nexthead.previous ← øend ifreturn true//删除头结点成功,返回trueend ifn ← head.next//如果不删除头结点,头结点的下⼀个节点赋值给nwhile n != ø and value != n.value//如果n不是空节点,并且n节点的值和value不相等就⼀直往后遍历 n ← n.nextend whileif n = tail//如果n是尾节点tail ← tail.previous//尾指针指向尾节点的前⼀个节点tail.next ← ø//新尾节点的下⼀个节点置空return true//删除尾节点成功,返回trueelse if n != ø//如果n不是空节点n.previous.next ← n.next//n的下⼀个节点赋值给n上⼀个节点的next指针n.next.previous ← n.previous//n的上⼀个节点赋值给n的下⼀个节点的previous指针return true//n节点删除成功,返回trueend ifreturn false//什么也没有删除,返回falseend Remove反向遍历ReverseTraversal(tail)Pre: tail is the node of the list to traversePost: the list has been traversed in reverse ordern ← tailwhile n != øyield n.valuen ← n.previousend whileend Reverse Traversal下⾯是注释:ReverseTraversal(tail)Pre: tail is the node of the list to traversePost: the list has been traversed in reverse order//tail是反向遍历的起始位置,默认是尾节点n ← tail//tail节点赋值给nwhile n != ø//如果n不是空节点,往前⾯遍历yield n.value//⽣成当前节点的值n ← n.previous//遍历到前⼀个节点end whileend Reverse Traversal复杂度时间复杂度获取:O(n)查询:O(n)插⼊:O(1)删除:O(1)空间复杂度O(n)DoublyLinkedList.jsimport DoublyLinkedListNode from './DoublyLinkedListNode';import Comparator from '../../utils/comparator/Comparator';export default class DoublyLinkedList {//双向链表类/*** @param {Function} [comparatorFunction]*/constructor(comparatorFunction) {//构造函数,comparatorFunction可以传递⾃定义的⽐较⽅法/** @var DoublyLinkedListNode */this.head = null;//双向链表的头指针/** @var DoublyLinkedListNode */this.tail = null;//双向链表的尾指针pare = new Comparator(comparatorFunction);//compare属性,⽐较⼯具类Comparator对象,上⾯有各种⽐较⽅法}/*** @param {*} value* @return {DoublyLinkedList}*/prepend(value) {//插⼊新节点作为头节点// Make new node to be a head.const newNode = new DoublyLinkedListNode(value, this.head);//将value值转成双向链表节点对象// If there is head, then it won't be head anymore.// Therefore, make its previous reference to be new node (new head).// Then mark the new node as head.if (this.head) {//如果原本有头结点,新头结点的previous指针指向新头结点this.head.previous = newNode;}this.head = newNode;//双向链表的头指针指向新头结点// If there is no tail yet let's make new node a tail.if (!this.tail) {//如果没有尾节点,说明⽬前为⽌只有⼀个节点,则尾指针指向新节点this.tail = newNode;}return this;//返回当前双向链表对象}/*** @param {*} value* @return {DoublyLinkedList}*/append(value) {//插⼊新节点作为尾节点const newNode = new DoublyLinkedListNode(value);//将value值转成双向链表节点对象// If there is no head yet let's make new node a head.if (!this.head) {//如果双向链表⼀个节点都没有,那么这个新节点既是头结点也是尾节点this.head = newNode;this.tail = newNode;return this;//返回当前双向链表对象}// Attach new node to the end of linked list.this.tail.next = newNode;//旧尾节点的next指针指向新节点// Attach current tail to the new node's previous reference.newNode.previous = this.tail;//新节点的previous指针指向旧尾节点// Set new node to be the tail of linked list.this.tail = newNode;//双向链表的尾指针指向新节点return this;//返回当前双向链表对象}/*** @param {*} value* @return {DoublyLinkedListNode}*/delete(value) {//删除节点if (!this.head) {//如果没有节点,返回nullreturn null;}let deletedNode = null;//即将被删除的节点let currentNode = this.head;//遍历时⽤到的当前节点while (currentNode) {//如果当前节点存在,就往后遍历if (pare.equal(currentNode.value, value)) {//如果当前节点的值与value相等,说明当前节点需要被删除deletedNode = currentNode;if (deletedNode === this.head) {//如果头结点需要被删除的情况// If HEAD is going to be deleted...// Set head to second node, which will become new head.this.head = deletedNode.next;//头结点的下⼀个节点赋值给头指针// Set new head's previous to null.if (this.head) {//如果新头节点存在,那么它的previous指针指向空节点this.head.previous = null;}// If all the nodes in list has same value that is passed as argument// then all nodes will get deleted, therefore tail needs to be updated.if (deletedNode === this.tail) {//如果整个双向链表只有⼀个节点,那么尾指针也置空this.tail = null;}} else if (deletedNode === this.tail) {//尾节点需要被删除的情况// If TAIL is going to be deleted...// Set tail to second last node, which will become new tail.this.tail = deletedNode.previous;//尾指针指向当前尾节点的上⼀个节点this.tail.next = null;//新尾节点的next指针指向空节点} else {//中间的节点需要被删除的情况// If MIDDLE node is going to be deleted...const previousNode = deletedNode.previous;//存下被删节点的前⼀个节点const nextNode = deletedNode.next;//存下被删节点的下⼀个节点previousNode.next = nextNode;//将被删节点的上⼀个节点和下⼀个节点连起来nextNode.previous = previousNode;}}currentNode = currentNode.next;//继续往下⼀个节点遍历}return deletedNode;//返回最后⼀个被删的节点}/*** @param {Object} findParams* @param {*} findParams.value* @param {function} [findParams.callback]* @return {DoublyLinkedListNode}*/find({ value = undefined, callback = undefined }) {//查找value值对应的节点if (!this.head) {//如果是空链表,返回nullreturn null;}let currentNode = this.head;//遍历时⽤到的当前节点,从头节点开始while (currentNode) {//当前节点存在,往后遍历// If callback is specified then try to find node by callback.if (callback && callback(currentNode.value)) {//如果指定了callback,就⽤callback来寻找节点return currentNode;//当前节点的值符合callback,返回当前节点}// If value is specified then try to compare by value..if (value !== undefined && pare.equal(currentNode.value, value)) {//如果没有callback就⽤默认的⽅法⽐较当前节点的值和value是否相等return currentNode;}currentNode = currentNode.next;//继续遍历下⼀个节点}return null;}/*** @return {DoublyLinkedListNode}*/deleteTail() {//删除尾节点if (!this.tail) {//如果没有尾节点,返回null// No tail to delete.return null;}if (this.head === this.tail) {//如果链表只有⼀个节点,头尾指针都置空,然后返回这个节点// There is only one node in linked list.const deletedTail = this.tail;this.head = null;this.tail = null;return deletedTail;}// If there are many nodes in linked list...const deletedTail = this.tail;//如果链表有多个节点this.tail = this.tail.previous;//尾指针指向旧尾节点的上⼀个节点this.tail.next = null;//新尾节点的next指针指向空节点return deletedTail;//返回被删除的尾节点}/*** @return {DoublyLinkedListNode}*/deleteHead() {//删除头节点if (!this.head) {//如果没有头结点,返回nullreturn null;}const deletedHead = this.head;//要被删除的节点if (this.head.next) {//如果链表有多个节点this.head = this.head.next;//头指针指向旧头结点的下⼀个节点this.head.previous = null;//新头结点的previous指针指向空节点} else {//如果链表只有⼀个节点,头尾节点都置空this.head = null;this.tail = null;}return deletedHead;// 返回被删除的头结点}/*** @return {DoublyLinkedListNode[]}*/toArray() {//将链表的值转换成⼀个数组const nodes = [];//结果数组let currentNode = this.head;//当前节点,从头结点开始while (currentNode) {//遍历链表nodes.push(currentNode);//结果数组push进当前节点currentNode = currentNode.next;//当前节点移动到下⼀个}return nodes;//返回数组}/*** @param {*[]} values - Array of values that need to be converted to linked list.* @return {DoublyLinkedList}*/fromArray(values) {//插⼊数组元素作为节点values.forEach(value => this.append(value));//循环数组,每⼀个元素变成链表节点插⼊到链表结尾return this;}/*** @param {function} [callback]* @return {string}*/toString(callback) {//返回⼀个数组,每⼀个元素是当前节点调⽤节点的toString⽅法返回的值,⽀持⾃定义callback return this.toArray().map(node => node.toString(callback)).toString();}/*** Reverse a linked list.* @returns {DoublyLinkedList}*/reverse() {//将链表顺序反转let currNode = this.head;//当前节点,从头结点开始let prevNode = null;//上⼀个节点let nextNode = null;//下⼀个节点while (currNode) {//遍历链表// Store next node.nextNode = currNode.next;//存下⼀个节点// Change next node of the current node so it would link to previous node. currNode.next = prevNode;//当前节点的next指针指向它的上⼀个节点// Move prevNode and currNode nodes one step forward.prevNode = currNode;//当前节点存为上⼀个节点currNode = nextNode;//当前节点存为下⼀个节点}// Reset head and tail.//重置头尾指针this.tail = this.head;this.head = prevNode;return this;}}DoublyLinkedListNode.jsexport default class DoublyLinkedListNode {//双向链表节点类constructor(value, next = null, previous = null) {//构造函数,value节点值,next下⼀个节点指针,previous前⼀个节点指针this.value = value;this.next = next;this.previous = previous;}toString(callback) {//返回节点的字符串类型值,⽀持回调函数处理return callback ? callback(this.value) : `${this.value}`;}}。

双向链表的名词解释

双向链表的名词解释

双向链表的名词解释在计算机科学中,双向链表是一种常用的数据结构,用于存储一系列元素。

与普通的单向链表不同,双向链表每个节点都包含两个指针,分别指向前一个节点和后一个节点,这样每个节点都可以从两个方向遍历。

双向链表的设计使得它在特定场景下具有独特的优势和灵活性。

一、双向链表的结构与操作双向链表通常由一个头节点和一个尾节点构成,这两个节点分别用于指向链表的第一个节点和最后一个节点。

每个节点都包含一个存储元素值的数据域和指向前一个和后一个节点的指针域。

除了常规的插入和删除操作外,双向链表还可以在任意位置插入或删除节点。

1. 插入操作:双向链表的插入操作类似于单向链表,需要通过遍历找到要插入位置的节点,然后将新节点的前后指针指向正确的节点,同时更新前后节点的指针指向新节点。

2. 删除操作:双向链表的删除操作也类似于单向链表,需要找到要删除的节点,然后将其前后节点的指针指向正确的节点,最后释放被删除节点的内存空间。

3. 遍历操作:双向链表的遍历可以从头节点开始,通过后继指针依次遍历到尾节点,或者从尾节点开始,通过前驱指针依次遍历到头节点。

这种双向遍历的方式在某些场景下更加高效,特别是需要反向查找或从尾部开始操作的情况。

二、双向链表的应用场景双向链表由于其特性,使得它在某些特定场景下非常有用,尤其是需要频繁插入、删除或者反向遍历操作的情况。

1. 缓存淘汰算法:LRU(Least Recently Used)是一种常见的缓存淘汰算法,在使用双向链表来实现LRU缓存淘汰策略时,可以通过双向链表的插入和删除操作来维护缓存的顺序,同时利用双向链表的反向遍历来快速定位最近最少使用的缓存项。

2. 字符串编辑器:文本编辑器通常使用双向链表来存储文本内容,在插入或删除字符时,只需要修改前后节点的指针即可完成操作,而无需移动其他字符。

3. 双向队列:双向链表可以用于实现双向队列(Deque),即两端都可以进行插入和删除操作的队列。

双向链表的基本操作和应用实验报告

双向链表的基本操作和应用实验报告

双向链表的基本操作和应用一、实验目的:1.掌握双向线性表的逻辑特征2.熟练掌握带头结点的双向链式表的指针操作、能完成双向链表的插入、删除、获取指定位序结点指针、遍历、和复杂应用;二、实验内容(双向链式线性表)1.熟悉Visual Studio 6.0,会设置断点,会查看运行过程中的指针情况;2.编写双向链表的插入、删除、获取指定位序的结点、遍历函数;3.实现将a1,a2,a3…an的双向链表转变为:a1,a3…a4,a2;三、实验结果#include <stdio.h>typedef struct DuLNode{char date;struct DuLNode *prior;struct DuLNode *next;}DuLNode,*DuLinkList;void DuLinkedListInsert_L(DuLinkList &L, int locate, char c){DuLNode* p=new DuLNode;p->date=c;DuLNode* q=L;for(int j=0;j<locate;j++)q=q->next;p->next=q->next;q->next->prior=p;p->prior=q;q->next=p;++L->date;}// DuLinkedListInsert_Lvoid DuLinkedListDelete_L(DuLinkList &L, int locate, char &e){DuLNode* q=L;for(int j=0;j<locate;j++)q=q->next;e=q->date;q->next->prior=q->prior;q->prior->next=q->next;--L->date;delete q;}// DuLinkedListDelete_Lvoid PrintfDuList(DuLinkList L){DuLNode*p=L->next;while(p!=L){printf("%2c",p->date);p=p->next;}printf("\n");}// PrintfDuListvoid DuLinkedListexchangete_L (DuLinkList &L){char e;DuLNode* q=L;for(int j=2;j< (L->date+3)/2;j++){DuLinkedListDelete_L(L, j, e);DuLinkedListInsert_L(L, L->date-j+2, e);}}// DuLinkedListexchangete_Lvoid main(){char c,e;int i,j;i=0;DuLNode* A=new DuLNode;A->date=0;A->next=A;A->prior=A;printf("请输入A单词,以回车结束:");scanf("%c",&c);while(c!=10){DuLinkedListInsert_L(A, i, c);i++;scanf("%c",&c);}printf("表A的元素个数为:%d\n",A->date);printf("带头结点的双向循环链表A的数据为:\n");PrintfDuList(A);DuLinkedListexchangete_L (A);PrintfDuList(A);printf("表A的元素个数为:%d\n",A->date);printf("请输入要删除在表中的位置:\n");scanf("%d",&j);DuLinkedListDelete_L(A,j,e);printf("被删除的数据为:%c\n",e);printf("被删除数据后的双向循环链表A的数据为:\n");PrintfDuList(A);printf("被删除数据后的双向循环链表A的元素个数为:%d\n",A->date);}四、实验中遇到的问题及解决方法遇到的问题:能不能利用修改原表实现数据重排功能。

线性表之双向链表

线性表之双向链表

线性表之双向链表⼀,双向链表的基础知识1.双向链表的概念 双向链表是在单链表的每个结点中,再设置⼀个指向其前驱结点的指针域。

所以在双向链表的每个结点中都有两个指针域,⼀个指向其前驱结点,⼀个指向其后继结点。

2.双向链表实现的难点每⼀个数据元素有两个指针域,⼀个指向其前驱结点,⼀个指向其后继结点。

第⼀个结点的前驱为NULL,最后⼀个节点的后继为NULL。

⼆,双向链表的实现1.双向链表的基本功能(DoubleLinkList.h)# ifndef DOUBLE_LINK_LIST# define DOUBLE_LINK_LIST/* 业务节点 */typedef void Node;/* 链表节点(被包含) */typedef struct DoubleNode{struct DoubleNode * prev;struct DoubleNode * next;}DoubleNode;/* 双向链表 */typedef struct DoubleLinkList{Node * ptr;DoubleNode header;DoubleNode slider;int length;}DoubleLinkList;/* 创建链表 */DoubleLinkList * createList();/* 清空链表 */void clear(DoubleLinkList * list);/* 销毁链表 */void destory(DoubleLinkList * list);/* 链表长度 */int length(DoubleLinkList * list);/* 链表是否为空 */int empty(DoubleLinkList * list);/* 插⼊链表 */void insert(DoubleLinkList * list, int pos, Node * node);/* 删除链表 */Node * del(DoubleLinkList * list, int pos);/* 删除某个元素 */Node * delNode(DoubleLinkList * list, Node * node);/* 获取链表 */Node * get(DoubleLinkList * list, int pos);/* 重置游标 */void resetSlider(DoubleLinkList * list);/* 游标下移 */Node * next(DoubleLinkList * list);/* 游标上移 */Node * prev(DoubleLinkList * list);/* 获取当前游标的结点 */Node * current(DoubleLinkList * list);# endif2.双向链表基本功能的实现(DoubleLinkList.c)# include<stdio.h># include<stdlib.h># include"DoubleLinkList.h"/* 创建链表 */DoubleLinkList * createList(){DoubleLinkList * list = (DoubleLinkList *)malloc(sizeof(DoubleLinkList)); /* 初始化头指针 */list->ptr = &(list->header);/* 初始化头结点 */list->header.next = NULL;list->header.prev = NULL;/* 初始化游标 */list->slider.next = NULL;/* 初始化链表长度 */list->length = 0;return list;}/* 清空链表 */void clear(DoubleLinkList * list){/* 置空头结点 */list->header.next = NULL;list->header.prev = NULL;/* 置空游标 */list->slider.next = NULL;/* 置空链表长度 */list->length = 0;}/* 销毁链表 */void destory(DoubleLinkList * list){if (list != NULL){free(list);list = NULL;}}/* 链表长度 */int length(DoubleLinkList * list){return list->length;}/* 链表是否为空 */int empty(DoubleLinkList * list){if (list->length <= 0){return1;}else {return0;}}/* 插⼊链表 */void insert(DoubleLinkList * list, int pos, Node * node){if (list == NULL){printf("链表为NULL\n");return;}/* 判断插⼊位置合法性 */pos = pos < 0 ? 0 : pos;pos = pos > length(list) ? length(list) : pos;/* 将业务结点转换为链表结点 */DoubleNode * _node = (DoubleNode *)node;/* 判断是否是第⼀次插⼊ */if (length(list) == 0){list->header.next = _node;_node->prev = NULL;_node->next = NULL;list->length++;return;}/* 判断是否是插⼊头部 */if (pos == 0){list->header.next->prev = _node;_node->next = list->header.next;list->header.next = _node;_node->prev = NULL;}else {DoubleNode * posNode = list->header.next; DoubleNode * previous = posNode;for (int i = 0; i < pos; i++){previous = posNode;posNode = posNode->next;}previous->next = _node;_node->prev = previous;posNode->prev = _node;_node->next = posNode;}list->length++;}/* 删除链表 */Node * del(DoubleLinkList * list, int pos){if (list == NULL){printf("链表为NULL\n");return NULL;}if (length(list) == 0){printf("链表长度为0,删除失败\n");return NULL;}/* 判断删除位置合法性 */pos = pos < 0 ? 0 : pos;pos = pos > length(list) ? length(list) : pos;/* 定义删除的返回结点 */DoubleNode * result = NULL;/* 判断是否删除第⼀个 */if (pos == 0){result = list->header.next;list->header.next = list->header.next->next; list->header.next->prev = NULL;}else {DoubleNode * posNode = list->header.next; DoubleNode * previous = posNode;for (int i = 0; i < pos; i++){previous = posNode;posNode = posNode->next;}result = posNode;previous->next = posNode->next;posNode->next->prev = previous;}list->length--;return result;}/* 删除某个元素 */Node * delNode(DoubleLinkList * list, Node * node) {/* 找出该元素位置 */int pos = -1;/* 遍历寻找 */for (int i = 0; i < length(list); i++){Node * tmp = get(list, i);if (tmp == node){pos = i;}}/* 如果未找到退出 */if (pos == -1){printf("未找到该元素\n");return NULL;}/* 找到后删除 */Node * result = del(list, pos);return result;}/* 获取链表 */Node * get(DoubleLinkList * list, int pos){DoubleNode * posNode = list->header.next; for (int i = 0; i < pos; i++){posNode = posNode->next;}return posNode;}/* 重置游标 */void resetSlider(DoubleLinkList * list){list->slider.next = list->header.next;}/* 游标下移 */Node * next(DoubleLinkList * list){if (list->slider.next->next != NULL){DoubleNode * result = list->slider.next; list->slider.next = list->slider.next->next;return result;}return NULL;}/* 游标上移 */Node * prev(DoubleLinkList * list){if (list->slider.next->prev != NULL){DoubleNode * result = list->slider.next; list->slider.next = list->slider.next->prev;return result;}return NULL;}/* 获取当前游标的结点 */Node * current(DoubleLinkList * list){return list->slider.next;}3.双向链表的测试# define _CRT_SECURE_NO_WARNINGS# include<stdio.h># include<string.h># include"DoubleLinkList.h"typedef struct Student{DoubleNode next;char name[64];int age;}Student;int main(){Student s1;strcpy(, "刘备");s1.age = 56;Student s2;strcpy(, "关⽻");s2.age = 40;Student s3;strcpy(, "张飞");s3.age = 38;/* 创建双向链表 */DoubleLinkList * list = createList();/* 插⼊数据 */insert(list, 0, &s1);insert(list, 0, &s2);insert(list, 0, &s3);/* 遍历 */printf("##############基本遍历##############\n");for (int i = 0; i < length(list); i++){Student * stu = (Student *)get(list,i);printf("name = %s,age = %d\n", stu->name, stu->age);}/* 删除 */del(list, 0);printf("##############删除0号位置后遍历##############\n"); for (int i = 0; i < length(list); i++){Student * stu = (Student *)get(list, i);printf("name = %s,age = %d\n", stu->name, stu->age);}/* 删除节点 */delNode(list,&s2);printf("##############删除s2后遍历##############\n");for (int i = 0; i < length(list); i++){Student * stu = (Student *)get(list, i);printf("name = %s,age = %d\n", stu->name, stu->age);}/* 重置游标 */resetSlider(list);Student * ss1 = (Student *)current(list);printf("##############重置游标##############\n");printf("name = %s,age = %d\n", ss1->name, ss1->age);/* 游标下移 */next(list);Student * ss2 = (Student *)current(list);printf("##############游标下移##############\n");printf("name = %s,age = %d\n", ss2->name, ss2->age);next(list);Student * ss3 = (Student *)current(list);printf("##############游标下移##############\n");printf("name = %s,age = %d\n", ss3->name, ss3->age);/* 游标上移 */prev(list);ss2 = (Student *)current(list);printf("##############游标上移##############\n");printf("name = %s,age = %d\n", ss2->name, ss2->age);}。

双向链表实验报告.概要

双向链表实验报告.概要

13软工转本1 钱剑滨实验报告双向链表实验报告信息工程系 13软工转本1 日期 2016年03月12日姓名钱剑滨学号 13131116 电话一、实验内容编写关于双向链表操作的C语言程序,要求包含双向链表的创建(生成)、输出(遍历)、查找、任意位置插入、有顺序插入、删除、等。

二、实验步骤1.分析操作所需思路,熟练运用单链表的各种特点。

2.编写程序,利用函数特性进行操作。

3.运行程序,纠正错误,对预测结果进行验证。

4.分析总结单链表的优缺点,并可以做到熟练掌握双向链表的各种操作。

三、设计概要1.本实验包含了7个函数:a)主函数main()b)创建链表函数Listcreate()c)数据输出函数Listprint()d)查找数据函数Listlocate()e)无序插入函数Listinsert_discretion ()f)有序插入函数Listinsert_orderg)删除数据函数Listdelete()2.明确函数的功能;a)Listcreate() 创建一个可控长度的斐波那契数列的双向链表。

b)Listprint() 将此链表中的数据全部输出。

c)Listlocate() 查找此链表中任意位置元素的值。

d)Listinsert_discretion ()向此链表的某个位置插入一组数据。

e)Listinsert_order() 向数列中插入一个数,使插入后的数列任然有序f)Listdelete() 将此链表的某个位置的一组数据删除。

四、程序设计1.函数前包含的头文件名、结点类型定义、全局变量和函数声明#include <stdio.h>#include <malloc.h>typedef struct number //定义结构体{struct number *prior;int num;struct number *next;}number;number *head; //定义全局变量头节点int k = 0; //k记录元素个数,防止溢出/*函数声明*/void Listcreate(); //建立链表void Listprint(); //全体输出void Listlocate(); //查找void Listinsert_discretion(); //任意位置插入void Listinsert_order(); //有顺序插入void Listdelete(); //删除2.主函数main()void main(){int select;printf("1、输入斐波那契数列的个数\n");printf("2、全部输出数列\n");printf("3、查找定位数列的元素\n");printf("4、添加数列元素\n");printf("5、插入顺序数列元素\n");printf("6、删除数列元素\n");printf("-------------------------------------\n");while (1){printf("请选择序号\n");scanf("%d", &select);switch (select){case 1: Listcreate(); break; //链表创建case 2: Listprint(); break; //全链表输出case 3: Listlocate(); break; //元素查找case 4: Listinsert_discretion(); break; //任意位置插入case 5: Listinsert_order(); break; //有顺序插入case 6: Listdelete(); break; //删除default: break;}}}3.创建链表函数Listcreate()void Listcreate() //建立链表{head = (number *)malloc(sizeof (number)); //开辟一个大小为number的内存空间给头节点head->next = NULL;head->prior = NULL;int a = 1,b = 1; //a、b为斐波那契数列的前两个元素int i;number *q;number *p = head; //p始终指向最后一个节点printf("输入元素的长度\n");scanf("%d",&i);/*给第一个元素赋值*/q = (number *)malloc(sizeof (number)); //开辟一个大小为number的内存空间给后面节点q->num = a;q->next = NULL; //新开辟的节点指向NULLp->next = q; //前一节点指向新开辟的节点q->prior = p; //开辟的节点前驱指向pp = p->next; //p指向新节点i = i - 1;k = k + 1;/*给第二个元素赋值*/q = (number *)malloc(sizeof (number));q->num = b;q->next = NULL;p->next = q;q->prior = p;p = p->next;i = i - 1;k = k + 1;/*给后续元素赋值*/while (i--)q = (number *)malloc(sizeof (number));q->num = (a+b);q->next = NULL;p->next = q;q->prior = p;p = p->next;a = a +b + b; //b = a - b; //将a+b的值付给b;b的值付给aa = a - b; //k = k + 1;}printf("链表建立成功\n");}4.数据输出函数Listprint()void Listprint() //全体输出{number *p = head->next;while (p != NULL){printf("%d ", p->num);p = p->next;}printf("\n");}5.查找数据函数Listlocate()void Listlocate() //查找{int i;number *p = head;printf("输入您要查询的元素位置\n");scanf("%d", &i);if ((i > k) || (i <= 0)){printf("查询的位置不存在\n");}else{while (i--){p = p->next;}printf("所查找数为%d\n", p->num);}}6.无序插入函数Listinsert_discretion()void Listinsert_discretion() //任意位置插入{int i;number *q;number *p = head;printf("输入您要插入哪个元素的后面\n");scanf("%d", &i);if ((i > k) || (i < 0)){printf("插入的位置不存在\n");}else{while (i--){p = p->next; //p最后指向要插入位置的前一个节点}q = (number *)malloc(sizeof (number));if (p->next == NULL) //插到末尾{q->next = NULL; //B的下一个元素指向空p->next = q; //p的下一个元素指向Bq->prior = p; //B的前驱指向p}else{q->next = p->next; //B的下一个元素指向p的下一个(两个不能反)p->next->prior = q; //p的下一个元素的前驱指向Bp->next = q; //p的下一个元素指向Bq->prior = p; //B的前驱指向p}printf("输入您要插入的数\n");scanf("%d", &(q->num));k = k + 1;}printf("插入成功\n");}7.有序插入函数Listinsert_order ()void Listinsert_order() //有顺序插入{number *q;number *p = head;q = (number *)malloc(sizeof (number));printf("输入您要插入哪个数\n");scanf("%d", &q->num);while (p->next){if (p->next->num >= q->num) //插在第一个比数大或等于的的数前{q->next = p->next;p->next->prior = q;p->next = q;q->prior = p;k = k + 1;break;}else{p = p->next;}}if (p->next == NULL) //插到末尾{q->next = NULL;p->next = q;q->prior = p;k = k + 1;}printf("插入成功\n");}8.删除数据函数Listdelete ()void Listdelete() //删除{int i;number *q;number *p=head;printf("输入您要删除哪个元素\n");scanf("%d", &i);if ((i > k) || (i <= 0)){printf("删除的位置不存在\n");}else{while (--i) //这里是先减再判断{p = p->next; //p指向被删除的元素的前一个元素}q = p->next; //q指向被删除的元素if (q->next == NULL) //删除最后一个元素{p->next = NULL;}else{p->next = q->next; //p的下一个元素指向q的下一个,即跳过被删除元素q->next->prior = p; //q的下一个元素的前驱指向p,也跳过被删除的元素}free(q); //清空删除元素k = k - 1;}printf("删除成功\n");}五、程序源码#include <stdio.h>#include <malloc.h>typedef struct number{struct number *prior;int num;struct number *next;}number;number *head; //定义全局变量头节点int k = 0; //k记录元素个数,防止溢出void Listcreate(); //建立链表void Listprint(); //全体输出void Listlocate(); //查找void Listinsert_discretion(); //任意位置插入void Listinsert_order(); //有顺序插入void Listdelete(); //删除void main(){int select;printf("1、输入斐波那契数列的个数\n");printf("2、全部输出数列\n");printf("3、查找定位数列的元素\n");printf("4、添加数列元素\n");printf("5、插入顺序数列元素\n");printf("6、删除数列元素\n");printf("-------------------------------------\n");while (1){printf("请选择序号\n");scanf("%d", &select);switch (select){case 1: Listcreate(); break;//链表创建case 2: Listprint(); break;//全链表输出case 3: Listlocate(); break; //元素查找case 4: Listinsert_discretion(); break; //任意位置插入case 5: Listinsert_order(); break; //有顺序插入case 6: Listdelete(); break; //删除default: break;}}}void Listcreate() //建立链表{head = (number *)malloc(sizeof (number)); //开辟一个大小为number 的内存空间给头节点head->next = NULL;head->prior = NULL;int a = 1,b = 1; //a、b为斐波那契数列的前两个元素int i;number *q;number *p = head; //p始终指向最后一个节点printf("输入元素的长度\n");scanf("%d",&i);q = (number *)malloc(sizeof (number)); //开辟一个大小为number的内存空间给后面节点q->num = a;q->next = NULL; //新开辟的节点指向NULLp->next = q; //前一节点指向新开辟的节点q->prior = p; //开辟的节点前驱指向pp = p->next; //p指向新节点i = i - 1;k = k + 1;q = (number *)malloc(sizeof (number));q->num = b;q->next = NULL;p->next = q;q->prior = p;p = p->next;i = i - 1;k = k + 1;while (i--){q = (number *)malloc(sizeof (number));q->num = (a+b);q->next = NULL;p->next = q;q->prior = p;p = p->next;a = a +b + b; //b = a - b; //将a+b的值付给b;b的值付给aa = a - b; //k = k + 1;}printf("链表建立成功\n");}void Listprint() //全体输出{number *p = head->next;while (p != NULL){printf("%d ", p->num);p = p->next;}printf("\n");}void Listlocate() //查找{int i;number *p = head;printf("输入您要查询的元素位置\n");scanf("%d", &i);if ((i > k) || (i <= 0)){printf("查询的位置不存在\n");}else{while (i--){p = p->next;}printf("所查找数为%d\n", p->num);}}void Listinsert_discretion() //任意位置插入{int i;number *q;number *p = head;printf("输入您要插入哪个元素的后面\n");scanf("%d", &i);if ((i > k) || (i < 0)){printf("插入的位置不存在\n");}else{while (i--){p = p->next; //p最后指向要插入位置的前一个节点}q = (number *)malloc(sizeof (number));if (p->next == NULL) //插到末尾{q->next = NULL; //B的下一个元素指向空p->next = q; //p的下一个元素指向Bq->prior = p; //B的前驱指向p}else{q->next = p->next; //B的下一个元素指向p的下一个(两个不能反)p->next->prior = q; //p的下一个元素的前驱指向Bp->next = q; //p的下一个元素指向Bq->prior = p; //B的前驱指向p}printf("输入您要插入的数\n");scanf("%d", &(q->num));k = k + 1;}printf("插入成功\n");}void Listinsert_order() //有顺序插入{number *q;number *p = head;q = (number *)malloc(sizeof (number));printf("输入您要插入哪个数\n");scanf("%d", &q->num);while (p->next){if (p->next->num >= q->num) //插在第一个比数大或等于的的数前{q->next = p->next;p->next->prior = q;p->next = q;q->prior = p;k = k + 1;break;}else{p = p->next;}}if (p->next == NULL) //插到末尾{q->next = NULL;p->next = q;q->prior = p;k = k + 1;}printf("插入成功\n");}void Listdelete() //删除{int i;number *q;number *p=head;printf("输入您要删除哪个元素\n");scanf("%d", &i);if ((i > k) || (i <= 0)){printf("删除的位置不存在\n");}else{while (--i) //这里是先减再判断{p = p->next; //p指向被删除的元素的前一个元素}q = p->next; //q指向被删除的元素if (q->next == NULL) //删除最后一个元素{p->next = NULL;}else{p->next = q->next; //p的下一个元素指向q的下一个,即跳过被删除元素q->next->prior = p; //q的下一个元素的前驱指向p,也跳过被删除的元素}free(q); //清空删除元素k = k - 1;}printf("删除成功\n");}六、测试结果1.最初运行程序时,有如下结果:2.在输入1后,创建长度为10的斐波那契数列,结果如下:3.在输入2后,显示数列,结果如下:4.在输入3后,进行查询操作后,结果如下:5.在输入4后,进行无序插入操作后,结果如下:6.在输入5后,进行有序插入操作后,结果如下:7.在输入6后,进行删除操作后,结果如下:8.输入其他数字的结果:七、总结反思刚做这个题目的时候发现自己结构体和链表学的太浅了,以前学得只是也不能很好的运用。

双向链表——精选推荐

双向链表——精选推荐

双向链表简介它的每个数据中都有两个,分别指向直接后继和直接前驱。

所以,从双向链表中的任意⼀个结点开始,都可以很⽅便地访问它的前驱结点和后继结点。

代码节点对象class Student2 {public int stuNo;public String name;//指向下⼀个节点public Student2 next;//指向上⼀个节点public Student2 prev;public Student2() {}public Student2(int stuNo, String name) {this.stuNo = stuNo; = name;}@Overridepublic String toString() {return "Student2{" +"stuNo=" + stuNo +", name='" + name + '\'' +'}';}}不考虑编号排序,直接添加class DoubleLinkedList {//初始化头节点,头节点不能动,不存放具体的数据private final Student2 head = new Student2(0,"");public Student2 getHead() {return head;}/*** 不考虑编号顺序,直接添加到最后* @param stu 添加的节点对象*/public void add(Student2 stu){Student2 temp = head;while (temp.next != null) {temp = temp.next;}stu.prev = temp;temp.next = stu;}/*** 显⽰所有节点*/public void show(){if(head.next == null){return;}Student2 temp = head.next;while (temp != null){System.out.println(temp);temp = temp.next;}}} Student2 stu1 = new Student2(1,"1号");Student2 stu2 = new Student2(2,"2号");Student2 stu3 = new Student2(3,"3号");Student2 stu4 = new Student2(4,"4号");DoubleLinkedList doubleLinkedList = new DoubleLinkedList(); doubleLinkedList.add(stu1);doubleLinkedList.add(stu2);doubleLinkedList.add(stu4);doubleLinkedList.add(stu3); doubleLinkedList.show();按照编号排序添加/*** @param stu 要添加的节点对象* 根据编号排序添加* 如果要添加的节点已经存在,给出提⽰*/public void addByNo(Student2 stu){Student2 temp = head;//标识要添加的学⽣是否存在boolean flag = false;while (temp.next != null){if(temp.next.stuNo == stu.stuNo){flag = true;break;}if(temp.next.stuNo > stu.stuNo){break;}temp = temp.next;}if(flag){System.out.printf("编号为%d的学⽣已经存在\n",stu.stuNo); }else {stu.next = temp.next;if(temp.next != null){temp.next.prev = stu;}temp.next = stu;stu.prev = temp;}} doubleLinkedList.addByNo(stu1);doubleLinkedList.addByNo(stu2);doubleLinkedList.addByNo(stu4);doubleLinkedList.addByNo(stu3);doubleLinkedList.addByNo(stu3);doubleLinkedList.show();修改/**** @param stu 要修改的学⽣对象*/public void update(Student2 stu){if(head.next == null){System.out.println("链表⽆数据");return;}boolean flag = false;Student2 temp = head;while (temp != null){if(temp.stuNo == stu.stuNo){flag = true;break;}temp = temp.next;}if(flag){ = ;}else {System.out.printf("编号为%d的学⽣不存在\n",stu.stuNo); }}doubleLinkedList.addByNo(stu1);doubleLinkedList.addByNo(stu2);doubleLinkedList.addByNo(stu4);doubleLinkedList.addByNo(stu3);doubleLinkedList.show();Student2 stu = new Student2(4,"00");doubleLinkedList.update(stu);System.out.println("修改后的数据");doubleLinkedList.show();删除/*** @param stuNo 根据学⽣编号删除*/public void delete(int stuNo){if(head.next == null){System.out.println("链表为空");return;}Student2 temp =head.next;//标识要删除的学⽣是否存在boolean flag = false;while (temp != null){if(temp.stuNo == stuNo){flag = true;break;}temp = temp.next;}if(flag){temp.prev.next = temp.next;//如果要删除的对象不是最后⼀个节点if(temp.next != null){temp.prev = temp.next.prev;}}else {System.out.printf("编号为%d的学⽣不存在\n",stuNo); }}doubleLinkedList.delete(4);doubleLinkedList.delete(1);doubleLinkedList.delete(1);System.out.println("删除后的数据=============="); doubleLinkedList.show();。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

实验名称:实验四双向或循环链表的基本操作一、实验项目名称:双向或循环链表的基本操作二、实验目的1)通过实验理解双向链表或循环链表的结构。

2)通过实验掌握双向链表或循环链表的基本操作。

三、实验基本原理1、数据结构2、算法思想1.构思:双向循环链表的创建建立在双向链表上,所以建立双向循环表每个结点都有三个属性:数据域、上个结点、下个结点,其中第一个结点的上一个结点是最后一个结点,最后一个结点的下一个结点就是第一个结点,所以就构成了双向循环链表。

双链表的单元类型定义Type struct DuLNode{Elemtype data;Struct DuLNode *prior;//建立表头结点Struct DuLNode *next;//建立表尾结点} DuLNode,*DuLinkList;3、算法描述①建立一个双链表,输入元素。

由p和s两个指针来存放元素。

以0判断是否结束,如果不为0 ,则继续,为0则截止。

s->data=e;s->next=p->next;s->prior=p;p->next->prior=s;p->next=s;p=s;②查找元素:首先判断位置是否合法(p->data!=e&& p->next!=head)若合法进行查找运算。

需要引用DuLinkList,(DuLinkList &L)输入要查找的元素的位置,判断双链表中是否有该元素,如果是则输出该元素,如果没有,则提示没有该元素。

③插入元素:判断位置是否合法(i<1||i>L.Length),若合法进行查插入运算。

需要引用DuLinkList,(DuLinkList &L)输入要插入的元素及其位置,插入数据然后输出数据。

④删除元素:判断位置是否合法(i<1||i>L.Length),若合法进行查删除运算。

输入要插入的元素及其位置,删除该数据。

本程序实现双链表的创建、查找、插入、删除、显示、菜单为主的六个函数组成。

四、主要仪器设备及耗材电脑运行环境:visual C++6.0五、实验步骤(1)打开visual C++,新建工程console application工程,新建文件C++ source file。

(2)输入自己的程序代码(3)如果有报错则修改程序。

无则组建调试六、实验数据及处理结果1、程序清单#include<stdio.h>#include<malloc.h>#include<stdlib.h>#define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 #define OK 1#define ERROR 0#define OVERFLOW -2typedef struct DuLnode{int data;Struct DuLNode *prior;//建立表头结点Struct DuLNode *next;//建立表尾结点int L;}DuLinkList;DuLinkList *ahead;int DuLinkListCreate(DuLinkList &L){ DuLinkList *p,*s;int x;ahead=(DuLinkList*)malloc(sizeof(DuLinkList));ahead->data=-1;ahead->next=ahead;ahead->prior=ahead;p=ahead;L.Length=0;printf("请输入元素,输入0结束\n");scanf("%d",&x);while(x!=0){s=(DuLinkList*)malloc(sizeof(DuLinkList));s->data=x;s->next=p->next;s->prior=p;p->next->prior=s;p->next=s;p=s;scanf("%d",&x);L.Length++;}return OK;}void DuLinkListDisplay(DuLinkList &L){DuLinkList * p=ahead->next;while(p!=ahead){printf("%d ",p->data);p=p->next;}printf("\n");}int DuLinkListLocate(DuLinkList &L,int e) //查找元素{ DuLinkList * p=ahead->next;int i=1;if(p==NULL)return ERROR;while(p->data!=e&& p->next!=ahead) {p=p->next;i++;}if(p->data==e)printf("查找的位置是:%d \n",i);elseprintf("并没有这个元素\n");return OK;}int DuLinkListInsert(DuLinkList &L,int i,int e) //插入元素 {int j;j=0;DuLinkList *p=ahead->next,*s;while((p->next!=ahead)&&(j<i-1)){p=p->next;j++;}if((i>0)&&(j=i-1)){s=(DuLinkList*)malloc(sizeof(DuLinkList));s->data=e;s->prior=p->prior;p->prior->next=s;s->next=p;p->prior=s;}DuLinkListDisplay(L);return OK;}int DuinkListDelet(DuLinkList &L,int i) //删除元素{int e,j=0;DuLinkList *p=head->next;while((p->next!=ahead)&&(j<i-1)) //找到第i个结点{p=p->next;j++;}if((i>0)&&(j==i))e=p->data;p->prior->next=p->next;p->next->prior=p->prior;free(p);Display_Dul(L);return OK;}int scan(){int a;do{printf("1. 输入元素 \n");printf("2. 查找 \n");printf("3. 插入\n");printf("4. 删除\n");printf("5. 显示数据\n");printf("6. 退出程序\n");printf("输入你想要的操作:");scanf("%d",&a);}while(a<0&&a>6);return (a);}int main(){DuLinkList L;for(;;) // 无限循环switch(scan()){int e,i;case 1:DuLinkListCreate(L);break;case 2:printf("请输入你想要查找的元素:");scanf("%d",&e);DuLinklistLocate(L,e);break;case 3:printf("请输入要插入的数的位置:");scanf("%d",&i);if(i<1||i>L.L){printf("输入不合法");break;}printf("请输入你要插入的元素:");scanf("%d",&e);DuLinkListInsert(L,i,e);break;case 4:printf("请输入你要删除的位置:");scanf("%d",&i);if(i<1||i>L.L){printf("输入不合法\n");break;}DuLinkListDelet(L,i);break;case 5:DuLinkListDisplay(L);break;}return 0;}2、运行结果①创建双向链表②查找元素③插入元素④删除元素七、思考讨论题或体会或对改进实验的建议在编程序的时候主要是创建双向链表的时候遇到了点困难,因为课本上没有,后来就查询了一下其他资料,然后又问了组长解决了这个问题。

我希望自己在做实验的过程中,通过查询资料,和同学交流能够提高我的写算法程序的能力。

八、参考资料。

相关文档
最新文档