Fork me on GitHub

线性表

------------------------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】

posted on 2017-05-09 14:23  siwuxie095  阅读(385)  评论(0编辑  收藏  举报

导航