数据结构代码精讲汇总

写在前面
  你们好,我是小庄。很高兴能和你们一起学习数据结构。如果您对Java感兴趣的话可关注我的动态.
  写博文是一种习惯,在这过程中能够梳理知识和巩固知识点。

数据结构目录

    • 1、表
    • 2、栈
    • 3、队列
    • 4、二叉树


1、表

思路:

  1. 定义表结构
  2. 初始化空表
  3. 对表进行操作

顺序表

typedef int ListItem;//设置表元素的类型
/**表的基本结构**/
typedef struct list{
int n;//表的长度
int curr;//当前位置
int maxsize;//数组的上界
ListItem *table;//表数组
}List;
//-------------------表结构定义end------------------------
/**初始化表结构**/
List ListInit(int size)//size表示表结构长度
{
	List L=(List)malloc(sizeof *L);//给结构体分配一个空间
	L->table=(ListItem *)malloc(size*sizeof(ListItem));//给表数组分配长度为size,size是动态调用设置的值
	L->maxsize=size;//设置数组的上界(数组总长度),以便插入表判断数组是否是满的(判断是否会越界)。
	L->n=0;//表是空表,所以长度为0
	/**这里不对curr进行操作**/
	return L;//返回空表
}
//--------------------初始化表end----------------------
/**插入表**/
void insert(int k,ListItem x,List L)//k表示插入位置,x表示插入元素,L表示插入的表
{
	//先判断插入的位置是否合法,要保证表的连续性
	if(k<0||k>L->n)	return ;//如果长度小于0或者大于表的长度,直接返回
	for(int i=L->n-1;i>=k;i--){
		//最后一个元素开始,把前面的值赋值给后面(元素移动方向从前往后)这里的i+1表示最后一个元素下标
		L->table[i+1]=L->table[i];
		L->table[k]=x;//在位置k插入元素
		L->n++;//表长度+1
	}
}
//----------------------插入表元素end---------------------
/**删除表**/
//这里返回删除的元素
ListItem ListDelete(int k,List L)//k表示删除位置,L表示删除的表
{
	if(k<0||k>L->n)return 0;//返回值为0,表示删除失败,删除位置不合法
	ListItem x=L->table[k-1];
	for(int i=k;i<L->n;i++){
		//删除的元素开始,把后面的值赋值给前面(元素移动方向从后往前)
		L->table[i-1]=L->table[i];
		L->n--;//表长度-1
		return x;//返回删除的元素
	}
}
//---------------------删除表元素end----------------------
//由于顺序表修改和查询操作都比较简单,这里就不进行介绍了

链表

//ListItem就是int类型
typedef struct node *link;//表结点指针类型
typedef struct node{
	ListItem element;//表元素
	link next;//这里等于node *next;指向下一个结点的指针
}Node;
//创建一个新结点
link NewNode(){
	return (link)malloc(sizeof(Node));
}
//表结构
typedef struct llist{
	link first;//表首指针
	link curr;//当前结点指针
	link last;//表尾指针
}Llist;
//-------------------表结构end-----------------------
/**初始化链表**/
Llist ListInit(){
	Llist L=(Llist)malloc(sizeof *L);//动态生成空间长度的链表
	L->first=0;//链表表首置为0;
	return L;//返回空表
}
//----------------初始化链表end----------------------
/**插入**/
void Insert(int k,ListItem x,Llist L)//k为位置,x为元素,L为链表
{
	if(k<0) return ;//位置不合法直接返回
	link p=L->first;
	for(int i=1;i<k&&p;i++)//p为0时退出循环
	{
		//遍历链表
		p=p->next;
	}	
	//创建一个新结点存放新数据
	link y=NewNode();
	y->element=x;//把数据存放进去
	//判断链表是否为空
	if(k){//不是空表
		y->next=p->next;//把新结点的下一个结点连接原链表的下一个结点
		p->next=y;//把原链表的下一个结点连接新结点
	}
	else{
		y->next=L->first;//把新结点的下一个结点连接表首
		L->first=y;//把新结点做为表首
	}
}

2、栈

栈的特点: 先进后出,只能在栈顶进行插入和删除操作
思路

  1. 定义栈结构
  2. 初始化栈空间
  3. 对栈顶操作前,判断栈是否溢出(上溢或下溢)

数组实现栈

//StackItem设置为int类型
typedef int StackItem;//指定栈元素的类型
typedef struct astack *Stack;//指定栈指针类型
typedef struct astack{
	int top;//栈顶
	int maxtop;//栈空间上限,用于判断是否上溢
	StackItem *data;//存储栈元素的数组,指针可表示数组
}Astack;
//---------------------栈结构end-----------------------
/**初始化栈**/
Stack StackInit(int size)//size表示空间长度
{
	Stack S=(Stack)malloc(sizeof *S);//分配栈空间
	S->data=(StackItem)malloc(size*sizeof(StackItem));//分配数组空间
	S->top=-1;//设置栈顶的初始值
	S->maxtop=size;//设置栈空间上限
	return S;
}
//-----------------初始化栈end-------------------------
/**入栈**/
void Push(StackItem x,Stack s)//x为入栈的元素,s为栈
{
	//先判断栈是否已经满了,即栈顶达到上限
	if(S->top=S->maxtop){
		return;//直接返回
	}else{//没满,栈顶往上移动一个单位,数据入栈
		S->top++;
		S->data[S->top]=x;//入栈到新的空间
	}
}
//----------------入栈end-----------------------
/**出栈**/
StackItem Pop(Stack S){
	//判断栈是否下溢
	if(S->top<0){//已经是空栈了,不能再出栈了
		return 0;
	}else{
		//数据先出栈,栈顶往下移动一个单位
		StackItem x=S->data[S->top];
		S->top--;//栈顶往下移动
		return x;//返回栈顶元素	
	}
}
//------------------出栈end----------------------
/**释放栈空间**/
void StackFree(Stack S){
	free(S->data);
	free(S);
}
//----------------释放栈空间end---------------------

指针实现栈
特点:对链首进行

typedef int StackItem;//栈元素类型
typedef struct snode *slink;//栈结点指针
//栈链表结构
typedef struct snode{
	StackItem element;//栈元素
	slink next;//指向下一个指针
}StackNode;
//创建新结点
StackNode NewNode(){
	StackNode node=(StackNode)malloc(sizeof(StackNode));//分配结点空间
	return node;
}
//栈结构
typedef struct lstack *Stack;//栈指针元素
typedef struct lstack{
	slink top;//设置栈顶
}Lstack;
//-----------------栈结构end----------------------------
/**初始化栈**/
Stack StackInit(){
	Stack S=(Stack)malloc(sizeof*S);
	S->top=0;
	return S;
}
//-----------------初始化栈end--------------------------
/**入栈**/
void Push(StackItem x,Stack S){
	slink p=NewNode();//分配一个结点空间
	p->element=x;//将数据存入结点	
	p->next=S->top;//把新结点做为链首
	S->top=p;//将栈顶指向链首
}
//-------------------入栈end---------------------------
/**出栈**/
StackItem Pop(Stack S){
	if(S->top==0){
		return 0;
	}else{
		StackItem x=S->top->element;//获取当前栈顶的元素,做为出栈的元素
		/**将栈顶指向下一个元素**/
		slink p=S->top;
		S->top=p->next;
		free(p);//释放出栈的空间
		return x;//返回出栈元素
	}
}
//-------------------出栈end--------------------------

3、队列

队列的特点:先进先出
思路:

  1. 定义队列结构,这里要设置队首和队尾的指针
  2. 初始化空间
  3. 队首进行出队,队尾进行入队

指针实现队列

typedef int QItem;//队列元素类型
typedef struct qnode *qlink;//队列结点指针
typedef struct qnode{
	QItem element;//队列元素
	qlink next;//下一个元素
}Qnode;
//创建新结点
Qnode NewQnode(){
	qlink q=(Qnode)malloc(sizeof(Qnode));//分配结点空间
	return q;
}
//队列结构
typedef struct lqueue *Queue;
typedef struct lqueue{
	qlink frist;//队首指针
	qlink end;//队尾指针
}Lqueue;
//-------------------队列结构end----------------------
/**初始化队列**/
Queue QueueInit(){
	Queue Q=(Queue)malloc(sizeof *Q);//分配队列空间
	//将队首队尾归为0;
	Q->frist=Q->end=0;
	return Q;
}
//---------------初始化队列end-----------------------
/**入队**/
void EnterQueue(QItem x,Queue Q){//x为元素,Q为队列
	qlink q=NewQnode();//分配结点空间
	q->element=x;//将数据添加到结点
	q->next=0;//先将下一结点置为空
	if(Q->frist){//队列不为空的条件
		Q->end->next=q;//将原队尾往后面插入新的结点
		Q->end=q;//重新将队尾指向新结点
	}else{//队列为空,直接指向队尾的是队首
		Q->frist=q;//队首指针指向新结点
		Q->end=q;//队尾指针指向新结点
	}
}
//-----------------入队end--------------------------
/**出队**/
QItem DeleteQueue(Queue Q){
	//先判断队列是否为空
	if(Q->frist==0){return 0;//直接返回0}
	QItem x=Q->frist->element;//获取当前队首的元素
	qlink p=Q->frist;//存放当前队首的结点,用于释放空间
	Q->frist=Q->frist->next;//将队首指向下一个结点
	free(p);
	return x;//返回出队的元素;
}
//-----------------出队end--------------------------

循环数组实现队列
特点:运用到模运算

typedef int QItem;//队列元素
typedef struct aqueue *Queue;//队列指针类型
typedef struct aqueue{
	int maxsize;//循环数组的大小
	int frist;//队首游标
	int end;//队尾游标
	QItem *table;//循环数组
}Aqueue;
//----------------队列结构end--------------------
/**初始化队列**/
Queue QueInit(QItem size){
	Queue q=(Queue)malloc(sizeof(Queue));//分配队列空间
	q->table=(QItem)malloc(size*sizeof(QItem));//分配数组空间
	q->maxsize=size;//设置循环数组大小
	q->frist=q->end=0;//设置队首队尾初始值
	return q;
}
//--------------初始化队列end----------------------
/**入队**/
void EnterQueue(QItem x,Queue Q){
	//判断队列是否满,设置空出一个空间
	QItem b=((Q->end+1)%Q->maxsize==Q->frist)?1:0;//满时模为0;否则为1
	if(b){return ;}//满的时候直接返回
	else{
		Q->end=(Q->end+1)%Q->maxsize;//重新设置队尾
		Q->table[Q->end]=x;//将数据入队
	}	
}
//-----------------入队end-------------------------
/**出队**/
QItem deleteQueue(Queue Q){
	//判断队列是否为空
	if(Q->frist==Q->end)return 0;
	QItem x=Q->table[Q->frist];//保存出队的数据
	Q->frist=(Q->frist+1)%Q->maxsize;//重新设置队首,往后移动一个单位
	return x;//返回出队数据
}
//-----------------出队end---------------------------

4、二叉树

特点: 用递归的思想
指针建立二叉树

typedef int TreeItem;//树元素类型
typedef struct tnode *tlink;
typedef struct tnode{//树结点
	TreeItem element;//结点元素
	tlink left;//左子树
	tlink right;//右子树
}Tnode;
//创建新结点
Tnode Newtnode(){
	Tnode t=(Tnode)malloc(sizeof(Tnode));//分配结点
	return t;
}
typedef struct tree{//树结构
	tlink root;//树根
}Tree;
//----------------树结构end-----------------------------
/**初始化树**/
Tree TreeInit(){
	Tree t=(Tree)malloc(sizeof *t);//分配树空间
	t->root=0;//设置默认根结点的值为0
	return t;
}
//-----------------初始化end----------------------------
/**建立一个二叉树**/
void MakeTree(TreeItem x,Tree Left,Tree Right,Tree T)//x为结点数据,Left为左子树,Right为右子树,T为新树
{
	T->root=Newtnode();//分配一个结点空间
	T->root->element=x;//把数据放到结点中
	T->root->left=Left->root;//左子树连接原子树的根结点
	T->root->right=Right->root;//右子树连接原子树的根结点
	Left->root=Right->root=0;//设置新树的左右子树的值为空
}
//------------------建树end----------------------------

二叉树遍历
遍历特点:根据根结点的位置不同分为前序、中序、后序遍历

typedef int TreeItem;//树元素类型
typedef struct bnode *blink;
typedef struct bnode{
	TreeItem element;//结点元素
	tlink left;//左子树
	tlink right;//右子树
}Bnode;
/**前序遍历**/
void PreOrder(blink t){
	if(t){//树非空
		printf("%d",t->element);//输出结点的元素值,这里是int类型
		PreOrder(t->left);
		PreOrder(t->right);
	}
}
//-----------------前序遍历end------------------------
/**中序遍历**/
void PreOrder(blink t){
	if(t){//树非空
		PreOrder(t->left);
		printf("%d",t->element);//输出结点的元素值,这里是int类型
		PreOrder(t->right);
	}
}
//----------------中序遍历end-------------------------
/**后序遍历**/
void PreOrder(blink t){
	if(t){//树非空
		PreOrder(t->left);
		PreOrder(t->right);
		printf("%d",t->element);//输出结点的元素值,这里是int类型
	}
}
//-----------------后序遍历end-------------------------

二叉搜索树
特点:

  1. 设 t 是二叉树的一个节点,如果 x 是左子树的一个节点,则t.key>=x.key。如果y是右子树的一个节点,则t.key<=y.keykey表示节点的值
  2. 中序排序是有序的
  3. 相比普通二叉树的树结构多一个父结点

简单来讲特点1: 左子树比父节点小,右子树比父节点大。

typedef int TreeItem;//二叉搜索树元素类型
typedef struct snode *slink;//二叉搜索树结点的指针类型
//二叉搜索树的结点
typedef struct snode{
	TreeItem element;//二叉树结点元素
	slink left;//左子树
	slink right;//右子树
	slink parent;//父节点指针
}Snode;
//创建一个新结点
Snode Newsnode(){
	Snode s=(slink)malloc(sizeof(Snode));//分配一个结点空间
	return s;//返回新结点
}
typedef struct tree{//树结构
	slink root;//树根
}Tree;
/**初始化树**/
Tree TreeInit(){
	Tree t=(Tree)malloc(sizeof *t);//分配树空间
	t->root=0;//设置默认根结点的值为0
	t->left=0;
	t->right=0;
	t->parent=0;
	return t;
}
//-----------------初始化end----------------------------
/**二叉搜索树插入操作**/
int SInsert(TreeItem x,slink t){
	if(t->root==0){//空树
		t->root->element=x;//把数据直接插入树根结点
		return 1;//返回状态
	}else if(x<t->root->element){//数据比根结点的值小,插入左边
		if(t->left!=0){//如果不是叶子,则进行递归
			Sinsert(x,t->left);//递归操作
		}
		Snode r=NewSnode();//创建新结点
		r->element=x;//将数据放入新结点
		t->left=r;//往左边插入结点
		return 1;//返回状态
	}
	else if(x>t->root->element){//数据比根结点的值大,插入右边
		if(t->right!=0){//如果不是叶子,则进行递归
			Sinsert(x,t->right);//递归操作
		}
		Snode r=NewSnode();//创建新结点
		r->element=x;//将数据放入新结点
		t->right=r;//往右边插入结点
		return 1;//返回状态
	}else{
		return 0;//返回错误状态
	}
}
//-----------------插入操作end----------------------

单独讲二叉搜索树删除
分三种情况

  1. 删除结点是叶子结点,直接删除
  2. 删除结点只有一个孩子结点:a、根结点 b、子结点
  3. 删除结点有两个孩子结点:左子树最大值的结点与该结点替换,并删除替换掉的结点,把替换掉的结点的子结点连接父结点

注:树的构造用的是上面的代码

slink DeleteTreeNode(TreeItem x,slink node){
	//情况一
	if(node.left==node.right==0){//没有左右结点,说明是叶子结点或者是根结点
		if(node.parent==0){//说明没有是根结点
			node=0;
		}else if(x > node.element){//是右孩子
			node.parent.right=0;
		}
		else{
			node.parent.left=0;
		}
	}
	//情况二
	else if(node.left!=0&&node.right==0){
		node=node.left;
	}else if(node.left==0&&node.right!=0){
		node=node.right;
	}
	//情况三
	else{
		slink max;//用于存放删除结点的左子树最大的结点
		while(node.left){//遍历最右的叶子结点
			max=node.left;//存左子树最大结点
			node.left=node.right;
		}
		node.element=node.left.element//替换结点的值
		node=node.left;//把删除的结点指向左子树最大的叶子结点
		if(node.right!=0){//变为情况二,只有右子树
			node=node.right;
		}else{//没有子树,情况一,并且自己是左子树
			node.parent.left=0;
		}
	}
}
//------------------二叉树删除操作end-----------------------

哈夫曼树

(1)、最优二叉树:

a、权值越大的叶子离根越近

b、具有相同带权结点的哈夫曼树不唯一

c、包含n个叶子结点的哈夫曼树中共有2n-1个结点

d、包含n棵树的森林要经过n-1次合并才能形成哈夫曼树,共产生n-1个新结点。

e、满二叉树不一定是哈夫曼树

(2)、如何构造一个哈夫曼树?

a、在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;

b、在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;

c、重复 a 和 b (使用递归),直到所有的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。

(3)、结构元素:weight(结点权重);parent,left,right(父结点、左孩子,右孩子在数组中的位置下标)

(4)、哈夫曼编码:

在建立哈夫曼树后,在左孩子结点设置标为0,右孩子结点设置标为1。用哈夫曼树设计总长最短的二进制前缀编码。

你可能感兴趣的:(c语言)