数据结构——线性表

线性表

【定义】

线性表( Linear List 是零个或多个具有相同类型的数据元素的有限序列

【实现】

线性表的顺序存储结构

简要说明

特点:线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,

作用:线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系

顺序存储的实现:一维数组存储顺序表中的数据

 

设顺序表的每个元素占用 c 个存储单元,则第 i 个元素的存储地址为:

     LOC ( a i ) = LOC ( a 1 ) + ( i - 1 ) × c

代码实现

#include<iostream>
using namespace std;
const int MaxSize=100;   

template <class T>
class SeqList{
private:
       T data[MaxSize]; /// 存放数据元素的数组
       int length; /// 线性表的长度
public:
       SeqList() ;/// 无参构造函数
       SeqList(T a[],int n ) ; /// 有参构造函数
       ~SeqList(){} /// 析构函数为空
       int Length () {return length;} /// 求线性表的长度
       T Get(int i); /// 按位查找,取线性表的第 i 个元素
       int Locate(T x) ; /// 按值查找,求线性表中值为 x 的元素序号
       void Insert (int i, T x); /// 在线性表中第 i 个位置插入值为 x 的元素
       T Delete (int i) ; /// 删除线性表的第 i 个元素
       void PrintList () ; /// 遍历线性表,按序号依次输出各元素
}; 
template <class T>
SeqList<T>::SeqList(T a[],int n){
       if(n>MaxSize)
              throw "参数非法" ;
       for(int i=0;i<n;i++)
              data[i]=a[i];
       length=n;
}
template <class T>
T SeqList<T>::Get(int i){
       if(i>MaxSize||i<1)
              throw "查找位置非法";
       else
              return data[i-1];     
}
template <class T>
int SeqList<T>::Locate(T v){
       for (int i=0; i<length; i++)
          if (data[i]==v) 
            return i+1 ;  ///下标为i的元素等于v,返回其序号i+1
       return -1;  ///查找无
}
template <class T>
void SeqList<T>::Insert(int i,T v){
       if(length>=MaxSize)throw "上溢";
       if(i>MaxSize+1||i<1)   ///易错点,有n+1个位置可以插入
              throw "插入位置非法";
       for(int j=length;j>=i;j++)
              data[j]=data[j-1];
       data[i-1]=v;
       length++;
}
template <class T>
T SeqList<T>::Delete(int i){
       if(length==0)throw "下溢";
       if(i>MaxSize||i<1)
              throw "删除位置非法";
       T v=data[i-1]; 
       for(int j=i-1;j<length-1;j++)
              data[j]=data[j+1];
       length--;
       return v;
}
template <class T>
void SeqList<T>::PrintList(){
       if(length==0)throw "线性表为空";
       for(int j=0;j<length;j++)
              cout<<data[j]<<" ";
       cout<<endl;
}

int main() {
       
       
       return 0;
}

插入和删除的时间复杂度分析

插入

过程:

最好情况:在最后插入,不需要移动元素,时间复杂性为O(1);

最坏情况:在开头插入,需要移动n个数据,O(n)

平均时间复杂度:

元素总个数为n,各个位置插入的概率相等为p=1/(n+1 )

平均移动元素次数为

时间复杂性为O(n)

删除

最好情况:在最后删除,不需要移动元素,时间复杂性为O(1);

最坏情况:在开头删除,需要移动n-1个数据,O(n)

平均复杂性

元素总个数为n,各个位置删除的概率为q=1/n

平均移动元素次数为

缺点

(1)插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低;

(2)由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配,因此当表长变化较大时,难以确定合适的存储规模

线性表的链式存储结构

特点

根据线性表的长度动态的申请存储空间,以解决顺序存储中存在的存储空间难以确定的问题,插入与删除操作的复杂度更低。

单链表实现

数据类型的定义

template <class T> //模板机制

struct Node

{

    T data;//数据

    Node<T> *next;//存放下一个节点的地址

};

总体功能

template <class T>

class List

{

    Node<T> *first;

public:

    List();

    List(T a[], int n);       //头插法

    List(T a[], int n, int k);//尾插法

    ~List();

    void PrintList();         //打印数据

    T del(int i);             //删除位置为i的节点信息,并返回删除的值 

    int del_x(T x);           //删除左边第一个=x的值,并返回位置

    int del_all_x(T x);       //删除全部=x的值

    T Find(int i);            //查询位置为i的值

    void Insert(int i, T x);   //在第i个位置插入x

    void Sort();               //从小到大排序,冒泡实现

    int Length();              //返回链表长度



};

单链表的构造

注意:s永远指向新节点

头插法

  1. 第一步:

  1. 第二步

 

  1. 第三步

我们发现插入首个元素和i个元素执行相同操作,古可以合并成一项

 

代码实现:

/*头插法*/

template <class T>

List<T>::List(T a[], int n)

{

    first = new Node<T>;

    first->next = NULL;//初始头节点为尾节点

    Node<T> *s;

    for (int i = 0; i < n; i++)

    {

       s = new Node<T>;

       s->data = a[i];//赋值

       s->next = first->next;//先链接后面的

       first->next = s;//生成新的首元节点

    }

}

尾插法

图示过程:

  1. 第一步

比上述头插法多了一个尾指针

  1. 第二步

执行语句:r->next = s;

                  r = s;

  1. 第三步

 

我们依旧可以发现插入首个元素和i个元素执行相同操作,可以合并成一项

 

 

代码实现:

/*尾插法*/

template <class T>

List<T>::List(T a[], int n, int k)

{

    Node<T> *s, *r;

    first = new Node<T>;

    r = first;//r记录尾节点的位置

    for (int i = 0; i < n; i++)

    {

       s = new Node<T>;

       s->data = a[i];//赋值

       r->next = s;//添加新的尾节点

       r = s;//更新尾节点

    }

    r->next = NULL;//尾节点赋NULL,易漏

}

头插法和尾插法总结

  1. 头插法和尾插法都在动态的维护一个指针,头插法:头节点,即first,尾插法:尾节点,即r。
  2. 头插法和尾插法,注意先链接远端的,在链接近端的。
  3. 头插法和尾插法,我们发现插入首个元素和i个元素执行相同操作,但具体以后还需要分析,如下面的例子。

 

 

不带头节点和尾节点的头插法和尾插法

不带头节点的头插法

template <class T>

List<T>::List(T a[], int n)

{

    first = NULL;//带头节点:first->next = NULL;

    Node<T> *s;

    for (int i = 0; i < n; i++)

    {

        s = new node<T>;

        s->data = a[i];

        s->next = first;

        first = s;//带头节点:first->next = s;

}

不带头节点的尾插法

template <class T>

List<T>::List(T a[], int n, int k)

{

    node<T> *r;

    first = NULL;

    if(n <= 0)return;

    s = new node<T>;

    //插入第一个节点,需要特殊对待

    s->data = a[0];

    s->next = first;

    first = s;

    r = first;



    for (int i = 1; i < n; i++) {

        s = new node<T>;

        s->data = a[i];

        r->next = s;

        r = s;

    }

}

分析

不带头节点的头插,跟带头节点的基本没有区别。但不带头节点的尾插却需要对第一个元素进行特判,因为他需要维护尾节点。

单链表的遍历

/*打印*/

template <class T>

void List<T>::PrintList() {

    Node<T> *p;

    p = first->next;//从第一个节点开始,不是头节点

    while (p)      //末尾节点的特点为NULL

    {

        cout << p->data << " ";

        p = p->next;

    }

    cout << endl;

}

单链表中按位置查找

/*按位置查找*/

template <class T>

T List<T>::Find(int i)

{

    Node<T> *p;

    p = first;

    int j = 0;

    //p = first->next;  j = 1;



    while (p&&j < i)

    {

        p = p->next;//指针后移

        j++;

    }

    if (!p)

        throw "查找位置出错";

    else

    {

        return p->data;

    }

}

复杂度分析:

查找算法的基本语句是工作指针 p 后移,该语句执行的次数与被查结点在表中的位置有关。

最坏O(n),最好O(1),平均时间性能为 O ( n ) 。

单链表的插入操作(按位置进行插入)

带头结点的单链表中插入结点

  1. 第一步

我们要在i插入一个元素,需要找到它的上一个节点:pre:i-1

  1. 第二步

依旧先更新远端的,注意一下顺序。代码实现:


/*插入*/

template <class T>

void List<T>::Insert(int i, T x) {

    Node<T> *p;

    p = first;

    int j = 0;//指针初始化

    while (p&&j < i - 1) ///找第i-1个的结点

    {

        p = p->next;//指针后移

        j++;

    }

    if (!p)

        throw "插入位置出错";

    else

    {

        Node<T> *temp = new Node<T>;

        temp->data = x;

        temp->next = p->next;//先将节点p的后继插入节点temp之后

        p->next = temp;//后将节点temp插入到节点p之后

    }

}

不带头结点的单链表中插入结点


template <class T>

void List<T>::Insert(int i, T x) {

    Node<T> *p;

    int j;

    //注意特判,插入为头节点需要特殊判断一下

    if(i <= 0) throw “位置非法”;

    if (i == 1 )

    {

        s = new Node<T>;

        s->next = first;

        first = s;

       return

    }



    p = first; j = 1;    //工作指针p初始化

    while (p && j < i - 1) {

        p = p->next;   //工作指针p后移

        j++;

    }

    if (!p) throw "位置";

    else {

        Node<T> *s;

        s = new Node<T>;

        s->data = x;  //向内存申请一个结点s,其数据域为x

        s->next = p->next;//先将节点p的后继插入节点s之后

        p->next = s; //后将节点s插入到节点p之后

    }

}

单链表中节点的删除(删除编号是i的结点)






/*删除位置为i的值*/

template <class T>

T List<T>::del(int i) {

    //if (i <= 0 || i > Length())  throw "删除位置出错"; 

    Node<T> *p, *temp;

    p = first;

    int j = 0;

    while (p&&j < i - 1) //找第i-1个的结点

    {

        p = p->next;

        j++;

    }

    if (!p||!p->next)//结点p不存在或结点p的后继结点不存在

        throw "删除位置出错";

    else

    {

        temp = p->next;

        T x = temp->data;//存一下被删除节点的值

        p->next = temp->next;//摘链

        delete temp;

        return x;

    }



}

注意一下:f (!p||!p->next)//结点p不存在或结点p的后继结点不存在

        throw "删除位置出错";

带头节点和不带头节点删除分析

结论:引入头结点,可以使插入和删除操作简单

析构函数


template <class T>

List<T>::~List()

{

    Node<T> *q;

    while (first)

    {

        q = first->next;//保存下一个节点

        delete first;

        first = q;

    }

}

单链表总体代码

template <class T>

struct Node

{

       T data;//数据

       Node<T> *next;//存放下一个节点的地址

};

template <class T>

class List

{

       Node<T> *first;

public:

       List();

       List(T a[], int n);       //头插法

       List(T a[], int n, int k);//尾插法

       ~List();

       void PrintList();         //打印数据

       T del(int i);             //删除位置为i的节点信息,并返回删除的值   

       int del_x(T x);           //删除左边第一个=x的值,并返回位置

       int del_all_x(T x);       //删除全部=x的值

       T Find(int i);            //查询位置为i的值

       void Insert(int i, T x);   //在第i个位置插入x

       void Sort();               //从小到大排序,冒泡实现

       int Length();              //返回链表长度



};

template <class T>

List<T>::List()

{

       first = new Node<T>;

       first->next = NULL;

}

/*头插法*/

template <class T>

List<T>::List(T a[], int n)

{

       first = new Node<T>;

       first->next = NULL;//初始头节点为尾节点

       Node<T> *s;

       for (int i = 0; i < n; i++)

       {

              s = new Node<T>;

              s->data = a[i];//赋值

              s->next = first->next;//注意先链接后面的

              first->next = s;//生成新的首元节点

       }

}

/*

不带头节点的头插法

template <class T>

List<T>::List(T a[], int n)

{

       first = NULL;//带头节点:first->next = NULL;

       Node<T> *s;

       for (int i = 0; i < n; i++)

       {

              s = new node<T>;

              s->data = a[i];

              s->next = first;

              first = s;//带头节点:first->next = s;

}

*/

/*尾插法*/

template <class T>

List<T>::List(T a[], int n, int k)

{

       Node<T> *s, *r;

       first = new Node<T>;

       r = first;//r记录尾节点的位置

       for (int i = 0; i < n; i++)

       {

              s = new Node<T>;

              s->data = a[i];

              r->next = s;//添加新的尾节点

              r = s;//更新尾节点

       }

       r->next = NULL;//尾节点赋NULL

}

/*

不带头节点的尾插法

template <class T>

List<T>::List(T a[], int n, int k)

{

       node<T> *r;

       first = NULL;

       if(n <= 0)return;

       s = new node<T>;

       //插入第一个节点,需要特殊对待

       s->data = a[0];

       s->next = first;

       first = s;

       r = first;



       for (int i = 1; i < n; i++) {

              s = new node<T>;

              s->data = a[i];

              r->next = s;

              r = s;

       }



}

*/

/*按位置查找*/

template <class T>

T List<T>::Find(int i)

{

       Node<T> *p;

       p = first;

       int j = 0;

       //p = first->next;  j = 1;



       while (p&&j < i)

       {

              p = p->next;//指针后移

              j++;

       }

       if (!p)

              throw "查找位置出错";

       else

       {

              return p->data;

       }

}

/*删除位置为i的值*/

template <class T>

T List<T>::del(int i) {

       Node<T> *p, *temp;

       p = first;

       int j = 0;

       while (p&&j < i - 1) //找第i-1个的结点

       {

              p = p->next;

              j++;

       }

       if (!p||!p->next)//结点p不存在或结点p的后继结点不存在

              throw "删除位置出错";

       else

       {

              temp = p->next;

              T x = temp->data;//存一下被删除节点的值

              p->next = temp->next;//摘链

              delete temp;

              return x;

       }

}

/*删除左边第一个=x的值*/

template <class T>

int List<T>::del_x(T x)

{

       Node<T> *p, *last, *temp;

       p = first;

       int j = 0;

       while (p)

       {



              last = p;

              p = p->next;

              j++;

              if (!p)break;

              if (p->data == x)

              {



                     temp = p;

                     last->next = temp->next;

                     delete temp;

                     return j;

              }

       }

       return j;





}

/*删除全部=x的值*/

template <class T>

int List<T>::del_all_x(T x)

{

       Node<T> *p, *last, *temp;

       p = first;

       last = first;

       int flag = 0;

       while (p)

       {



              if (flag == 0)

                     last = p;

              p = p->next;

              if (!p)break;

              if (p->data == x)

              {

                     temp = p;

                     last->next = temp->next;

                     flag = 1;

                     delete temp;

              }

              else flag = 0;



              //cout<<p->data<<endl;

       }





}

/*插入*/

template <class T>

void List<T>::Insert(int i, T x) {

       Node<T> *p;

       p = first;

       int j = 0;//指针初始化

       while (p&&j < i - 1) ///找第i-1个的结点

       {

              p = p->next;//指针后移

              j++;

       }

       if (!p)

              throw "插入位置出错";

       else

       {

              Node<T> *temp = new Node<T>;

              temp->data = x;

              temp->next = p->next;//先将节点p的后继插入节点temp之后

              p->next = temp;//后将节点temp插入到节点p之后

       }

}

/*//不带头结点的单链表中插入结点

template <class T>

void List<T>::Insert(int i, T x) {

       Node<T> *p;

       int j;

       //注意特判,插入为头节点需要特殊判断一下

       if(i <= 0) throw “位置非法”;

       if (i == 1 )

       {

              s = new Node<T>;

           s->next = first;

           first = s;

          return

       }



       p = first; j = 1;    //工作指针p初始化

       while (p && j < i - 1) {

              p = p->next;   //工作指针p后移

              j++;

       }

       if (!p) throw "位置";

       else {

              Node<T> *s;

              s = new Node<T>;

              s->data = x;  //向内存申请一个结点s,其数据域为x

              s->next = p->next;//先将节点p的后继插入节点s之后

              p->next = s; //后将节点s插入到节点p之后

       }

}*/



/*打印*/

template <class T>

void List<T>::PrintList() {

       Node<T> *p;

       p = first->next;//从第一个节点开始,不是头节点

       while (p)      //末尾节点的特点为NULL

       {

              cout << p->data << " ";

              p = p->next;

       }

       cout << endl;

}

/*长度*/

template <class T>

int List<T>::Length() {

       Node<T> *p;

       p = first->next;

       int j = 1; ///注意从1开始       

       while (p)

       {

              p = p->next;

              j++;

       }

       return j - 1;

}

/*排序*/

template <class T>

void List<T>::Sort() {

       int len = Length();

       Node<T> *temp, *p;





       for (int i = 0; i < len - 1; i++)

       {



              p = first->next;



              for (int j = 0; j < len - i - 1; j++)

              {



                     temp = p->next;

                     //cout<<temp->data<<endl;

                     if ((p->data) > (temp->data))

                     {

                            T x = p->data;

                            p->data = temp->data;

                            temp->data = x;

                     }

                     p = p->next;

              }



       }



}

template <class T>

List<T>::~List()

{

       Node<T> *q;

       while (first)

       {

              q = first->next;//保存下一个节点

              delete first;

              first = q;

       }

}

int a[100000];

int main()

{

       int i;

       for (i = 0;; i++)

       {

              scanf("%d", &a[i]);

              if (a[i] == 0) break;

       }

       List<int> l1(a, i, 1);

        l1.del(2) ;

       //     cout << l1.Length();

       l1.PrintList();



       return 0;

}

 顺序表和单链表的比较

时间性能比较

按位查找:

  • 顺序表的时间为(1),是随机存取;
  • 单链表的时间为(n),是顺序存取。

插入和删除:

  • 顺序表需移动表长一半的元素,时间为(n);
  • 单链表不需要移动元素在给出某个合适位置的指针后插入和删除操作所需的时间仅为(1)。

结论:

若线性表的操作主要是进行查找,很少做插入和删除时,宜采用顺序表做存储结构。

若对于频繁进行插入和删除的线性表, 宜采用链表做存储结构。

空间性能比较

例如:单链表的结点的数据均为整数,指针所占空间和整型量相同,则单链表的存储密度为50%。因此若不考虑顺序表中的备用结点空间, 则顺序表的存储空间利用率为100%,而单链表的存储空间利用率为50%。

结点的存储密度:

  • 顺序表中每个结点的存储密度为1(只存储数据元素),没有浪费空间;
  • 单链表的每个结点的存储密度<1(包括数据域和指针域),有指针的结构性开销。

整体结构:

  • 顺序表需要预分配存储空间,如果预分配得过大,造成浪费,若估计得过小,又将发生上溢;

单链表不需要预分配空间,只要有内存空间可以分配,单链表中的元素个数就没有限制

结论:

当线性表中元素个数变化较大或者未知时,最好使用单链表实现;而如果用户事先知道线性表的大致长度,使用顺序表的空间效率会更高。

循环链表

定义】

将单链表或者双链表的头尾结点链接起来,就是一个循环链表。

特点:

  1. 首尾相接的链表。
  2. 可以从任一节点出发,访问链表中的所有节点。
  3. 判断循环链表中尾结点的特点:

                   q->next==first

【实现】

空表的构造

template <class T>

CycleLinkList<T>::CycleLinkList()

{

    first = new Node<T>;

first->next = first;//头节点指向自己

}

头插法构造循环链表

template <class T>

CycleLinkList<T>::CycleLinkList(T a[], int n, int k)

{

    first = new Node<T>;   //生成头结点

    first->next = first;   //唯一和单链表实现不一样的地方

    Node<T> *s;

    for (int i = 1; i < n; i++)

    {

        s = new Node<T>;

        s->data = a[i];  //为每个数组元素建立一个结点

        s->next = first->next;/ / 注意先链接后面的

        first->next = s;//生成新的首元节点

    }

}

尾插法构造循环链表

template <class T>

CycleLinkList<T>::CycleLinkList(T a[], int n) {

    first = new Node<T>;   //生成头结点

    Node<T> *r, *s;

    r = first;          //尾指针初始化

    for (int i = 0; i < n; i++) {

        s = new Node<T>;

        s->data = a[i];

        r->next = s;

        r = s;

    }

    r->next = first;   //唯一和单链表不一样的地方,单链表建立完毕,将终端结点的指针域指向头结点

}

插入

template <class T>

void CycleLinkList::Insert(int i, T x)

{

    p = first;   j = 0;

    while (p->next != first && j < i - 1)//和单链表的不同

    {

        p = p->next;

        j++;

    }

    if (!p) throw "位置";

    else {

        s = new Node<T>;

        s->data = x;

        s->next = p->next;

        p->next = s;

    }

}

两个循环单链表的合并

 算法思想:

  1. 找到两个链表的尾,并分别由指针p、q指向它们,
  2. 将第一个链表的尾p与第二个表的第一个结点链接起来,并修改第二个表的尾q,使它的链域指向第一个表的头结点。

代码实现:

LinkList<T>   merge(LinkList<T> LA, LinkList<T> LB)

{  // 将两个链表的首尾相连

p = new Node<T>; q = new Node<T>; 

p = LA; q = LB; 

while (p->next!= LA)  p = p->next;   //找到表LA的表尾, 用p指向它         

while (q->next!= LB)    q = q->next;  // 找到表LB的表尾, 用q指向它 

q->next = LA;  // 修改表LB 的尾指针, 使之指向表LA 的头结点

p->next = LB->next;  // 修改表LA的尾指针, 使之指向表LB 中的第一个结点

}

应用

约瑟夫环:

1:约瑟夫环问题

查看

提交

统计

提问

总时间限制: 

 

1000ms

 

 

 

内存限制: 

 

1000kB

 

描述

 

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

 

输入

 

8 1 3 (n=8 k=1 m=3 )

 

输出

 

7 (剩下的那个)

 

样例输入

 

8 3 1

样例输出

 

7

代码实现:

#include<cstdio>

#include<iostream>

#include<fstream>

#include<algorithm>

#include<functional>

#include<cstring>

#include<string>

#include<cstdlib>

#include<iomanip>

#include<numeric>

#include<cctype>

#include<cmath>

#include<ctime>

#include<queue>

#include<stack>

#include<list>

#include<set>

#include<map>

using namespace std;

int sum = 0, s;

template <class T>



struct Node

{

    T data;

    Node<T> *next;

};

template <class T>

class List

{

    Node<T> *first;

public:

    List();

    List(int n);

    List(T a[], int n, int k);

    void display();

    T del(int be, int n);

    int Len();

};

template <class T>

List<T>::List()

{

    first = new Node<T>;

    first->next = NULL;

}

template <class T>

List<T>::List(int n) ///头插法

{

    first = new Node<T>;

    first->next = first;

    Node<T> *s;

    for (int i = n; i >= 1; i--)

    {

        s = new Node<T>;

        s->data = i;

        s->next = first->next;

        first->next = s;

    }

}

template <class T>

T List<T>::del(int be, int k)

{

    Node<T> *temp = first;

    T x;



    for (int i = 1; i < be; i++)

    {

        temp = temp->next;

        if (temp == first)i--;//头节点的影响

    }

    while (Len() > 0)

    {



        for (int i = 1; i < k; i++)

        {

             temp = temp->next;

             if (temp == first) i--;//头节点的影响

        }

        if (temp->next == first) temp = temp->next;//头节点的影响



        Node<T> *q;

        q = temp->next;

        x = q->data;

        temp->next = q->next;



        delete q;

        sum++;

        if (sum == s)

        {

             cout << x;

             break;

        }

    }

    return x;



}

template <class T>

void List<T>::display()

{

    Node<T> *temp = first;

    if (first != NULL)

    {

        do

        {

             temp = temp->next;

        } while (temp != first);

    }

}

template <class T>

int List<T>::Len()

{

    Node<T> *temp = first;

    int j = 0;

    if (first != NULL)

    {

        do

        {

             temp = temp->next;

             j++;

        } while (temp != first);

    }

    return j - 1;

}

int main()

{

    int n, m, be;

    while (scanf("%d%d%d", &n, &be, &m) != -1)

    {

        sum = 0; s = n;

        List<int> l1(n);

        l1.del(be, m);

    }

    return 0;

}

 

 

 

双链表

【定义】

在单链表的每个结点中再设置一个指向其前驱结点的指针域。

【实现】

双链表的结点结构

 

 

代码实现:

template <class T>

struct  Node {

    T  data;

    Node<T> *llink;//前驱指针

    Node <T>*rlink;//后继指针

};

空的双链表的构造

核心代码:

first->rlink = NULL;

first->llink = NULL;

Last = first;

双向链表的插入操作

1和2步骤可以互换,3和4不可

核心代码:

q->llink = p;

q->rlink = p->rlink;

p->rlink->llink = q;

p->rlink = q;

但注意对于末尾节点不适用

正确代码:

q->llink = p;

q->rlink = p->rlink;

if(q->rlink)//这里需要特判

p->rlink->llink = q;

p->rlink = q;

双向链表的删除操作

一般情况:

特殊情况:

核心代码:

p->llink->rlink = p->rlink;

if (p->rlink)//尾节点

p->rlink->llink = p->rlink;

delete(p);

双向链表的其他功能代码实现

有序单链表
/*插入(头插)*/
template <class T>
void DoubleLink<T>::Append(T data) {
       Node<T> *s;
       s = new Node<T>;
       s->data = data;
       s->rlink = head->rlink;
       head->rlink = s;
       s->llink = head;
       if (s->rlink)
              s->rlink->llink = s;
       return;
}
/*遍历*/
template <class T>
void DoubleLink<T>::Display() {
       Node <T> *p;
       p = head->rlink;
       while (p) {
              cout << p->data << "   ";
              p = p->rlink;
       }
       cout << endl;
       return;
}
/*析构*/
template <class T>
DoubleLink<T>::DoubleLink() {
       Node<T>  *p, *q;
       p = head;
       while (p)
       {
              q = p->rlink;
              delete p;
              p = q;

       }
}

 

构造有序的单链表

总时间限制

10000ms

 

内存限制

65535kB

描述

构造有序(升序)的单链表

并实现单链表的逆置

(可以采用结构化的程序设计方法实现,即不必定义类)

输入

输入链表中的数据。(用0表示输入的结束,0不能添加到链表中)

输出

按顺序输出有序链表中的数据

样例输入

4 1 6 8 2 0

样例输出

1 2 4 6 8
8 6 4 2 1

 

template <class T>
struct Node
{
       T data;
       Node<T> *next;
};
template <class T>
class List
{
       Node<T> *first;
public:
       List();

       void PrintList();
       int del_x(T x);
       void Insert(T a[],int n) ;
};
template <class T>
List<T>::List()
{
       first=new Node<T>;
       first->next=NULL;
}

/*删除左边第一个=x的值*/
template <class T>  
int List<T>::del_x(T x)
{
       Node<T> *p,*last,*temp;
       p=first;
       last=first;
       int flag=0;
       while(p)
       {
              
              if(flag==0)//注意判断上一次操作是否删除
                last=p;
              p=p->next;
              if(!p)break;
              if(p->data==x)
              {
           temp=p;
           last->next=temp->next;
           flag=1;
           delete temp;
              }
              else flag=0;
              
              //cout<<p->data<<endl;
       }
       return 0;
       
}
/*时刻有序单链表的插入*/
template <class T>  
void List<T>::Insert(T a[],int n){
       if(n==0)  return;
       
       Node<T> *p,*s,*last;
       p=first->next;
       int i=0;
       /*if(!p) 
       {
              s=new Node<T>;
              s->data=a[i];
              i++;
              s->next=first->next;
              first->next=s;
       } */
       for(;i<n;i++)
       {
              last=first;
              p=first->next;
              
              int flag=0;
              while(p)
           {
               
                if(a[i]<=p->data)
                {
                     s=new Node<T>;
                     s->data=a[i];
                     s->next=last->next;
                     last->next=s;
                     flag=1;
                     break;
                }
                  last=p;
               p=p->next;
               
           }
          
           if(flag==0)
              {
                     s=new Node<T>;
                     s->data=a[i];
                     s->next=last->next;
                     last->next=s;
              }
       }
       
}
/*打印*/
template <class T>  
void List<T>::PrintList(){
    Node<T> *p;
       p=first->next;          
     while(p)
       {
              cout<<p->data<<" ";
            p=p->next;
       }
       cout<<endl;
}
int a[100000];
int main()
{
       int i;
       
       for(i=0;;i++)
       {
              scanf("%d",&a[i]);
              if(a[i]==0) break;
       }
       List<int> l1;
       l1.Insert(a,i);
       l1.PrintList();
       
       for(i=0;;i++)
       {
              scanf("%d",&a[i]);
              if(a[i]==0) break;
       }
       l1.Insert(a,i);
       l1.PrintList();
       //l1.Find(2);
       int x;
       while(scanf("%d",&x)!=-1)
       {
              if(x==0) break;
              l1.del_x(x);
       }
       l1.PrintList();
       return 0;
}
   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值