一、c++与线性表
入门篇
基本概念:
- 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。
- 数据元素:是由数据组成的、有一定意义的基本单位,有时也称为record.
- 数据对象:有相同性质的数据元素的集合,是数据的子集。
- 数据:可以输入到计算机,能被程序处理的符号,包括整数型,实型以及字符型等。
数据结构的分类:
从逻辑上而言:表示数据元素之间的关系。
- 集合结构
- 线性结构
- 树形结构
- 图形结构
从存储的物理结构,也就是数据在计算机中的存储形式:
- 顺序存储
- 链式存储
c++动态存储分配:new与delete
int *p = new int ; // 会自动进行进行类型转换,得到int类型的指针
Point *p = new Point[100] ; //得到一个数组指针
delete []p ; //释放该数组
delete p ; // 仅仅释放指针p所指向的第一个,而失去其他位置的空间,而无法复用他们
c++ friend 友元
类的封装过程中包含了private函数以及public函数。为了使另外的类能够访问本类的private函数,可以通过友元对其“赋予权限”。
class List; //类的前视说明
class LinkNode{
friend class List;
private:
int data;
}
class List{
public:
//链表公共类
private:
//链表私有类}
友元的关系不具有对称性。这里,LinkNode类中生命了List是它的友元,哪里List的所有成员都可以直接使用LinkNode的私有成员,反之,LinkNode则不能。
顺序表与数组之间的差别
- 顺序表: 各个表项的逻辑循序与其存放的物理顺序一致,第i个元素存放在data[]的第i-1个位置。各个表元素必须相继存放在一个连续的空间内;
- 数组:只有两个按元素的下标存或者取,这样,以为数组中的数据可能是跳跃式的、不连续存放的。
顺序表的应用:两个顺序表的‘与’运算和‘并’预算的实现。
顺序表的优缺点评价,优点:
节点间逻辑简单,无需额外的空间存储逻辑关系,存储利用率高;课方便的存取任一节点,存取速度快;
缺点:插入或删除元素时效率低,平均需要移动一半的元素;需要连续的空间。
单链表
单链表的存储结构
typedef struct Node{
ElemType data; //可以是整形数据或者结构体的数据类型
struct Node *next ;
};
单链表的插入与删除
插入:在表的第一个节点;插入在首尾节点;插入在尾节点。
删除:删除第一个节点;删除中间或尾部。
整表创建
举例:前插入法
for (int i = 0; i < count; i++)
{
Node *p = new Node ;
p->data = i ;
p->next = head->next ;
head->next = p ;
}
整表删除
Status ClearList(LinkList *L){
LinkList *p,*q;
p = L ;
while(p){
q = p->next;
delete(p) ;
p = q ;
}
return ok ;
}
静态链表
链表需要进行指针操作,那么能不能将这种指针的方式来进行通用化,使一些没有指针的编程语言也能利用这种操作呢。这里可以利用数组。
让数组的元素由两个数据域组成,data与cur。数据域用来表示data,cur来表示next指针,存放该元素的下一个数组在数组中的位置,称cur为游标。用数组来描述的链表叫做静态链表。
双向链表
循环链表
栈
栈的链式存储结构
// 代表栈内的一个元素
typedef struct StackNode{
ElemType data ; //ElemType代表了数据类型
struct StackNode *next ;
}StackNode, *LinkStackPtr;
typedef struct LinkStack{
LinkStack top; //指向栈顶指针
int count;
}
//example
// 在栈中插入元素
bool Push(LinkStack *S, ElemType e){
// 新建一个栈单元
LinkStackPtr ss;
ss->data = e ;
ss->next = S->top;
S->top = ss;
S->count ++ ; //记录栈内元素的数量
return true ;
}
//同理,有出栈操作为(将栈顶值返回为e并删除栈顶)
bool Pop(LinkStack *S, ElemType *e){
// 新建一个栈单元,使其获取栈顶
LinkStackPtr p;
if(StackEmpty(*S))
return error;
e = S->top->data ;
p = S->top;
S->top = S->top->next ;
free(p) ;
s->count -- ;
return true ;
}
队列(循环队列)
队列中,如果数据要进行频繁的出队入队操作,链式结构可能需要频繁的进行内存分配等操作,带来很大的开销。
循环队列就很适合这样的问题,它的主要特点在于:定义了队列的maxsize,以及由两个指针来指示队列中数据。
下面给出构造循环队列的类的代码。
class SqQueue{
//在public中存放构造函数、析构函数以及其他的队列操作函数
public:
SqQueue(int size); //size为打算存放的数据量
virtual ~SqQueue();
bool EnQueue(int data);
bool DeQueue(int* pRecvData=nullptr);
void DeTraverse(); //遍历队列中存储的数据元素
int GetSize();//获取队列中的数据量
bool IsFull();
bool IsEmpty();
private:
ElemType data[size] ;
int front ; //头指针
int rear ; //尾指针
int length ; //队列的实际长度
};
链队列
队列的链式存储结构,其实就是线性表的单链表,只不过它能尾进头出罢了。
下面给出链队列的结构。这个需要定义两个东西,其一是队列中每一个element结构的定义,按照链式队列,中间的每一个元素都包含了数据部分以及指针部分。其二则是队列结构的定义,队列是有许多个element组成的,有许多的成员函数,以及有头指针和尾指针。队列元素可用用class定义或者struct结构体进行定义,这里,我均采用class来定义
template <class T >
class QNode{
T data; //
QNode *next ;
};
template <class T>
class LinkQueue{
public:
//定义成员函数
void InitQueue(Linkqueue *Q); //队列的初始化
...
private:
QNode *front; //尾指针
QNode *rear; //尾指针
};
//队列的初始化
void Linkqueue::InitQueue(Linkqueue *Q) {
QNode *p = new QNode;
p->next = NULL;
Q.front = p;
Q.rear = p;
}
串
串中,需要掌握KMP模式匹配算法。
这个算法可以参考知乎上的一些教程。
其实就是一个匹配前缀和后缀最大相等的长度。前缀:除去最后一个字符,后缀:除去第一个字符。
树
树的存储结构
双亲表示法
孩子表示法
孩子兄弟表示法
参考文献:
- 《大话数据结构》程杰
- 《数据结构》 殷人昆