C++常用数据结构

                                                                                  常用数据结构


1.线性表

定义:

  • 由n个具有相同性质的数据元素组成的有穷序列

特点:

(一对一的关系)

  • 存在唯一一个称为”第一个“的数据元素,它没有直接的前驱。
  • 存在唯一一个称为“最后一个”的元素,他没有直接后驱。
  • 除第一个数据元素以外,表中的每个数据元素有且仅有一个直接前驱
  • 除第一个数据元素以外,表中的每个数据元素有且仅有一个直接后驱

​1.1顺序表

定义:

  • 用一组地址连续存储单元依次存储线性表中每个数据元素。

特点:

  •  逻辑关系相邻的两个元素在物理位置上也相邻。

Image result for 顺序表

实现:

  • c语言实现:
typedef struct {int items[LISTSIZE];int length;
} SqList;
  • length:线性表当前的长度。
  • LISTSIZE:最大容量。
  • c++实现:
​
template 
class sqlist{
public:sqlist(int maxsize=10):Maxsize(maxsize){elements=new T[maxsize];length=0;}
private:int  Maxsize;//最大容量T *elements;//元素初始地址int length;//当前有效长度
};​

基本操作:

  • 初始化顺序表

//初始化顺序表,表的长度设置为0
void InitList(SqList *L)
{L->length=0;
}
  • 求顺序表中当前元素的个数

//顺序表的长度
int ListLength(SqList * L)
{return  L->length;
}
  • 判断顺序表是否为空

//判断顺序表是否为空
bool ListEmpty(SqList *L)
{if(L->length==0)return true;elsereturn false;
}
  • 向顺序表中插入数据元素

//向顺序表中插入数据元素
bool ListInsert(SqList *L,int pos,int item)
{int i;if(L->length==LISTSIZE){printf("顺序列表已满,无法进行插入操作");return false;}if(pos<1||pos>L->length+1){printf("插入位置不合法");return false;}for(i=L->length-1;i>=pos-1;i--){L->items[i+1]=L->items[i];}L->items[pos-1]=item;L->length++;return true;}
  • 删除顺序表中的元素

//删除顺序表中的元素
bool ListDelete(SqList *L,int pos,int *item)
{int i;if(L->length==0){printf("顺序表为空表");return false;}if(pos<1||pos>L->length){printf("删除位置无效");}*item=L->items[pos-1];for(int i=pos-1;ilength-1;i++){L->items[i]=L->items[i+1];}L->length--;return true;
}
  • 查找指定元素在顺序表中的位置

//查找制定元素在顺序表中的位置
int Find(SqList L,int item)
{int pos=0;if(ListEmpty(&L)){printf("顺序表为空表,无法进行查找操作");return 0;}while(pos
  • 获取顺序表中指定位置上的数据元素

//获得顺序表中指定位置上的数据元素
int GetElem(SqList *L,int pos,int *item)
{if(ListEmpty(L))return 0;if(pos<1||pos>L->length)return 0;*item=L->items[pos-1];return 1;
}
  • 遍历顺序表
//遍历顺序表
void TravereList(SqList *L)
{int pos =0;while(poslength){printf("item1: %d",L->items[pos]);pos++;}
}

c语言版本: 


#include 
using namespace std;
#define LISTSIZE 100typedef struct {int items[LISTSIZE];int length;
} SqList;//初始化顺序表,表的长度设置为0
void InitList(SqList *L)
{L->length=0;
}//顺序表的长度
int ListLength(SqList * L)
{return  L->length;
}//判断顺序表是否为空
bool ListEmpty(SqList *L)
{if(L->length==0)return true;elsereturn false;
}//向顺序表中插入数据元素
bool ListInsert(SqList *L,int pos,int item)
{int i;if(L->length==LISTSIZE){printf("顺序列表已满,无法进行插入操作");return false;}if(pos<1||pos>L->length+1){printf("插入位置不合法");return false;}for(i=L->length-1;i>=pos-1;i--){L->items[i+1]=L->items[i];}L->items[pos-1]=item;L->length++;return true;}//删除顺序表中的元素
bool ListDelete(SqList *L,int pos,int *item)
{int i;if(L->length==0){printf("顺序表为空表");return false;}if(pos<1||pos>L->length){printf("删除位置无效");}*item=L->items[pos-1];for(int i=pos-1;ilength-1;i++){L->items[i]=L->items[i+1];}L->length--;return true;
}//查找制定元素在顺序表中的位置
int Find(SqList L,int item)
{int pos=0;if(ListEmpty(&L)){printf("顺序表为空表,无法进行查找操作");return 0;}while(posL->length)return 0;*item=L->items[pos-1];return 1;
}//遍历顺序表
void TravereList(SqList *L)
{int pos =0;while(poslength){printf("item1: %d",L->items[pos]);pos++;}
}int main()
{SqList Fibonacci;cout<<"建立顺序表"<>num;if(ListInsert(&Fibonacci,i+1,num))i++;}TravereList(&Fibonacci);int item;ListDelete(&Fibonacci,7,&item);TravereList(&Fibonacci);return 0;
}

c++版本:

#include
using namespace std;
template 
class sqlist{
public:sqlist(int maxsize=10):Maxsize(maxsize){elements=new T[maxsize];length=0;}//打印顺序表的长度int listLength(){return length;}//判断顺序表是否为空bool IsEmpty(){return length==0;}//顺序表中插入元素void InsertElement(T elem,int position){if(IsEmpty()){elements[length++]=elem;}else{for(int i=length;i>=position;i--)elements[i]=elements[i-1];elements[position-1]=elem;length++;}}//顺序表中删除元素void DelElement(T elem,int position){T del_elem=elements[position-1];for(int i=position-1;i sq(20);sq.InsertElement(10,2);sq.Printf();sq.InsertElement(30,1);sq.Printf();sq.InsertElement(50,1);sq.Printf();sq.InsertElement(100,2);sq.Printf();sq.DelElement(30,3);sq.Printf();sq.FindElement(100);return 0;
}

1.2链表 

定义:

  • 用一组任意存储单元存储线性表中的数据元素。

特点:

  • 相比顺序存储结构,链式存储结构中,除了需要存储数据元素信息之外,还需要存储它的后继元素的存储地址(指针)
  • 单链表结点的物理位置不一定连续,单链表逻辑上通过指针实现连续。
  • 单链表有一个头结点和一个尾结点,并且只有尾结点没有后继结点,其他结点有且仅有一个后继结点。
  • 只要知道了链表的头结点,就可以遍历整个链表。
     

  • 单链表的每一个结点包含两部分:数据域和指针域。
  • 表头:用来存放第一个结点的地址。
  • 表尾:表尾没有后继,所以尾结点的指针域为空指针(NULL)。

 实现:

  • c语言定义:
typedef struct node{int data;//数据域 node * next;//指针域 
}LNode,*LinkList;//LinkList是指向LNode类型数据的指针类型定义 
  • LinkList是指向LNode类型数据的指针类型定义 ,也就是说,在定义一个指向LNode类型数据的指针变量时,语句
LNode *L;

LinkList L;

是等价的。

  • c++定义:
class ListNode{public:		ListNode * next;int data;
};
class LinkList
{
public:LinkList(){node=new ListNode;node->next=NULL;}~LinkList(){delete node;}void CreateLinkListBack(int n);void CreateLinkListHead(int n);void InsertNode(int position,int elem);void removeNode(ListNode* p); ListNode* findNode(int value);void PrintLinkList();
public:ListNode *node;
};

基本操作:

  • 初始化链表
//初始化链表 
LinkList init_list() 
{LinkList L=new LNode;if(!L)return NULL;L->next=NULL;//指针域置空 return L;	
}
  • 打印链表
//打印链表
void Printf_list(LinkList &L)
{LinkList temp=L->next;while(temp!=NULL){cout<data<<"->";temp=temp->next;}} 
  •  插入数据(头插)

步骤:

  1. 先创建一个新节点s(LinkList),并给数据域赋值,
  2. 新节点的指针域指向L-next,
  3. L的指针域next指向s。

//插入数据(头插)
void create_LinkList(LinkList &L,int n)
{	while(n--){LinkList s=new LNode;cin>>s->data;s->next=L->next;L->next=s;	} 		
} 

 

注意:使用头插法插入数据后,链表中数据的顺序和插入前的数据顺序相反。 

  • 插入数据(尾插)

步骤:

  1. 先让r节点指向尾结点,
  2. 创建一个新节点s(LinkList),并给数据域赋值,且s->next为NULL,因为s为尾结点,
  3. r的指针域指向s,
  4. 更新尾节点,即r=s。

//插入数据(尾插)
void create_LinkList_back(LinkList &L,int n)
{LinkList r=L;while(n--){LinkList s=new LNode;cin>>s->data;s->next=NULL;r->next=s;r=s;}} 

 注意:使用尾插法插入数据后,链表中数据的顺序和插入前的数据顺序相同。 

  • 在指定位置插入数据
 //插入数据
void InsertList(LinkList q,int e)
{LinkList p=new LNode;p->data=e;p->next=q->next;q->next=p;}

 

  • 查找相应数据
LinkList Get_elem(LinkList &L,int e)
{LinkList temp=L->next;while(temp!=NULL){if(e==temp->data){return temp;}temp=temp->next;		}return NULL;} 

完整程序

#include using namespace std;typedef struct node{int data;//数据域 struct node * next;//指针域 
}LNode,*LinkList;//LinkList是指向LNode类型数据的指针类型定义 //初始化链表 
LinkList init_list()//引用 
{LinkList L=new LNode;if(!L)return NULL;L->next=NULL;//指针域置空 return L;	
}
//插入数据(头插)
void create_LinkList(LinkList &L,int n)
{	while(n--){LinkList s=new LNode;cin>>s->data;s->next=L->next;L->next=s;	} 		
} 
//插入数据(尾插)
void create_LinkList_back(LinkList &L,int n)
{LinkList r=L;while(n--){LinkList s=new LNode;cin>>s->data;s->next=NULL;r->next=s;r=s;}} 
//打印链表
void Printf_list(LinkList &L)
{LinkList temp=L->next;while(temp!=NULL){cout<data<<"->";temp=temp->next;}cout<data=e;p->next=q->next;q->next=p;}
LinkList Get_elem(LinkList &L,int e)
{LinkList temp=L->next;while(temp!=NULL){if(e==temp->data){return temp;}temp=temp->next;		}return NULL;} 
int main()
{LinkList L=init_list();if(!L){cout<<"初始化失败"<>num;create_LinkList_back(L,num);	Printf_list(L);	int elem;LinkList elem_list;cout<<"请输入需要查找的数"<>elem;elem_list=Get_elem(L,elem);while(!elem_list) {cout<<"没有查找到,请重新输入"<>elem; elem_list=Get_elem(L,elem);}cout<<"找到相应数据,数据地址为:"<>elem_insert; 	InsertList(elem_list,elem_insert); Printf_list(L);} return 0;
}

c++版本:

#include 
using namespace std;
class ListNode{public:		ListNode * next;int data;
};
class LinkList
{
public:LinkList(){node=new ListNode;node->next=NULL;}~LinkList(){delete node;}void CreateLinkListBack(int n);void CreateLinkListHead(int n);void InsertNode(int position,int elem);void removeNode(ListNode* p); ListNode* findNode(int value);void PrintLinkList();
public:ListNode *node;
};
//头插 
void LinkList::CreateLinkListHead(int n)
{while(n--){//插入的数据 ListNode* l=new ListNode;cin>>l->data;l->next=node->next;node->next=l;}
}
//尾插 
void LinkList::CreateLinkListBack(int n)
{ListNode* r=node;while(n--){ListNode* l=new ListNode;cin>>l->data;l->next=NULL;r->next=l;r=l;}}
//在第i个位置插入元素 
void LinkList::InsertNode(int position,int elem)
{int j;ListNode* p=node->next;for(j=1;jnext;if(p==NULL)break;}if(p==NULL &&j<(position-1))return ;
//	while(p)
//	{
//		j++;
//		if(j==position)
//			break;
//		p=p->next;
//	}ListNode* q=new ListNode;q->data=elem;q->next=p->next;p->next=q;
}
//移除节点 
void LinkList::removeNode(ListNode* p)
{if(p==NULL)return;ListNode* temp=node;while(temp->next!=p){temp=temp->next;		}temp->next=p->next;delete p;
}
//寻找某节点 
ListNode* LinkList::findNode(int value)
{ListNode* temp=node->next;while(temp){if(temp->data==value)return temp;temp=temp->next;}return NULL;}
//打印链表 
void LinkList::PrintLinkList()
{ListNode* temp=node->next;while(temp){cout<data<<"->";temp=temp->next;}std::cout<

2.栈

  • 定义:

栈(stack)是一种元素满足后进先出(Last in first out, LIFO)规则的线性表

  • 特点:

  • 栈的本质是一种线性表, 但是具有其特殊的操作要求——元素后进先出。这种要求也决定了栈的操作是在表尾进行
  • 栈底(bottom):栈的表头
  • 栈顶(top):将栈的表尾,所以栈的所有操作都是在栈顶进行的。
  • 添加元素,称为入栈(push);删除元素,称为出栈(pop)。
  • 实现:

栈可以用顺序存储实现,也可以用链式存储实现,分别成为顺序栈和链栈。但一般使用顺序存储实现,因为栈不允许在栈中间进行插入和删除,需要准寻栈特有的规则。

  • 动态实现 
typedef struct SqStack_dynamic{int* base;//栈底 int* top;//栈顶 
}SqStack_dynamic;
  • 静态实现
typedef struct SqStack_static{int data[Maxsize];int top;//栈顶 
}SqStack_static;
  • C++
class stack_static{public:stack_static(){MaxSize=10;top=0;	data= new int[MaxSize];				}void push(int e);void pop(int &e);void GetTop(int &e);private:int MaxSize;int top;int *data;
};

 操作: 

  • 初始化
void InitStack(SqStack_dynamic &stack)
{stack.base= new int[Maxsize];stack.top=stack.base;
}
  • 入栈
void push(SqStack_dynamic &stack,int e)
{if((stack.top-stack.base)>=Maxsize)return;*stack.top=e;stack.top++;
}
void stack_static::push(int e)
{data[top]=e;top++;
}
  • 出栈
void pop(SqStack_dynamic &stack,int &e)
{if(stack.base==stack.top)return;stack.top--;e=*stack.top;	
}
void stack_static::pop(int &e)
{top--;e=data[top];
}
  •  栈顶元素
int GetTop(SqStack_dynamic &stack)
{if(stack.base!=stack.top)return *(stack.top-1);elsereturn -1;
}
void stack_static::GetTop(int &e)
{if(top!=MaxSize)e=data[top-1];elsee=-1;
}

 完整程序

#include
using namespace std;
#define Maxsize 100
typedef struct SqStack_dynamic{int* base;//栈底 int* top;//栈顶 
}SqStack_dynamic;class stack_static{public:stack_static(){MaxSize=10;top=0;	data= new int[MaxSize];				}void push(int e);void pop(int &e);void GetTop(int &e);private:int MaxSize;int top;int *data;
};
void stack_static::push(int e)
{data[top]=e;top++;
}
void stack_static::pop(int &e)
{top--;e=data[top];
}
void stack_static::GetTop(int &e)
{if(top!=MaxSize)e=data[top-1];elsee=-1;
}
void InitStack(SqStack_dynamic &stack)
{stack.base= new int[Maxsize];stack.top=stack.base;
}
void push(SqStack_dynamic &stack,int e)
{if((stack.top-stack.base)>=Maxsize)return;*stack.top=e;stack.top++;
}
void pop(SqStack_dynamic &stack,int &e)
{if(stack.base==stack.top)return;stack.top--;e=*stack.top;	
}
int GetTop(SqStack_dynamic &stack)
{if(stack.base!=stack.top)return *(stack.top-1);elsereturn -1;
}
int main()
{SqStack_dynamic stack;InitStack(stack);push(stack,2);push(stack,5);push(stack,10);std::cout<<"------dynamic stack-----"<

 3.树

1.定义

树是指由 n(n≥0)个结点组成的有穷集合 D 与 D 上的关系的集合 R构成的结构,通常我们用 T 表示。树需要满足以下 3 个条件:

  • 当 n=0 时,该结点集合为空,该树被称为空树
  • 在任意的非空树中,有且仅有一个根结点 root。
  • 当 n>1 时 ,除根结点以外的其余结点可以分为 m(m>0)个不相交的子集D1,D2,D3,…,Dm, 其中每一个这样的子集 Di(i≤m)本身又是一棵树, 称为根结点 root的子树

注意

  • 树的定义是一个递归定义,因为树是由根结点子树构成的,而每一个子树也是一棵树。
  • 从图中我们还了解,树结构在形式上最主要的特点就是分层特点具有一定的排列顺序
     

 2.树的表示方法

  • 树形表示法

  • 文氏图表示法

3.基本术语

  • 结点的度

结点拥有的直接子树数目。如图 A结点的度为3,B结点的度为2,c结点的度为1,D结点的度为3,E、F、G、H、I 以及J度都为0

  • 树的度

树中各结点度的最大值称为该树的度。如上图,树的度为3.

  • 分支结点

度不为 0 的结点称为分支结点或普通结点。

  • 叶子结点

度为 0 的结点称为叶子结点或终端结点。由此可知叶子结点没有子结点。

  • 结点的层次

从树的根结点开始,根结点为第 1 层,根结点的子结点为第 2 层,第 2 层结点的子结点为第 3 层……

  • 树的深度

树中结点的最大层次数被定义为该树的深度或高度。

  • 森林

n(n≥0)棵不相交的树的集合称为森林。

4.基本结构 

  • 二叉树具有的几种形态

5.性质 

6. 存储方式

  • 顺序存储

顺序存储将二叉树的每一个节点和数组的编号一一对应,根据这个编号,可以找到该结点的父结点和孩子结点

BinaryTree[0]表示该树的根结点,从根结点开始我们可以按照性质 6 去检索该树的所有结点,比如一个有 20 个结点的
二叉树, 根据性质 6 编号为 10 的结点的父结点为编号为 5 的结点,其左孩子结点为编号为20 的结点,其没有右孩子结点。

由此可以看出:

  • 如果二叉树是一棵完全二叉树的话,顺序结构的优点就非常明显,它能紧凑的存储二叉树,而且可以随机存取二叉树的某个结点,只需要根据性质 6 计算出该结点的元素下标即可;
  • 如果是非完全二叉树,则顺序存储结构就存在空间的浪费,它会为不存在的结点分配空间,而且如果二叉树规模很大,在实际应用中,内存里也很难找出相应的连续存储空间。
     
  • 链式存储

二叉树的节点一般定义为:数据部分、左子树指针右子树指针,用指针将节点串起来,其中 l 表示左孩子指针, r 表示右孩子指针,∧表示空指针

优点:

  • 能够很直观地表示二叉树的逻辑结构;
  • 在二叉树不是完全二叉树的时候,链式结构不会像顺序存储结构一样产生空结点,而且链式存储结构能很好地利用内存中的碎片空间,提高内存空间利用效率

缺点:

  • 链式存储结构不能随机存取某个结点,要找到某个结点,必须从根结点开始沿着指针往叶子结点遍历
     

7.基本操作

参考:

  • 二叉树的前中后和层序遍历详细图解(递归和非递归写法)
  • 二叉树前序、中序、后序遍历非递归写法的透彻解析
  • 构建一棵树,即初始化操作
#include
using namespace std;
class BinaryTreeNode{
public://initBinaryTreeNode(){data=NULL;lchild=NULL;rchild=NULL;	}BinaryTreeNode(int n){data=new int;*data=n;lchild=NULL;rchild=NULL;}
private:int *data;//dataBinaryTreeNode* lchild;//left child tree BinaryTreeNode* rchild;//right child tree
};
int main()
{BinaryTreeNode bt(5);	return 0;	
} 
  • 前序遍历(根、左、右)
//递归
void preorderTraversal(BinaryTreeNode *node)
{if(!node)return;std::cout<data<<" ";preorderTraversal(node->lchild);preorderTraversal(node->rchild); 
}
//迭代
void preoder(BinaryTreeNode *root)
{BinaryTreeNode *cur=root;std::stacks;while(cur||!s.empty()){//遍历直到最左叶子节点 while(cur){std::cout<data<<" "; s.push(cur);cur=cur->lchild;}//while结束时,cur=NULL//cur为左子节点 cur=s.top();s.pop();cur=cur->rchild;}}
  • 中序遍历(左、根、右)
	void midorderTraversal(BinaryTreeNode *node){if(!node)return;preorderTraversal(node->lchild);std::cout<data<<" ";preorderTraversal(node->rchild); }void midoder(BinaryTreeNode *root){BinaryTreeNode *cur=root;std::stacks;while(cur||!s.empty()){//遍历直到最左叶子节点 while(cur){s.push(cur);cur=cur->lchild;}//while结束时,cur=NULL//cur为左子节点 cur=s.top();std::cout<data<<" "; s.pop();cur=cur->rchild;}}
  • 后序遍历(左、右、根)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章