C++常用数据结构

线性表

定义:

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

特点:
(一对一的关系)

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

 

1.1 顺序表

定义:
  • 用一组地址连续存储单元依次存储线性表中每个数据元素。
特点:
  •  逻辑关系相邻的两个元素在物理位置上也相邻。

  • c++实现:
​
template <typename T>
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;
    else
        return 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;i<L->length-1;i++)
    {
        L->items[i]=L->items[i+1];
    }
    L->length--;
    return true;
}
  • 获取顺序表中指定位置上的数据元素

//获得顺序表中指定位置上的数据元素
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(pos<L->length)
    {
        printf("item1: %d",L->items[pos]);
        pos++;
    }
}
c++版本:
#include<iostream>
using namespace std;
template <typename T>
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<length-1;i++)
            elements[i]=elements[i+1];
        length--;
    }
    //查找元素的位置
    int FindElement(T elem)
    {
        int i;
        for(i=0;i<length;i++)
        {
            if(elements[i]==elem)
            {
                std::cout<<"i: "<<i<<std::endl;
                break;
            }
        }
        if(i==length)
        {
            std::cout<<"find not"<<std::endl;
            return -1;
        }
        else
        {
            std::cout<<"find yes"<<std::endl;
            return i;
        }
    }
    //打印
    void Printf()
    {
        for(int i=0;i<length;i++)
            std::cout<<elements[i]<<" ";
        std::cout<<std::endl;
    }
private:
    int  Maxsize;//最大容量
    T *elements;//元素初始地址
    int length;//当前有效长度
};
int main()
{
    sqlist<int> 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++定义:
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<<temp->data<<"->";
		temp=temp->next;
	}
 } 
  •  插入数据(头插)
    • 步骤:

      • 先创建一个新节点s(LinkList),并给数据域赋值,
      • 新节点的指针域指向L-next,
      • 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 <iostream>
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;j<position-1;j++)
	{
		p=p->next;
		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<<temp->data<<"->";
		temp=temp->next;
	}
	std::cout<<std::endl;
}
 
int main()
{
	LinkList list;
	list.CreateLinkListHead(5);
	list.PrintLinkList();
	list.InsertNode(4,9);
	list.PrintLinkList();
	ListNode* address=list.findNode(2);
	list.removeNode(address);
	list.PrintLinkList();
	return 0;
}

  • 定义:

栈(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);
	else
		return -1;
}
void stack_static::GetTop(int &e)
{
	if(top!=MaxSize)
		e=data[top-1];
	else
		e=-1;
}
完整程序
#include<iostream>
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];
	else
		e=-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);
	else
		return -1;
}
int main()
{
	SqStack_dynamic stack;
	InitStack(stack);
	push(stack,2);
	push(stack,5);
	push(stack,10);
	std::cout<<"------dynamic stack-----"<<std::endl;
	
	int num;
	pop(stack,num);
	std::cout<<num<<" ";
	pop(stack,num);
	std::cout<<num<<" ";
	pop(stack,num);
	std::cout<<num<<" ";
	
	stack_static stack_s;
	stack_s.push(5);
	stack_s.push(10);
	stack_s.push(20);
	std::cout<<std::endl;
	std::cout<<"------static stack-----"<<std::endl;
	int num_s;
	stack_s.pop(num_s);
	std::cout<<num_s<<" ";
	stack_s.pop(num_s);
	std::cout<<num_s<<" ";
	stack_s.pop(num_s);
	std::cout<<num_s<<" ";
	
	return 0;	
} 

 树

定义

树是指由 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<iostream>
using namespace std;
class BinaryTreeNode{
public:
	//init
	BinaryTreeNode(){
		data=NULL;
		lchild=NULL;
		rchild=NULL;	
	}
	BinaryTreeNode(int n){
		data=new int;
		*data=n;
		lchild=NULL;
		rchild=NULL;
	}
private:
	int *data;//data
	BinaryTreeNode* 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<<node->data<<" ";
	preorderTraversal(node->lchild);
	preorderTraversal(node->rchild); 
}
//迭代
void preoder(BinaryTreeNode *root)
{
	BinaryTreeNode *cur=root;
	std::stack<BinaryTreeNode *>s;
		
	while(cur||!s.empty())
	{
		//遍历直到最左叶子节点 
		while(cur)
		{
			std::cout<<cur->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<<node->data<<" ";
		preorderTraversal(node->rchild); 
	}
	void midoder(BinaryTreeNode *root)
	{
		BinaryTreeNode *cur=root;
		std::stack<BinaryTreeNode *>s;
		
		while(cur||!s.empty())
		{
			//遍历直到最左叶子节点 
			while(cur)
			{
				s.push(cur);
				cur=cur->lchild;
			}
			//while结束时,cur=NULL
			//cur为左子节点 
			cur=s.top();
			std::cout<<cur->data<<" "; 
			s.pop();
			cur=cur->rchild;
		}
		
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值