zoukankan      html  css  js  c++  java
  • 线性表

    ------------------------siwuxie095

       

       

       

       

       

       

       

       

       

    线性表

       

       

    这里介绍 线性表,那么什么是线性表呢?

       

    比较官方的说法 线性表是 n 个数据元素有限序列

       

       

       

       

    通俗的理解,如下:

       

       

       

    有这么一段数字,其中有 43、52、41 … 它们没有什么顺序,也没有

    什么规律

       

    要说明的问题是:当我们把这段数字排列起来,成线性展开,这时就

    叫做 线性表

       

       

       

    除此之外,还有比较复杂一点的线性表,如下:

       

       

       

    上表中的内容:小明,男,28;李磊,男,32 … 其实也构成了

    一个 线性表

       

    显然,定义中的数据元素,可以是简单的数据,也可以是复杂

    数据

       

       

       

    对于线性表来说,大致分为两大类:

       

       

       

    一大类叫做 顺序表,其实在线性表中,顺序表 就是用数组来表达的,

    它的优势就在于访问速度快、搜索能力强,因为数组本身就带有天然

    的下标,它是与内存的地址直接相关的

       

       

    另一大类叫做 链表,也叫做 线性链表 线性表的链式表达方式,链

    表又分为:静态链表单链表循环链表双向链表

       

    无论是哪种链表方式,都有一个字很重要,即

       

    那么这个 究竟要表达什么意思?链表的基本特征又是什么样的?链

    表之所以重要又是为什么?

       

       

       

       

       

       

    顺序表

       

       

    为什么要在线性表中去区分顺序表和链表呢?因为它们之间互为补充

       

    顺序表的优缺点:

       

       

       

    顺序表的优点非常明显,就是它在进行遍历寻址时非常快

       

    如:拿着一个数据元素在顺序表中定位

       

    因为顺序表是基于数组的,所以在做这些操作时,效率都会很高

       

       

       

    但它的缺点也是显而易见的

       

    如:想要向顺序表中插入一个数据元素

       

    你就会发现,当我们在插入的时候,当前位置向后的所有元素,都必须

    要向后移一个位置,才能够顺利的插入

       

    同理:当删除一个元素时,在要删除位置向后的所有元素,都必须要向

    前移动一个位置,才能顺利的删除

       

    那么有没有一种方式使得插入删除的操作,效率高一点呢?这就要用

    到链表了

       

       

       

       

       

       

    链表

       

       

    1)单链表

       

       

       

    单链表的特点:单向性

       

    结点:

       

    结点其实就是线性表中的数据元素,只不过此时,它的元素分为

    两部分:一部分是数据域,另一部分是指针域

       

    指针域用来指向下一个结点,下一个结点又分为数据域和指针域,

    而其指针域又指向下一个结点,直到找到最后一个结点为止,它

    的指针域指向 NULL,也就是指向

       

       

       

    2)循环链表

       

       

       

    循环链表与单链表略有不同,它最大的不同在于:

       

    尾结点,即 最后一个结点的位置,它的指针域又指向了头结点,指向

    头结点之后,你会发现它就变成了一个 ,即 循环链表

       

       

       

    3)双向链表

       

       

       

    双向链表,也叫做 双链表,它的每一个结点由三部分组成:其中

    两部分都是指针域,一部分是数据域

       

       

    为什么在双向链表中要有两部分指针域呢?

       

    因为其中一个指针域是走正向,而另外一个指针域走反向

       

    换言之,即 其中一个指针域,是从头结点不停的去寻找,能够找到尾节点,

    而另一个指针域,则是从尾结点不停的去寻找,能够找到头结点

       

       

       

    4)静态链表

       

       

       

    对于某些计算机语言来说,它没有指针,却又想做链表,

    就可以通过数组来完成

       

    对于一个数组来说,数组天然就具有编号,即 下标,如

    上图的 01234

       

    对于静态链表的每一个结点来说,又分为两部分:一部分

    是所谓的指针域,它起到指针的作用,另一部分是数据域

       

       

    那么指针域是如何来寻址的呢?

       

    当进入这个数组之后,指针域的第一个位置就是要找的头结点

       

    头结点的指针域中是 1,指向下标 1 所在的结点;

    该结点的指针域中是 4,指向下标 4 所在的结点;

    该结点的指针域中是 2,指向下标 2 所在的结点;

    该结点的指针域中是 3,指向下标 3 所在的结点;

    该结点的指针域中是 0,指向下标 0 所在的结点

       

    下标 0 所在的结点 头结点,这代表当前的链表走到了最后

       

       

       

       

       

       

    应用场景

       

       

    线性表的一个比较常见的场景就是 通讯录

       

    大家的手机上都有通讯录,通讯录的特点就是:时常要向通讯录中加

    一些内容,还要从通讯录中把一些内容删掉

       

    无论是添加、删除,还是在打电话时想要搜索,这个时候都要用到线

    性表,所以线性表在通讯录中也可以得到最佳的实践

       

       

       

    此外还有 一元多项式,它其实解决的是一个数学问题,如下:

       

       

       

    p0 后面跟的是 x0,p1 后跟的是 x1,p2 后面跟的是 x2 … pn 后面跟的是 xn

       

    其中,p0、p1、p2 … pn 都表示 系数,而因为只有 x 一个变量,没有 y、z

    等其他变量,所以称之为 一元

       

       

       

       

       

       

    程序 1:顺序表

       

    List.h:

       

    #ifndef LIST_H

    #define LIST_H

       

    class List

    {

    public:

    List(int size); //创建顺序表

    ~List(); //销毁顺序表

    void ClearList(); //清空顺序表

    bool ListEmpty(); //顺序表为空

    int ListLength(); //顺序表长度

    bool GetElem(int i, int *e); //获取指定元素

    int LocateElem(int *e); //寻找第一个满足e的数据元素的位序

    bool PriorElem(int *currentElem,int *preElem); //获取指定元素的前驱

    bool NextElem(int *currentElem, int *nextElem); //获取指定元素的后继

    void ListTraverse(); //遍历顺序表

    bool ListInsert(int i, int *e); //在第i个位置插入元素

    bool ListDelete(int i, int *e); //删除第i个位置的元素

    private:

    int *m_pList; //指向顺序表的指针

    int m_iSize; //顺序表的容量大小

    int m_iLength; //当前顺序表的长度

    };

       

       

    #endif

       

       

       

    List.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    List::List(int size)

    {

    //初始化顺序表的容量

    m_iSize = size;

    //分配内存

    m_pList = new int[size];

    //初始化顺序表中已有元素的个数

    m_iLength = 0;

    }

       

       

    List::~List()

    {

    delete []m_pList;

    m_pList = NULL;

    }

       

       

    //清空顺序表不等于释放当前顺序表的内存

    //只是将顺序表中已经存放的元素全部清空

    void List::ClearList()

    {

    //只需将m_iLength赋值0即可,至于已经存放在内存中的值

    //完全可以忽略不计

    //

    //因为未来再往顺序表中放值时可以直接将原有值覆盖掉

    m_iLength = 0;

    }

       

       

    bool List::ListEmpty()

    {

    if (0 == m_iLength)

    {

    return true;

    }

    return false;

       

    //或采用另一种形式:

    //return m_iLength == 0 ? true : false;

    }

       

       

    int List::ListLength()

    {

    return m_iLength;

    }

       

       

    bool List::GetElem(int i, int *e)

    {

    //判断 i 是否合法

    if (i < 0 || i >= m_iSize)

    {

    return false;

    }

       

    *e = m_pList[i];

    return true;

    }

       

       

    int List::LocateElem(int *e)

    {

    //与已有元素进行比较

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

    {

    if (m_pList[i] == *e)

    {

    return i;

    }

    }

    //返回-1表示没有找到相应的数据元素

    return -1;

    }

       

       

    bool List::PriorElem(int *currentElem, int *preElem)

    {

    int temp = LocateElem(currentElem);

    //如果为-1,则根本没有这个元素

    if (-1 == temp)

    {

    return false;

    }

    else

    {

    //如果为0,即第一个元素,则没有前驱

    if (0 == temp)

    {

    return false;

    }

    else

    {

    *preElem = m_pList[temp-1];

    return true;

    }

    }

    }

       

       

    bool List::NextElem(int *currentElem, int *nextElem)

    {

    int temp = LocateElem(currentElem);

    //如果为-1,则根本没有这个元素

    if (-1 == temp)

    {

    return false;

    }

    else

    {

    //如果为m_iLength-1,即当前已有元素的最后一个,则没有后继

    if (m_iLength-1 == temp)

    {

    return false;

    }

    else

    {

    *nextElem = m_pList[temp + 1];

    return true;

    }

    }

    }

       

       

    void List::ListTraverse()

    {

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

    {

    cout << m_pList[i] << endl;

    }

    cout << endl;

    }

       

       

    bool List::ListInsert(int i, int *e)

    {

    if (i<0 || i>m_iLength || m_iLength == m_iSize)

    {

    return false;

    }

    //先从后往前的移动i位置及以后的已有元素

    //如果先插入,则会将i位置的元素覆盖掉

    for (int k = m_iLength; k >= i; k--)

    {

    m_pList[k + 1] = m_pList[k];

    }

       

    m_pList[i] = *e;

       

    //插入元素后,顺序表长度加1

    m_iLength++;

       

    return true;

    }

       

       

    bool List::ListDelete(int i, int *e)

    {

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

    //将第i个位置的元素拷贝出来

    *e = m_pList[i];

       

    //直接移动元素进行覆盖即可,从前往后进行移动

    //这就已经删除了第i个位置的元素

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

    {

    m_pList[k - 1] = m_pList[k];

    }

       

    //删除元素后,顺序表长度减1

    m_iLength--;

       

    return true;

    }

       

       

       

    main.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    //顺序表:

    //示例:3 5 7 2 9 1 8

    //该顺序表比较简单,都是 int 型整数

    //

    //介绍两个概念:前驱和后继

    //如:指定元素 2,它的前驱就是 7,它的后继就是 9

    //

    //说的更简单一点,所谓前驱就是指定元素的前边的元素,

    //而所谓后继就是指定元素的后边的元素

    //

    //有的也把紧邻的元素叫做直接前驱直接后继

    int main(void)

    {

    //因为在插入时,多向后赋值了一位,所以初始化的

    //空间至少比实际插入数值的个数多1

    List *p = new List(8);

       

    int e1 = 3;

    int e2 = 5;

    int e3 = 7;

    int e4 = 2;

    int e5 = 9;

    int e6 = 1;

    int e7 = 8;

    int temp = 0;

       

    p->ListInsert(0, &e1);//3

    p->ListInsert(1, &e2);//5

    p->ListInsert(2, &e3);//7

    p->ListInsert(3, &e4);//2

    p->ListInsert(4, &e5);//9

    p->ListInsert(5, &e6);//1

    p->ListInsert(6, &e7);//8

    p->ListTraverse();

       

    cout << "length:" << p->ListLength() << endl;

       

    p->GetElem(0,&temp);

    cout << "temp:" << temp << endl;

       

    cout << "index:" << p->LocateElem(&temp) << endl;

       

       

    p->PriorElem(&e4, &temp);

    cout << "preElem:" << temp << endl;

       

    p->NextElem(&e4, &temp);

    cout << "nextElem:" << temp << endl;

    p->ListDelete(0, &temp);

    cout << "#:" << temp << endl;

       

    p->ClearList();

    if (p->ListEmpty())

    {

    cout << "empty" << endl;

    }

       

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

    程序 2:

       

    Coordinate.h:

       

    #ifndef COORDINATE_H

    #define COORDINATE_H

       

    #include <ostream>

    using namespace std;

       

       

    class Coordinate

    {

    friend ostream &operator<<(ostream &out, Coordinate &coor);

    public:

    Coordinate(int x = 0, int y = 0);

    void printCoordinate();

    //==的重载,传入的参数实际上第二个参数,第一个参数是this指针

    bool operator==(Coordinate &coor);

    private:

    int m_iX;

    int m_iY;

    };

       

    //Coordinate的对象或引用作参数时,会调用拷贝构造函数,

    //因为这里Coordinate的数据成员比较简单,没有涉及到指针,

    //就使用默认拷贝构造函数即可

    #endif

       

       

       

    Coordinate.cpp:

       

    #include "Coordinate.h"

    #include <iostream>

    using namespace std;

       

       

    Coordinate::Coordinate(int x, int y)

    {

    m_iX = x;

    m_iY = y;

    }

       

       

    void Coordinate::printCoordinate()

    {

    cout << "(" << m_iX << "," << m_iY << ")" << endl;

    }

       

       

    bool Coordinate::operator==(Coordinate &coor)

    {

    if (this->m_iX == coor.m_iX && this->m_iY == coor.m_iY)

    {

    return true;

    }

    return false;

    }

       

       

    ostream &operator<<(ostream &out, Coordinate &coor)

    {

       

    cout << "(" << coor.m_iX << "," << coor.m_iY << ")" << endl;

    return out;

    }

       

       

       

    List.h:

       

    #ifndef LIST_H

    #define LIST_H

       

    #include "Coordinate.h"

       

       

       

    class List

    {

    public:

    List(int size); //创建顺序表

    ~List(); //销毁顺序表

    void ClearList(); //清空顺序表

    bool ListEmpty(); //顺序顺序空

    int ListLength(); //顺序表长度

    //获取指定元素

    bool GetElem(int i, Coordinate *e);

    //寻找第一个满足e的数据元素的位序

    int LocateElem(Coordinate *e);

    //获取指定元素的前驱

    bool PriorElem(Coordinate *currentElem, Coordinate *preElem);

    //获取指定元素的后继

    bool NextElem(Coordinate *currentElem, Coordinate *nextElem);

    void ListTraverse(); //遍历顺序表

    bool ListInsert(int i, Coordinate *e); //在第i个位置插入元素

    bool ListDelete(int i, Coordinate *e); //删除第i个位置的元素

    private:

    Coordinate *m_pList; //指向顺序表的指针

    int m_iSize; //顺序表的容量大小

    int m_iLength; //当前顺序表的长度

    };

       

       

    #endif

       

       

       

    List.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    List::List(int size)

    {

    //初始化顺序表的容量

    m_iSize = size;

    //分配内存

    m_pList = new Coordinate[size];

    //初始化顺序表中已有元素的个数

    m_iLength = 0;

    }

       

       

    List::~List()

    {

    delete[]m_pList;

    m_pList = NULL;

    }

       

       

    //清空顺序表不等于释放当前顺序表的内存

    //只是将顺序表中已经存放的元素全部清空

    void List::ClearList()

    {

    //只需将m_iLength赋值0即可,至于已经存放在内存中的值

    //完全可以忽略不计

    //

    //因为未来再往顺序表中放值时可以直接将原有值覆盖掉

    m_iLength = 0;

    }

       

       

    bool List::ListEmpty()

    {

    if (0 == m_iLength)

    {

    return true;

    }

    return false;

       

    //或采用另一种形式:

    //return m_iLength == 0 ? true : false;

    }

       

       

    int List::ListLength()

    {

    return m_iLength;

    }

       

       

    bool List::GetElem(int i, Coordinate *e)

    {

    //判断 i 是否合法

    if (i < 0 || i >= m_iSize)

    {

    return false;

    }

       

    *e = m_pList[i];

    return true;

    }

       

       

    int List::LocateElem(Coordinate *e)

    {

    //与已有元素进行比较

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

    {

    if (m_pList[i] == *e)

    {

    return i;

    }

    }

    //返回-1表示没有找到相应的数据元素

    return -1;

    }

       

       

    bool List::PriorElem(Coordinate *currentElem, Coordinate *preElem)

    {

    int temp = LocateElem(currentElem);

    //如果为-1,则根本没有这个元素

    if (-1 == temp)

    {

    return false;

    }

    else

    {

    //如果为0,即第一个元素,则没有前驱

    if (0 == temp)

    {

    return false;

    }

    else

    {

    *preElem = m_pList[temp - 1];

    return true;

    }

    }

    }

       

       

    bool List::NextElem(Coordinate *currentElem, Coordinate *nextElem)

    {

    int temp = LocateElem(currentElem);

    //如果为-1,则根本没有这个元素

    if (-1 == temp)

    {

    return false;

    }

    else

    {

    //如果为m_iLength-1,即当前已有元素的最后一个,则没有后继

    if (m_iLength - 1 == temp)

    {

    return false;

    }

    else

    {

    *nextElem = m_pList[temp + 1];

    return true;

    }

    }

    }

       

       

    void List::ListTraverse()

    {

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

    {

    //Coordinate.h中完成了对输出运算符<<的重载

    //所以可以直接用cout进行输出

    cout << m_pList[i] << endl;

       

    //或使用以下方法进行输出

    //m_pList[i].printCoordinate();

    }

    }

       

       

    bool List::ListInsert(int i, Coordinate *e)

    {

    if (i<0 || i>m_iLength || m_iLength == m_iSize)

    {

    return false;

    }

    //先从后往前的移动i位置及以后的已有元素

    //如果先插入,则会将i位置的元素覆盖掉

    for (int k = m_iLength; k >= i; k--)

    {

    m_pList[k + 1] = m_pList[k];

    }

       

    m_pList[i] = *e;

       

    //插入元素后,顺序表长度加1

    m_iLength++;

       

    return true;

    }

       

       

    bool List::ListDelete(int i, Coordinate *e)

    {

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

    //将第i个位置的元素拷贝出来

    *e = m_pList[i];

       

    //直接移动元素进行覆盖即可,从前往后进行移动

    //这就已经删除了第i个位置的元素

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

    {

    m_pList[k - 1] = m_pList[k];

    }

       

    //删除元素后,顺序表长度减1

    m_iLength--;

       

    return true;

    }

       

       

       

    main.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    int main(void)

    {

    //因为在插入时,多向后赋值了一位,所以初始化的

    //空间至少比实际插入数值的个数多1

    List *p = new List(8);

       

    Coordinate c1(3, 5);

    Coordinate c2(5, 7);

    Coordinate c3(6, 8);

    Coordinate temp(0, 0);

       

    p->ListInsert(0, &c1);

    p->ListInsert(1, &c2);

    p->ListInsert(2, &c3);

       

       

    p->ListTraverse();

       

    cout << "length:" << p->ListLength() << endl;

    cout << endl;

       

    p->GetElem(0,&temp);

    cout << "temp:" << temp;

    cout << "index:" << p->LocateElem(&temp) << endl;

    cout << endl;

       

       

    p->PriorElem(&c2, &temp);

    cout << "preElem:" << temp << endl;

       

    p->NextElem(&c2, &temp);

    cout << "nextElem:" << temp << endl;

    p->ListDelete(0, &temp);

    cout << "#:" << temp << endl;

       

    p->ClearList();

    if (p->ListEmpty())

    {

    cout << "empty" << endl;

    }

       

       

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

    程序 3:链表

       

    Node.h:

       

    #ifndef NODE_H

    #define NODE_H

       

       

    class Node

    {

    public:

    //为了操作的方便,将数据域和指针域都定义在public

    int data; //数据域

    Node *next; //指针域指向下一个结点

    void printNode();

    };

       

       

    #endif

       

       

       

    Node.cpp:

       

    #include "Node.h"

    #include <iostream>

    using namespace std;

       

    void Node::printNode()

    {

    //只需打印数据域即可

    cout << data << endl;

    }

       

       

       

    List.h:

       

    #ifndef LIST_H

    #define LIST_H

       

    #include "Node.h"

       

       

       

    class List

    {

    public:

    List(); //创建链表(1

    ~List(); //销毁链表(5

    void ClearList(); //清空链表(4

    bool ListEmpty(); //链表为空(2

    int ListLength(); //链表长度(3

    //获取指定i位置结点:需要从头结点开始顺藤摸瓜找到指定位置(10

    bool GetNode(int i, Node *pNode);

    //拿着当前给定的结点,去找与这个结点中数据域相同的第一个结点,并返回位序(11

    int LocateNode(Node *pNode);

    //获取指定结点的前驱:从头结点开始寻找到pCurrentNode,然后...12

    bool PriorNode(Node *pCurrentNode, Node *pPreNode);

    //获取指定结点的后继:从头结点开始寻找到pCurrentNode,然后...13

    bool NextNode(Node *pCurrentNode, Node *pNextNode);

    void ListTraverse(); //遍历链表(14

    bool ListInsert(int i, Node *pNode); //在第i个位置插入结点(8

    bool ListDelete(int i, Node *pNode); //删除第i个位置的结点(9

    bool ListInsertHead(Node *pNode); //从头开始插入结点(6

    bool ListInsertTail(Node *pNode); //从尾开始插入结点(7

    private:

    Node *m_pList; //指向链表的指针

    int m_iLength; //当前链表的长度

    };

       

    //作为链表来说,它的每一个元素都是它的结点

       

    #endif

       

       

       

    List.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    //1

    List::List()

    {

    //初始化链表首先要定义一个头结点

    //数据域与指针域分别为 0 NULL

    //

    //对于一个链表来说,它的第一个结点即头结点的数据域

    //是没有意义的,而它的指针域,一开始的情况下也没有意义

    //因为作为第一个结点来说,它也是最后一个结点

    //如果再挂载新结点,再将next指向新结点

    m_pList = new Node;

    m_pList->data = 0;

    m_pList->next = NULL;

       

    //将链表的长度初始化为 0

    //注意:虽然已经从堆中分配了内存,已经有了一个结点

    //但这个结点并不算在当前的链表中

    m_iLength = 0;

    }

       

       

    //5

    //析构函数与ClearList()其实有着异曲同工之妙,二者的区别就在于:

    //析构函数将构造函数中申请的第一个结点也释放掉,ClearList()

    //将其保留下来,而释放掉后面的所有结点

    //

    //只有第一个结点是否释放的区别

    List::~List()

    {

    //调用ClearList(),将除了头结点m_pList之外的

    //所有其他结点都删除掉了

    ClearList();

    //只需再删除m_pList即可

    delete m_pList;

    m_pList = NULL;

    }

       

       

    //4

    //ClearList()的实现原理:

    //举个例子:如果我们面临着一群敌人,这些敌人彼此之间是通过

    //单线进行联系的,那么怎么把这群敌人全部消灭掉呢?

    //首先我们要先找到一个敌人,他是团伙组织的最上线,然后审问

    //他:他的下线是谁,他交代了之后,就将他干掉,然后找到他的

    //下线,再去审问...直到找到一个敌人,他再也交代不出来自己的

    //下线了,把抓到的最后一个敌人再干掉,那么工作也就完成了

    void List::ClearList()

    {

    //找到第一条线索,顺着线索m_pListnext找到第一个敌人currentNode

    //因为m_pList是头结点,并不算在链表中,所以不算第一个敌人

    Node *currentNode = m_pList->next;

    //对敌人进行审问,如果不为空,就顺藤摸瓜

    while (currentNode != NULL)

    {

    //先审一审当前的敌人currentNode的下线next是谁

    Node *temp = currentNode->next;

    //currentNode交代后,就毫不客气的干掉他

    delete currentNode;

    //这时,temp变成了当前要审问的敌人

    currentNode = temp;

    }

       

    //currentNode为空时,敌人也就全部被消灭了

    //m_pList重新置为NULL

    m_pList = NULL;

    }

       

       

    //2

    bool List::ListEmpty()

    {

    if (0 == m_iLength)

    {

    return true;

    }

    return false;

       

    //或采用另一种形式:

    //return m_iLength == 0 ? true : false;

    }

       

       

    //3

    int List::ListLength()

    {

    return m_iLength;

    }

       

       

    //10

    bool List::GetNode(int i, Node *pNode)

    {

    //判断 i 是否合法

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

       

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //通过for循环来找到第i个位置

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

    { //遍历

    currentNode = currentNode->next;

    }

    pNode->data = currentNode->data;

    return true;

    }

       

       

    //11

    //看看链表中有没有哪个结点的数据域与pNode的数据域相同

    //如果有就将这个结点的位序返回出来

    //注意:是返回第一个相同的结点

    int List::LocateNode(Node *pNode)

    {

    //先取到m_pList的值

    Node *currentNode = m_pList;

    int count = 0;

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

    currentNode = currentNode->next;

    if (currentNode->data == pNode->data)

    {

    return count;

    }

    count++;

    }

    //一个结点都没找到,返回-1

    return -1;

    }

       

       

    //12

    bool List::PriorNode(Node *pCurrentNode, Node *pPreNode)

    {

    //先取到m_pList的值

    Node *currentNode = m_pList;

    Node *tempNode = NULL;

       

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

    tempNode = currentNode;

    currentNode = currentNode->next;

    if (currentNode->data == pCurrentNode->data)

    {

    //默认头结点不算在链表中,

    //即头结点的下一个结点的是没有前驱的

    if (tempNode == m_pList)

    {

    return false;

    }

    pPreNode->data = tempNode->data;

    //找到第一个符合条件的结点就返回true

    return true;

    }

    }

    return false;

    }

       

       

    //13

    bool List::NextNode(Node *pCurrentNode, Node *pNextNode)

    {

    Node *currentNode = m_pList;

       

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

    currentNode = currentNode->next;

    if (currentNode->data == pCurrentNode->data)

    {

    //如果当前结点是最后一个结点,则没有后继

    if (currentNode->next == NULL)

    {

    return false;

    }

    pNextNode->data = currentNode->next->data;

    //找到第一个符合条件的结点就返回true

    return true;

    }

    }

    return false;

    }

       

       

    //14

    void List::ListTraverse()

    {

    //先拿到m_pList

    Node *currentNode = m_pList;

    while (currentNode->next != NULL)

    {

    currentNode = currentNode->next;

    currentNode->printNode();

    }

    }

       

       

    //8

    //i即插入的位置:

    //如果是0,即插入在头结点的后边,

    //如果是m_iLength,即插入在当前链表的最后边

    bool List::ListInsert(int i, Node *pNode)

    {

    //判断i是否合法

    if (i<0 || i>m_iLength)

    {

    return false;

    }

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //通过for循环来找到第i个位置的上一个位置

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

    { //遍历

    currentNode = currentNode->next;

    }

       

    Node *newNode = new Node;

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

    newNode->data = pNode->data;

    //原来currentNode的下一个结点变成newNode的下一个结点

    newNode->next = currentNode->next;

    //newNode成为了currentNode的下一个结点

    //即将newNode插入到了整个链表当中

    currentNode->next = newNode;

    m_iLength++;

       

    return true;

    }

       

       

    //9

    //i即要删除的结点的位置:如果为0,则删除头结点的下一个结点

    //注意:i不能等于m_iLength

    bool List::ListDelete(int i, Node *pNode)

    {

    //如果i等于m_iLength就意味着你在删尾结点的下一个结点

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //如果要删除一个结点,就应该能够找到当前结点的上一个结点

    //才容易通过上一个结点再去连接要删除的这个结点的下一个结点

    //从而将要删除的结点分离出来并删除

    Node *currentNodeBefore = NULL;

    //通过for循环来找到第i个位置

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

    { //遍历

    currentNodeBefore = currentNode;

    currentNode = currentNode->next;

    }

    currentNodeBefore->next = currentNode->next;

    //将要删除结点的数据域赋值给传入进来的参数pNode

    pNode->data = currentNode->data;

    delete currentNode;

    currentNode = NULL;

       

    m_iLength--;

       

    return true;

    }

       

       

    //6

    //将结点插入到头结点的后边

    bool List::ListInsertHead(Node *pNode)

    {

    //先保存一下m_pListnext

    Node *temp = m_pList->next;

    //将传入进来的pNode的数据域保存到一个新的结点里

    //所以需要先定义一个新结点,注意:一定要从堆中申请内存,

    //如果从栈中申请内存,此函数执行完毕,内存就被回收掉了

    Node *newNode = new Node;

       

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

       

    //对于pNode的指针域并不用关心,只拿数据域即可

    newNode->data = pNode->data;

    //将头结点m_pListnext指向新申请的newNode

    m_pList->next = newNode;

    //newNodenext则需要指向原来m_pListnext

    newNode->next = temp;

       

    m_iLength++;

       

    //插入成功,向外发出成功的信号

    return true;

    }

       

       

    //7

    //将结点插入到链表的最后边,即链表的尾部

    bool List::ListInsertTail(Node *pNode)

    {

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //如果当前结点currentNodenext不为空,

    //就在while循环里做遍历的操作

    while (currentNode->next != NULL)

    {

    //currentNodenext赋值给currentNode

    //就相当于来到了下一个结点

    currentNode = currentNode->next;

    }

       

    //currentNode为空了,也就跳出了while循环,

    //此时的currentNode就是最后一个结点

    //将传入进来的pNode挂载进去,也即插入

    Node *newNode = new Node;

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

    newNode->data = pNode->data;

    //此时,newNode作为最后一个结点

    newNode->next = NULL;

    currentNode->next = newNode;

       

    m_iLength++;

       

    return true;

    }

       

       

       

    main.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    int main(void)

    {

    List *p = new List();

       

    Node n1;//Node n1();

    n1.data = 3;

    Node n2;

    n2.data = 4;

    Node n3;

    n3.data = 5;

    Node n4;

    n4.data = 6;

    Node n5;

    n5.data = 7;

    Node temp;

       

    /*p->ListInsertHead(&n1);

    p->ListInsertHead(&n2);

    p->ListInsertHead(&n3);

    p->ListInsertHead(&n4);*/

    p->ListInsertTail(&n1);

    p->ListInsertTail(&n2);

    p->ListInsertTail(&n3);

    p->ListInsertTail(&n4);

       

    p->ListInsert(1, &n5);

       

    p->GetNode(1, &temp);

    cout << "#:" << temp.data << endl;

       

    p->PriorNode(&n5, &temp);

    cout << "preNode:" << temp.data << endl;

    p->NextNode(&n5, &temp);

    cout << "nextNode:" << temp.data << endl;

    /*p->ListDelete(1,&temp);

    cout << "delNoe:" << temp.data << endl;*/

       

    //ListInsertHead()的遍历结果是逆序

    //ListInsertTail()的遍历结果是顺序

    p->ListTraverse();

       

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

    程序 4:通讯录

       

    Person.h:

       

    #ifndef PERSON_H

    #define PERSON_H

       

    #include <string>

    #include <ostream>

    using namespace std;

       

    //通讯录:

    //联系人信息(姓名、电话)作为结点的数据域

    class Person

    {

    friend ostream &operator<<(ostream &out,Person &person);

       

    public:

    string name;

    string phone;

    Person &operator=(Person &person);

    bool operator==(Person &person);

    };

       

       

    #endif

       

       

       

    Person.cpp:

       

    #include "Person.h"

       

    Person &Person::operator=(Person &person)

    {

    this->name = person.name;

    this->phone = person.phone;

    return *this;

    }

       

       

    bool Person::operator==(Person &person)

    {

    if (this->name == person.name && this->phone == person.phone)

    {

    return true;

    }

    return false;

    }

       

       

    ostream &operator<<(ostream &out, Person &person)

    {

    out << person.name << "---" << person.phone << endl;

    return out;

    }

       

       

       

    Node.h:

       

    #ifndef NODE_H

    #define NODE_H

       

    #include "Person.h"

       

    class Node

    {

    public:

    //为了操作的方便,将数据域和指针域都定义在public

    Person data; //数据域

    Node *next; //指针域指向下一个结点

    void printNode();

    };

       

       

    #endif

       

       

       

    Node.cpp:

       

    #include "Node.h"

    #include <iostream>

    using namespace std;

       

       

       

    void Node::printNode()

    {

    //只需打印数据域即可

    cout << data << endl;

    }

       

       

       

    List.h:

       

    #ifndef LIST_H

    #define LIST_H

       

    #include "Node.h"

       

       

       

    class List

    {

    public:

    List(); //创建链表(1

    ~List(); //销毁链表(5

    void ClearList(); //清空链表(4

    bool ListEmpty(); //链表为空(2

    int ListLength(); //链表长度(3

    //获取指定i位置结点:需要从头结点开始顺藤摸瓜找到指定位置(10

    bool GetNode(int i, Node *pNode);

    //拿着当前给定的结点,去找与这个结点中数据域相同的第一个结点,并返回位序(11

    int LocateNode(Node *pNode);

    //获取指定结点的前驱:从头结点开始寻找到pCurrentNode,然后...12

    bool PriorNode(Node *pCurrentNode, Node *pPreNode);

    //获取指定结点的后继:从头结点开始寻找到pCurrentNode,然后...13

    bool NextNode(Node *pCurrentNode, Node *pNextNode);

    void ListTraverse(); //遍历链表(14

    bool ListInsert(int i, Node *pNode); //在第i个位置插入结点(8

    bool ListDelete(int i, Node *pNode); //删除第i个位置的结点(9

    bool ListInsertHead(Node *pNode); //从头开始插入结点(6

    bool ListInsertTail(Node *pNode); //从尾开始插入结点(7

    private:

    Node *m_pList; //指向链表的指针

    int m_iLength; //当前链表的长度

    };

       

    //作为链表来说,它的每一个元素都是它的结点

       

    #endif

       

       

       

    List.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    //1

    List::List()

    {

    //初始化链表首先要定义一个头结点

    //

    //对于一个链表来说,它的第一个结点即头结点的数据域

    //是没有意义的,而它的指针域,一开始的情况下也没有意义

    //因为作为第一个结点来说,它也是最后一个结点

    //如果再挂载新结点,再将next指向新结点

    m_pList = new Node;

    m_pList->data.name = "#";

    m_pList->data.phone = "#";

    m_pList->next = NULL;

       

    //将链表的长度初始化为 0

    //注意:虽然已经从堆中分配了内存,已经有了一个结点

    //但这个结点并不算在当前的链表中

    m_iLength = 0;

    }

       

       

    //5

    //析构函数与ClearList()其实有着异曲同工之妙,二者的区别就在于:

    //析构函数将构造函数中申请的第一个结点也释放掉,ClearList()

    //将其保留下来,而释放掉后面的所有结点

    //

    //只有第一个结点是否释放的区别

    List::~List()

    {

    //调用ClearList(),将除了头结点m_pList之外的

    //所有其他结点都删除掉了

    ClearList();

    //只需再删除m_pList即可

    delete m_pList;

    m_pList = NULL;

    }

       

       

    //4

    //ClearList()的实现原理:

    //举个例子:如果我们面临着一群敌人,这些敌人彼此之间是通过

    //单线进行联系的,那么怎么把这群敌人全部消灭掉呢?

    //首先我们要先找到一个敌人,他是团伙组织的最上线,然后审问

    //他:他的下线是谁,他交代了之后,就将他干掉,然后找到他的

    //下线,再去审问...直到找到一个敌人,他再也交代不出来自己的

    //下线了,把抓到的最后一个敌人再干掉,那么工作也就完成了

    void List::ClearList()

    {

    //找到第一条线索,顺着线索m_pListnext找到第一个敌人currentNode

    //因为m_pList是头结点,并不算在链表中,所以不算第一个敌人

    Node *currentNode = m_pList->next;

    //对敌人进行审问,如果不为空,就顺藤摸瓜

    while (currentNode != NULL)

    {

    //先审一审当前的敌人currentNode的下线next是谁

    Node *temp = currentNode->next;

    //currentNode交代后,就毫不客气的干掉他

    delete currentNode;

    //这时,temp变成了当前要审问的敌人

    currentNode = temp;

    }

       

    //currentNode为空时,敌人也就全部被消灭了

    //m_pList重新置为NULL

    m_pList = NULL;

    }

       

       

    //2

    bool List::ListEmpty()

    {

    if (0 == m_iLength)

    {

    return true;

    }

    return false;

       

    //或采用另一种形式:

    //return m_iLength == 0 ? true : false;

    }

       

       

    //3

    int List::ListLength()

    {

    return m_iLength;

    }

       

       

    //10

    bool List::GetNode(int i, Node *pNode)

    {

    //判断 i 是否合法

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

       

    //先保存一下m_pList

    Node *currentNode = m_pList;

       

    //通过for循环来找到第i个位置

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

    { //遍历

    currentNode = currentNode->next;

    }

    pNode->data = currentNode->data;

       

    return true;

    }

       

       

    //11

    //看看链表中有没有哪个结点的数据域与pNode的数据域相同

    //如果有就将这个结点的位序返回出来

    //注意:是返回第一个相同的结点

    int List::LocateNode(Node *pNode)

    {

    //先取到m_pList的值

    Node *currentNode = m_pList;

    int count = 0;

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

    currentNode = currentNode->next;

    if (currentNode->data == pNode->data)

    {

    return count;

    }

    count++;

    }

    //一个结点都没找到,返回-1

    return -1;

    }

       

       

    //12

    bool List::PriorNode(Node *pCurrentNode, Node *pPreNode)

    {

    //先取到m_pList的值

    Node *currentNode = m_pList;

    Node *tempNode = NULL;

       

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

    tempNode = currentNode;

    currentNode = currentNode->next;

    if (currentNode->data == pCurrentNode->data)

    {

    //默认头结点不算在链表中,

    //即头结点的下一个结点的是没有前驱的

    if (tempNode == m_pList)

    {

    return false;

    }

    pPreNode->data = tempNode->data;

    //找到第一个符合条件的结点就返回true

    return true;

    }

    }

    return false;

    }

       

       

    //13

    bool List::NextNode(Node *pCurrentNode, Node *pNextNode)

    {

    Node *currentNode = m_pList;

       

       

    //不断去对比每一个结点,直到最后一个结点为止

    while (currentNode->next != NULL)

    {

       

    currentNode = currentNode->next;

    if (currentNode->data == pCurrentNode->data)

    {

    //如果当前结点是最后一个结点,则没有后继

    if (currentNode->next == NULL)

    {

    return false;

    }

    pNextNode->data = currentNode->next->data;

    //找到第一个符合条件的结点就返回true

    return true;

    }

       

    }

    return false;

    }

       

       

    //14

    void List::ListTraverse()

    {

    //先拿到m_pList

    Node *currentNode = m_pList;

    while (currentNode->next != NULL)

    {

    currentNode = currentNode->next;

    currentNode->printNode();

    }

    }

       

       

    //8

    //i即插入的位置:

    //如果是0,即插入在头结点的后边,

    //如果是m_iLength,即插入在当前链表的最后边

    bool List::ListInsert(int i, Node *pNode)

    {

    //判断i是否合法

    if (i<0 || i>m_iLength)

    {

    return false;

    }

       

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //通过for循环来找到第i个位置的上一个位置

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

    { //遍历

    currentNode = currentNode->next;

    }

       

    Node *newNode = new Node;

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

    newNode->data = pNode->data;

    //原来currentNode的下一个结点变成newNode的下一个结点

    newNode->next = currentNode->next;

    //newNode成为了currentNode的下一个结点

    //即将newNode插入到了整个链表当中

    currentNode->next = newNode;

       

    m_iLength++;

       

    return true;

    }

       

       

    //9

    //i即要删除的结点的位置:如果为0,则删除头结点的下一个结点

    //注意:i不能等于m_iLength

    bool List::ListDelete(int i, Node *pNode)

    {

    //如果i等于m_iLength就意味着你在删尾结点的下一个结点

    if (i < 0 || i >= m_iLength)

    {

    return false;

    }

       

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //如果要删除一个结点,就应该能够找到当前结点的上一个结点

    //才容易通过上一个结点再去连接要删除的这个结点的下一个结点

    //从而将要删除的结点分离出来并删除

    Node *currentNodeBefore = NULL;

    //通过for循环来找到第i个位置

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

    { //遍历

    currentNodeBefore = currentNode;

    currentNode = currentNode->next;

    }

    currentNodeBefore->next = currentNode->next;

    //将要删除结点的数据域赋值给传入进来的参数pNode

    pNode->data = currentNode->data;

    delete currentNode;

    currentNode = NULL;

       

    m_iLength--;

       

    return true;

    }

       

       

    //6

    //将结点插入到头结点的后边

    bool List::ListInsertHead(Node *pNode)

    {

    //先保存一下m_pListnext

    Node *temp = m_pList->next;

    //将传入进来的pNode的数据域保存到一个新的结点里

    //所以需要先定义一个新结点,注意:一定要从堆中申请内存,

    //如果从栈中申请内存,此函数执行完毕,内存就被回收掉了

    Node *newNode = new Node;

       

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

       

    //对于pNode的指针域并不用关心,只拿数据域即可

    newNode->data = pNode->data;

    //将头结点m_pListnext指向新申请的newNode

    m_pList->next = newNode;

    //newNodenext则需要指向原来m_pListnext

    newNode->next = temp;

       

    m_iLength++;

       

    //插入成功,向外发出成功的信号

    return true;

    }

       

       

    //7

    //将结点插入到链表的最后边,即链表的尾部

    bool List::ListInsertTail(Node *pNode)

    {

    //先保存一下m_pList

    Node *currentNode = m_pList;

    //如果当前结点currentNodenext不为空,

    //就在while循环里做遍历的操作

    while (currentNode->next != NULL)

    {

    //currentNodenext赋值给currentNode

    //就相当于来到了下一个结点

    currentNode = currentNode->next;

    }

       

    //currentNode为空了,也就跳出了while循环,

    //此时的currentNode就是最后一个结点

    //将传入进来的pNode挂载进去,也即插入

    Node *newNode = new Node;

    if (NULL == newNode)

    {

    //内存申请有可能失败

    return false;

    }

    newNode->data = pNode->data;

    //此时,newNode作为最后一个结点

    newNode->next = NULL;

    currentNode->next = newNode;

       

    m_iLength++;

       

    return true;

    }

       

       

       

    main.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    int main(void)

    {

       

    List *p = new List();

       

    Node n1;//Node n1();

    n1.data.name = "tester1";

    n1.data.phone = "123456";

    Node n2;

    n2.data.name = "tester2";

    n2.data.phone = "234567";

    Node temp;

       

    /*p->ListInsertHead(&n1);

    p->ListInsertHead(&n2);*/

       

    p->ListInsertTail(&n1);

    p->ListInsertTail(&n2);

       

       

       

    //ListInsertHead()的遍历结果是逆序

    //ListInsertTail()的遍历结果是顺序

    p->ListTraverse();

       

       

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

    程序 5:基于程序 4,修改其中的 main.cpp

       

    main.cpp:

       

    #include "List.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    int menu();

    void createPerson(List *pList);

    void deletePerson(List *pList);

       

    int main(void)

    {

       

    int userOrder = 0;

    List *p = new List();

       

    while (userOrder != 4)

    {

    userOrder = menu();

    switch (userOrder)

    {

    case 1:

    cout << "用户指令--->>新建联系人:" << endl;

    createPerson(p);

    cout << endl;

    break;

    case 2:

    cout << "用户指令--->>删除联系人:" << endl;

    deletePerson(p);

    cout << endl;

    break;

    case 3:

    cout << "用户指令--->>浏览通讯录:" << endl;

    p->ListTraverse();

    break;

    case 4:

    cout << "用户指令--->>退出通讯录:" << endl;

    cout << endl;

    break;

    default:

    break;

    }

    }

       

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    int menu()

    {

    //显示通讯录功能菜单

    cout << "功能菜单" << endl;

    cout << "1.新建联系人" << endl;

    cout << "2.删除联系人" << endl;

    cout << "3.浏览通讯录" << endl;

    cout << "4.退出通讯录" << endl;

    cout << "请输入:";

    int order = 0;

    cin >> order;

    return order;

    }

       

       

    void createPerson(List *pList)

    {

    Node node;

    Person person;

       

    cout << "请输入姓名:";

    cin >> person.name;

       

    cout << "请输入电话:";

    cin >> person.phone;

       

    node.data = person;

    //因为这个node是从栈中实例化的,所以在

    //ListInsertTail()中只取它的数据域即可

    //不要直接将这个node直接挂到链表上

    //否则createPerson()一执行完,内存一回收,

    //就会出现内存的访问错误

    pList->ListInsertTail(&node);

    }

       

       

    void deletePerson(List *pList)

    {

    Node node;

       

    cout << "请输入姓名:";

    cin >> node.data.name;

       

    cout << "请输入电话:";

    cin >> node.data.phone;

       

    if (-1 == pList->LocateNode(&node))

    {

    cout << "无此联系人!" << endl;

    return;

    }

    Node temp;

    pList->ListDelete(pList->LocateNode(&node),&temp);

    }

       

       

    运行一览:

       

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    1026. 程序运行时间(15)
    C语言字符串/数组去重
    1025. 反转链表 (25)
    1024. 科学计数法 (20)
    1023. 组个最小数 (20)
    1022. D进制的A+B (20)
    1021. 个位数统计 (15)
    1020. 月饼 (25)
    前端001/正则表达式
    SSM001/构建maven多模块项目
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/6830437.html
Copyright © 2011-2022 走看看