数据结构C++复习笔记
数据结构复习(c++)
线性表
1.线性表
线性表是一个有限序列。
线性表基于数组存储的表示叫做顺序表。常用数组作为表。
线性表基于指针的存储表示叫做链表。通常有单链表、双链表、循环链表等。
2.顺序表
0x01顺序表的定义和特点
假设顺序表A的起始存储位置为Loc(1),第i个表项的存储位置为Loc(i)则有:
Loc(i)=Loc(1)+(i-1)*sizeof(T)
其中,Loc(1)为第一个表项的存储位置,即数组中第0个元素的位置。
0x02顺序表的类定义及操作
顺序表存储分为静态存储和动态存储。
静态存储
#include <iostream> //顺序表静态存储
#define maxSize 100
using namespace std;
typedef int T; //typedef 类型定义
typedef struct{
T data[maxSize];
int n;
}Seqlist;
int main()
{
Seqlist a;
T m;
for(T i=0;i<3;i++){
cin >> m;
a.data[i] = m;
}
for(T i=0;i<3;i++){
cout << a.data[i] << ' '
}
return 0;
}
动态存储
typedef int T; //动态存储
typedef struct {
T *data; //指针new新的数组
int maxSize,n;
}Seqlist;
顺序表的类声明和部分操作
#include <iostream>
const int defaultSize=100;
using namespace std;
template<class T>
class SeqList{
protected:
T * data;
int maxSize;
int last;
public:
Seqlist(int sz=defaultSize); //构造函数
SeqList(SeqList<T>& L); //复制构造函数
~SeqList(){delete[]data;} //析构函数
int Size()const{return maxSize;} //常成员函数返回最大可容纳表项个数
int Length()const{return last+1;} //计算表长度
int Search(T &x)const; //查找x在表中的位置,并返回序号
int Locate(int i)const; //定位第i个表项
bool getData(int i,T&x)const; //取第i个表项的值
bool Insert(int i,T&x); //插入x在第i个表项之后
bool Remove(int i,T&X); //删除第i个表项,通过x返回表项的值
bool Isempty(); //判断是否为空表
bool Isfull(); //判断表是否为满表
void Input(); //输入
void Output(); //输出
};
const成员函数无法改变它的对象的数据成员的值,同时const成员函数无法调用非const成员函数。
1.构造函数和复制构造函数
//构造函数
template<class T>
SeqList<T>::SeqList(int sz){
if(sz>0){
maxSize=sz;
last=-1;
data=new T[maxSize]; //动态分配空间
if(data==0){
cerr <<"存储分配错误"<< endl;exit(1);
}
}
}
//复制构造函数
template<class T>
SeqList<T>::SeqList(SeqList<T>& L){
maxSize=L.Size();
last=L.Length()-1;
data=new T[maxSize];
if(data==0){
cerr << "存储分配错误" << endl;exit(1);
}
for(int i=0;i<=last;i++){
data[i]=L.data[i];
}
}
2.搜素和查找函数
//搜索函数
template<class T>
int SeqList<T>::Search(T&x)const{
for(int i=0;i<=last;i++){
if(data[i]==x)return i+1;
}
return 0;
}
//查找函数
template<class T>
int SeqList<T>::Locate(int i)const{
if(i<0||i>last+1)cout <<"查找位置错误";
else if(i>=1&&i<=last+1)return i;
}
3.插入和删除操作
//插入操作
template<class T>
bool SeqList<T>::Insert(int i,T&x){
if(last==maxSize-1)return false; //表满
if(i<0||i>last+1) return false; //插入位置不合理
for(int j=last;j>=i;j--){ //第i个元素其key值为i-1
data[j+1]=data[j];
}
data[i]=x;
last++;
return true;
}
//删除操作
template<class T>
bool SeqList<T>::Remove(int i,T&x){
if(last==-1)return false;
if(i<0||i>last+1)return false;
x=data[i-1];
for(int j=i;j<=last;j++){
data[j-1]=data[j];
}
last--;
return true;
}
4.输入输出和赋值操作
//输入操作
template<class T>
void SeqList<T>::Input(){
while(1){
cin >> last;
if(last<=maxSize-1)break;
}
for(int i=0;i<=last;i++){
cin >> data[i];
}
}
//输出操作
template<class T>
void SeqList<T>::Output(){
for(int i=0;i<=last;i++){
cout << data[i] <<" ";
}
}
0x03顺序表的应用
交运算和并运算中通常会用两个顺序表来进行操作。
3.单链表
采用链接方式来存储线性表,通常将链接方式存储的线性表称为链表。链表常用于插入或删除比较频繁,存储空间需求不定的情形。
0x01单链表的概念
单链表的一个存储结点Node包含两个部分,一部分为data域,用于存储线性表的数据元素,link部分为指针数据域,存放一个指针,该指针指向链表中下一个结点的存储地址。
链表中的第一个结点的地址可以通过链表的头指针first找到,其他结点的地址则在前驱结点的link域中,链表的最后一个结点没有后继,在结点的指针域中放一个空指针NULL。
0x02单链表的类定义
单链表通常有两个类,第一个为结点类(linknode)和链表类(list)。
复合类
//用复合类表示单链表
class List;
class LinkNode{
friend class List;
private:
int data;
LinkNode *Link;
};
class List{
private:
LinkNode *first;
public:
};
LinkNode成员不能直接使用List的私有成员,但List类所有成员都可以直接使用LinkNode的私有成员。
嵌套类
class List{
private:
class LinkNode{
private:
int data;
LinkNode *link;
};
LinkNode *first;
};
由于是嵌套定义的,LinkNode为私有成员,所以List类外部的对象和函数无法访问LinkNode类的对象。
基类和派生类
class LinkNode{
private:
int data;
LinkNode *link;
};
class List:public LinkNode{
private:
LinkNode *first;
};
struct定义LinkNode类
struct LinkNode{
int data;
LinkNode *link;
};
class List{
private:
LinkNode *first;
};
0x03单链表的插入和删除
插入主要是有两个步骤,第一步就是将newNode的指针域指向被插结点的后一个结点,(newNode->link=current->link)第二个就是被插结点的指针域指向newNode。(currnet->link=newNode)。
删除主要有三个步骤,第一个就是找出要删除的结点(del)以及其前一个结点(current),第二个就是将current的指针域指向被删结点的下一个结点,第三个就是删除要删除的结点。
//单链表的插入算法
bool List::Insert(int i,int &x){
//i=0时意味插入第一个结点之前,first指针指向新的结点
if(i==0||first==NULL){
LinkNode *newNode=new LinkNode(x);
if(newNode==NULL){
cerr << "存储空间分配错误。";exit(1);
}
newNode->link=first;
first=newNode;
}
//无论插入表中还是表尾,都是一样的操作
else{
LinkNode *current=first;
for(int j=1;j<i;j++){
if(current=NULL)break;
else current=current->link;
}
if(current==NULL){cerr<<"无效的插入位置";return false;}
else{
LinkNode *newNode=new LinkNode(x);
newNode->link=current->link;
current->link=newNode;
}
}
return true;
}
//删除算法
bool List::Remove(int i,int &x){
//将链表中的第i个元素删去,i从1开始
LinkNode* del,* current;
if(i==1){
del=first;
first=del->link;
delete del;
}
else{
current=first;
for(int j=1;j<i-1;j++){ //寻找current结点
if(current==NULL)break;
else current=current->link;
}
if(current==NULL||current->link==NULL){
cerr << "删除位置错误";exit(1);
}
del=current->link;
current->link=del->link;
}
x=del->data; //取出被删除结点中的数据值
delete del;
return true;
}
0x04带附加头结点的单链表
插入头结点的目的是在空表或非空表第一个结点之前的插入可以不作为特殊情况来处理,可以统一处理。
为了统一操作,在表的最前端设置附加头结点,本身不带数据,仅标志表头。
0x05单链表的模板类
带有附加头结点的单链表模板类的定义。
template<class T>
struct LinkNode{
T data;
LinkNode<T> *link;
LinkNode(LinkNode<T>* ptr=NULL){link=ptr;}
LinkNode(T item,LinkNode<T> *ptr=NULL){
data=item;
link=ptr;
}
};
template<class T>
class List{
private:
LinkNode<T> *first;
public:
List(){first=new LinkNode<T>;} //构造函数
List(T&x){first=new LinkNode<T>(x);} //构造函数
List(List<T>&L);
~List(){makeEmpty();}//析构函数
void makeEmpty();//置空表
};
//置空表
template<class T>
void List<T>::makeEmpty(){
LinkNode<T> *q;
while(first->link!=NULL){ //头结点的指针域不为空,就删除后面的结点
q=first->link;
first->link=q->link;
delete q;
}
}
1.插入和删除
//在附加头结点中插入算法
template<class T>
bool List<T>::Insert(int i,T&x)
{
LinkNode<T>* current=Locate(i);
if(current==NULL) return false;
LinkNode<T> * newNode=new LinkNode<T>(x);
if(newNode==NULL){
cerr << "内存分配错误"; exit(1);
}
newNode->link=current->link;
current->link=newNode;
return true;
}
//删除算法
template<class T>
bool List<T>::Remove(int i,T& x)
{
LinkNode<T>* current=Locate(i-1);
if(current==NULL||current->link==NULL)return false;
LinkNode<T>* del=Locate(i);
current->link=del->link;
x=del->data;delete del;
return true;
}
2.计算链表长度和定位函数
//计算带有附加头结点的链表的长度
template<class T>
int List<T>::Length(){
LinkNode<T>*p=first->link;int count=0; //p指向第一个结点
while(p!=NULL){
p=p->link;
count++;
}
return count;
}
//定位函数,并返回该结点的地址
template<class T>
LinkNode<T>* List<T>::Locate(int i){
LinkNode<T>* p=first; int j=0;
while(p!=NULL&&j<i){
p=p->link;
j++;
}
return p;
}
3.前插法建立链表
主要有两步操作
(1)生成新结点,将读入数据存放到新结点的data域中。
(2)将该新结点插入到链表的前端,直到读入结束符为止。
template<class T>
void List<T>::inputFront(T endtag)
{
LinkNode<T>* newNode;
T val;
makeEmpty();
cin >> val;
while(val!=endtag){
newNode=new LinkNode<T>(val);
if(newNode==NULL)cerr << "内存分配错误";exit(1);
newNode->link=first->link; //newNode的指针域现在为空
first->link=newNode;
cin >> val;
}
}
int
4.后插法建立链表
template<class T>
void List<T>::inputRear(T endtag)
{
LinkNode<T>* newNode,*last;
T val;
makeEmpty();
cin >> val;
last=first;
while(val!=endtag){
newNode=new LinkNode<T>(val);
if(newNode==NULL)cerr << "内存分配错误";exit(1);
last->link=newNode;
last=newNode;
cin >> val;
}
last->link=NULL;
}