//===================================== //===================================== //===========线性表的链式表示============= //===================================== //===================================== //与链式存储有关的术语 //-------------- //1、结点:数据元素的存储映像。由数据域与指针域两部分组成 //2、链表:n个结点由指针链组成一个链表(它时线性表的链式存储映像,称为线性表的链式存储结构) //3、单链表:结点只有一个指针域的链表 // 双链表:由两个指针域的链表称为双链表 // 循环链表:首尾相接的链表 //4、头指针:是指向链表中第一个结点的指针 // 首元结点:是指链表中存储第一个数据元素a1的结点 // 头结点:是再链表的首元结点之前附设的一个结点 //-------------- //****链表中不带头结点时,它的头指针是直接指向第一个数据元素 //****链表中带头节点时候,它的头指针是指向头结点,然后头结点的指针域再去指向第一个数据元素 //-------------- //===一、如何表示空表?=== //1、无头结点时,头指针为空时表示空表 //2、有头结点时,当头结点的指针域为空时表示空表 //--- //===二、在链表中设置头结点的好处=== //1、便于首元结点的处理(首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无需进行特殊处理) //2、便于空表和非空表的统一处理(无论链表是否为空,头指针都是指向头结点的非空指针,便于处理) //--- //===头结点的数据域内装的是什么?=== //可以为空,也可以存放线性表长度等附加信息,但是此结点不能计入链表的长度值 //============================= //*****链表的特点****** //1、结点在存储器中的位置任意,逻辑相邻但是物理不一定相邻 //2、访问**只能**通过头指针进入链表,并依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费时间不等 //**链表是顺序存取法 //**顺序表是随机存取法 //******************* //===================== //******链表的定义******* //===================== //========= /* * typedef struct LNode //声明结点的类型和指向结点的指针类型 * { * ElemType data; //结点的数据域 * struct LNode *next; //结点的指针域(一种嵌套的定义) * }LNode, *LinkList; //LinkList为指向结构体LNode的指针类型 * LNode代表的是一个结点,LinkList代表的是整个链表,这两个都是数据类型 */ //========= //定义链表L: LinkList L; //定义结点指针p: LNode *p; 和 LinkList p; 是等价的 //========= //===单链表应用实例=== //存储学生的学号、姓名、成绩的单链表结点类型定义如下: //-------- /* * typedef Struct student * { * char num[8]; //数据域 * char name[8]; //数据域 * int score; //数据域 * strut student *next; //指针域 * }LNode, *LinkList; */ //------- //*****比较常用的链表定义方法***** //------ /* * typedef Struct * { * char num[8]; * char name[8]; * int score; * }ElemType; * * typedef struct LNode * { * ElemType data; * struct LNode *next; * }LNode, *LinkList; * */ //------ //***************************** //===单链表的初始化(带头结点的单链表)=== //----- //1、生成新结点作头结点,用头指针L指向头结点 //2、将头结点的指针域置空 //----- //***算法描述*** /* * Status InitList_L(LinkList &L) * { * L = new LNode; //或者 L = (LinkList)malloc(sizeof(LNode)) * L->next = NULL; //将指针域置空 * return OK; * } */ //=== //===判断链表是否为空=== //----- //空表:链表中无元素,称为空链表(头指针和头结点仍存在,但是指针域是空的) //----- //***算法描述*** /* *int ListEmpty(LinkList L) //若L为空表则返回1,否则返回0 * { * if(L->next) //判断是否为空 * { * return 0; * } * else * { * return 1; * } * } */ //=== //===单链表的销毁=== //----- //链表销毁后不存在 //从头指针开始,依次释放所有结点 //结束条件:L == NULL; //循环条件:L != NULL; //销毁链表是从头结点开始,p = L //----- //***算法描述*** /* *如何让一个结点指向下一个结点 L = L -> next; delete p; *Status DestroyList_L(LinkList &L) * { * LNode *p; * while(L) * { * p = L; //p现在是存放的L的地址,后续用delete去删除,然后L依次向后移动,依次删除 * L = L->next; * delete p; * } * } */ //=== //===清空单链表=== //----- //链表仍存在,但是链表中无元素,成为空的链表(头指针和头结点仍然存在) //依次释放所有结点,并将头结点指针域设置为空 //清空链表是从首元结点开始,p = L-> next; //结束条件:P == NULL; //循环条件:P != NULL; //----- //***算法描述*** /* * Status ClearList(LinkList &L) //将L重置为空表 * { * LNode *p,*q; * p = L -> next; * while(p) //判断是否到了表尾 * { * q = p -> next; * delete p; * p = q; * } * L->next = NULL; //头结点指针域为空 * return OK; * } */ //=== //===求单链表的表长=== //----- //***算法表示*** /* * int ListLength_L(LinkList L) //返回L中数据元素的个数 * { * LinkList p; * p = L->next; //P指向第一个结点 * i = 0; * while(p) //遍历单列表,统计结点数 * { * i++ * p = p->next; * } * return i; * } */ //=== //===取值——取单链表中第i个元素的内容=== //----- //1、从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = L->next; //2、j做计数器,累计当前扫描过的结点数,j的初值为1 //3、当p指向扫描到的下一结点时,计数器j加1 //----- //***算法描述*** /* * Status GetElem_L(LinkList L, int i, ElemType &e) * //获取线性表L中的某个数据元素的内容,通过变量e返回 * { * p = L->next; //p指向首个元素 * j = 1; //初始化 * while(p && j < i) //向后扫描,直到p指向第i个元素或者p为空 * { * p = p->next; * ++j; * } * if(!p || j > i) * { * return ERROR; //第i个元素不存在 * } * e = p -> data; //取第i个元素 * return OK; * } */ //=== //===按值查找——根据指定数据获取该数据所在的位置(地址)=== //----- //1、从第一个结点起,依次和e相比较 //2、如果找到一个其值与e相等的数据元素,则返回其在链表中的位置或地址 //3、如果查遍整个链表都没有找到其值和e相等的元素,则返回0或者NULL //----- //***算法实现*** //一、返回的是地址 /* * LNode *LocateElem_L(LinkList L, ElemType e) * { * //在线性表L中查找值为e的数据元素 * //找到,则返回L中为e的元素的地址,查找失败返回NULL * p = L->next; * while(p && p->data!=e) * { * p = p->next; * } * return p; * } */ //二、返回的是位置序号 /* * //在线性表L中查找值为e的数据元素的位置序号 * int LocateElem_L(LinkList L, ElemType e) * { * //返回L中值为e的数据元素的位置序号,查找失败返回0 * p = L->next; * j = 1; * while(p && p->data != e) * { * p = p->next; * j++; * } * if(p) * { * return j; * } * else * { * return 0; * } * } */ //=== //===插入——在第i个结点前插入值为e的新结点=== //----- //1、首先找到ai-1个的存储位置p //2、生成一个数据域为e的新结点s //3、插入新节点:(1)新节点的指针域指向结点ai (2)结点ai-1的指针域指向新结点 //----- //***算法描述*** /* * //在链表L中第i个元素**之前**插入数据元素e * Status ListInsert_L(LinkList &L, int i, ElemType e) * { * p = L; * j = 0; * while(p && j < i - 1) //寻找第i-1个结点,p指向i-1结点 * { * p = p->next; * ++i; * } * if(!p || j > i-1) //i大于表长+1或者小于1,则插入位置违法 * { * return ERROR; * } * s = new LNode; //建立一个新的结点 * s->data = e; //将e的值存入这个结点的数据域 * s -> next = p -> next; //p是ai-1的指针域所指向的地址,p->next的地址是ai的地址,将s的指针域指向ai * p -> next = s; //再将p->next所指向的地址改为s这个结点的地址,成功插入 * } */ //=== //===删除——删除第i个结点 //----- //1、首先找到ai-1的存储位置p,保存要删除的a的值 //2、令p->next指向ai+1 //----- //***算法描述*** /* * Status ListDelete_L(LinkList &L,int i,ElemType &e) * { * p = L; * j = 0; * while(p->next && j < i-1) //寻找第i个结点,并令p指向其前驱 * { * p = p->next; * ++j; * } * if(!(p->next) || j > i-1) //判断删除的位置是否合理 * { * return ERROR; * } * q = p->next; //临时保存被删除的结点以备释放 * p->next = q->next; //改变删除结点前驱结点的指针域 * //令p->next = p->next->next;指向下下个结点 * e = q->data; //保存删除结点的数据域 * delete q; //释放删除结点的空间 * return OK; * } */ //=== //===建立单链表-头插法——元素插入再链表头部,也叫前插法=== //----- //1、从一个空表开始,重复读入数据 //2、生成新的结点,将读入数据存放到新结点的数据域中 //3、从最后一个结点开始,依次将各结点插入到链表的前端 //----- /* * void CreateList_H(LinkList &L,int n) * { * L = new LNode; * L -> next = NULL; //先建立一个带头结点的单链表 * for(int i = n; i > 0; --i) * { * p = new LNode; //生成新的结点 * cin >> p->data; //输入元素值 //scanf(&p->data); * p->next = L -> next; //插入到表头 * L -> next = p; * } * } */ //=== //===建立单链表-尾插法——元素插入在链表尾部也叫后插法=== //------ //1、从一个空表开始,将新结点诸葛插入到链表的尾部,尾指针r指向链表的尾结点 //2、初始时,r同l均指向头结点,每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点 //----- //***算法描述*** /* * //正位序输入n个元素的值,建立带表头的单链表L * void CreateList_R(LinkList &L, int n) * { * L = new LNode; * L -> next = NULL; * r = L; * for( i = 0; i<n; ++i) * { * p = new LNode; //生成新结点,输入元素的值 * cin >> p->data; * p->next = NULL; * r -> next = p; //插入到表尾 * r = p; //r指向新的尾结点 * } * } */ //=== //=================== //======循环链表====== //=================== //----- //定义:是一种头尾想借的链表 //即:表中最后一个结点的指针域指向头结点,整个链表形成一个环 //优点:从表中任一结点出发均可找到表中其他结点 //----- //***注意*** //由于循环链表中没用NULL指针,故涉及遍历操作时 //其终止条件不再像非循环链表那样判断p或p->next是否为空 //***而是判断其是否等于头指针*** //----- //表的操作常常实在表的首尾上进行 //------ //头指针表示单链表循环时不方便 //找a1的时间复杂度O(1) //找an的时间复杂度O(n) //----- //可以用尾指针来表示单循环链表 //a1的存储位置就是p->next->next //an的存储位置就是p //头结点的存储位置是p->next //时间复杂度O(1) //----- //=== //===带尾指针循环链表的合并=== //----- //从尾指针的视角去看 //----- //***算法描述*** /* * LinkList Connect(LinkList Ta, LinkList Tb) * //假设Ta,Tb都是非空的单循环链表 * p = Ta -> next; //p存表头结点 * Ta -> next = Tb -> next -> next; //Tb存表头连结Ta表尾 * delete Tb -> next; //释放Tb表头结点 * Tb -> next = p; //修改指针 * return Tb; */ //=== //=================== //======双向链表====== //=================== //----- //单链表的结点哟指示后继的指针域,找后继结点方便,时间复杂度O(1) //但是无指针前驱的指针域,找前驱结点困难,要从表头出发进行查找,时间复杂度是O(n) //----- //双向链表:在单链表的每个结点力再增加一个指向其直接前驱的指针域prior //这样链表中就形成了两个有方向不同的链,故称为双向链表 //=== //---对称性--- //p->prior ->next = p = p -> next ->prior; //就是p指针指向的前驱指向的后继就是p //p指针指向的后继的前驱也是p //----------- //***双链表的结构定义**** /* * typedef struct DuLNode * { * ElemType data; * struct DuLNode *prior, *next; * }DuLNode, *DuLinkList; */ //=== //==双向***循环***链表=== //----- //和单链的循环表类似,双向链表也可以有循环表 //让头结点的前驱指针指向链表的最后一个结点 //让最后一个结点的后继指针指向头结点 //----- //=== //===双向链表的插入=== //----- //***算法描述*** /* * //在带头结点的双向循环链表L中第i个位置之前插入元素e * void ListInsert_DuL(DuLinkList &L, int i, ElemType e) * { * if(!(p = GetElemP_DuL(L,I))) return ERROR; * s = new DuLNode; * s -> data = e; * s -> prior = p -> prior; //s要与之前的结点相连结,而之前结点的地址是记录在p的前驱之中的 * p -> prior ->next = s; //要让前一个结点的后继指针域指向s * s -> next = p; //这个插入的结点的后继指针域要去指向p * p -> prior = s; //p的前驱指针域也要指向s * } */ //=== //===双向链表的删除=== //--- //***算法描述*** /* * void ListDelete_Dul(DuLink &L, int i, ElemType &e) * { * if(!(p = GetElemP_Dul(L,i))) * { * return ERROR; * } * e = p -> data; * p -> prior -> next = p -> next; //前驱结点的尾指针域要指向下一个结点 * p -> next -> prior = p -> prior; //后继结点的头指针域要指向前一个结点 * delete p; * return OK; * } */ //=== //===================================== //===================================== //=============线性表的应用============== //===================================== //===================================== //===线性表的合并=== //----- //***算法描述*** /* * void union(List &La, List LB) * { * La_len = ListLength(La); * Lb_len = ListLength(Lb); * for(i = 1; i <= Lb_len; i++) * { * GetElem(Lb, i, e); * if(!LocateElem(La,e)) * { * ListInsert(&La, ++La_len, e); * } * } * } */ //=== //===有序表的合并=== //----- //已知线性表La和Lb中的数据元素按值非递减(其实是递增,但是可能会有相等所以称为非递减) //将两表合并为一个新的线性表Lc,且仍按值非递减有序排列 //--- //1、创建一个空表 //2、依次从La或Lb中摘取元素值较小的结点插入到Lc表的最后,直至其变为一个空表为止 //3、继续将La或Lb其中一个表的剩余结点插入在Lc的最后 //----- //==有序表合并---顺序表实现---=== //***算法描述*** /* * void MergeList_Sq(SqList LA, SqList LB, SqList &LC) * { * pa = LA.elem; * pb = LB.elem; //指针pa和pb的初值分别指向两个表的第一个元素 * LC.length = LA.length + LB.length; //新表长度为待合并两表的长度之和 * LC.elem = new ElemType[LC.length]; //为合并后的新表分配一个数组空间 * pc = LC.elem; //指针pc指向新表的第一个元素 * pa_last = LA.elem + LA.length - 1; //指针pa_last指向LA表的最后一个元素 * pb_last = LB.elem + LB.length - 1; //指针pa_last指向LB表的最后一个元素 * while(pa <= pa_last && pb <= pb_last) //需要判断两表是否都为空 * { * if(*pa <= *pb) * { * *pc++ = *pa++; //依次摘取两表中值较小的结点 * } * else * { * *pc++ = *pb++; * } * } * while(pa <= pa_last) * { * *pc++ = *pa++; //LB表已经到达表尾,将LA中剩余元素加入LC * } * while(pb <= pb_last) * { * *pc++ = *pb++; //LA表已经道道表尾,将LB中剩余元素加入LC * } * } */ //--- //===有序表合并——用链表实现=== //***算法描述*** /* *void MergeList_L(LinkList &La, LinkList &Lb,LinkList &Lc) * { * pa = La -> next; * pb = Lb -> next; * pc = Lc = La; //用La的头结点作为Lc的头结点 * while(pa && pb) * { * if(pa->data <= pb ->data) * { * pc -> next = pa; * pc = pa; * pa = pa -> next; * } * else * { * pc -> next = pb; * pc = pb; * pb = pb -> next; * } * } * pc -> next = pa?pa:pb; //插入剩余段 * delete Lb; //释放Lb的头结点 * } */ //=== //---案例分析与实现--- //===1、一元多项式的运算:实现两个多项式加减乘运算=== //--- //用数组存储会比较方便一些 //--- //实现两个多项式相加运算 //--- //=== //===2、稀疏多项式的运算=== //--- //---以顺序表数组去解决--- //存储两部分的内容 //系数 以及 指数 //创建一个新的数组c //分别从头遍历比较a和b的每一项 //指数相同,对应系数相加,其和不为0,则在c中新增一个新项 //指数不相同,则将指数较小的项复制到c中 //一个多项式已遍历完毕时,将另一个剩余项依次复制到c中即可 //--- //---以链表去解决--- //1、创建只有一个头结点的空链表 //2、根据多项式的项的个数n,循环n次执行以下操作: // 生成一个新结点*s // 输入多项式当前项的系数和指数赋给新结点*s的数据域 // 设置一前驱指针pre,用于指向待找到的第一个大于输入项指数的结点的前驱 // pre初值指向头结点 // 指针q初始化,指向首元结点 // 循链向下逐个比较链表中当前结点与输入项指数,找到第一个大于输入项指数的结点*q // 将输入项结点*s插入到结点*q之前 //----------------- //--链式存储结构--- /* * typedef struct PNode * { * float coef; //系数 * int expn; //指数 * struct PNode *next; //指针域 * } */ //用双指针分别去指向两个多项式,分别向后移动 //去判断指数是否相等若相等 //----------------- //***算法描述*** /* * void CreatePoly(Polynomial &P,int n) * { //输入m项的系数和指数,建立表示多项式的有序链表P * P = new PNode; * P -> next = NULL; //先建立一个带头结点的单链表 * for(i = 1; i <= n; ++i) //依次输入n个非零项 * { * s = new PNode; //生成新的结点 * cin >> s -> coef >> s -> expn; //输入系数和指数 * pre = P; //pre用于保存q的前驱,初值为头结点 * q = P -> next; //q初始化,指向首元结点 * while(q && q->expn < s->expn) //找到第一个大于输入项指数的项*q * { * pre = q; * q = q -> next; //将输入项s插入到q和其前驱结点pre之间 * } * s -> next = q; * pre -> next =s; * } * } */ //===
数据结构——链表
最新推荐文章于 2024-09-04 20:48:11 发布