数据结构——链表

//=====================================
//=====================================
//===========线性表的链式表示=============
//=====================================
//=====================================



//与链式存储有关的术语
//--------------
//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;
 *      }
 * }
 */
//===
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值