线性表

------------------------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 =newint[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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

程序 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指针

booloperator==(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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

程序 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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

程序 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);

booloperator==(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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

程序 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)

{

case1:

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

createPerson(p);

cout << endl;

break;

case2:

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

deletePerson(p);

cout << endl;

break;

case3:

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

p->ListTraverse();

break;

case4:

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

cout << endl;

break;

default:

break;

}

}

  

  

delete p;

p = NULL;

  

system("pause");

return0;

}

  

  

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】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值