Day26.C提高(数据结构03)

Day26.C提高(数据结构03)

01、队列概念

队列是一种受限制的线性表(先进先出)
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出的线性表,允许插入的一端为队尾,允许删除的一端为队头。
队列不允许在中间部位进行操作!假设队列是q=(a1,a2,a3---an),那么a1就是队头元素,而an为队尾元素,这样我们就可以删除时,总是在a1开始,而插入时,总是在队列最后。

02、队列的链式存储实现

LinkQueue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#ifdef __cplusplus
extern "C" {
#endif

	//链表结点的数据类型
	struct QueueNode
	{
		struct QueueNode* next;
	};
	
	//链表数据类型
	struct LQueue
	{
		struct QueueNode header;//头结点
		int size;
		struct QueueNode* rear;//尾指针,始终指向链表尾部最后一个结点
	};

	typedef void* LinkQueue;
	//初始化
	LinkQueue Init_LinkQueue();
	//入队
	void Push_LinkQueue(LinkQueue queue, void* data);
	//出队
	void Pop_LinkQueue(LinkQueue queue);
	//获得队头元素
	void* Front_LinkQueue(LinkQueue queue);
	//获得队尾元素
	void* Back_LinkQueue(LinkQueue queue);
	//大小
	int Size_LinkQueue(LinkQueue queue);
	//销毁队列
	void Destroy_LinkQueue(LinkQueue queue);

#ifdef __cplusplus
}
#endif

LinkQueue.c

#include"LinkQueue.h"

//初始化
LinkQueue Init_LinkQueue()
{
	struct LQueue* queue = malloc(sizeof(struct LQueue));
	if (NULL == queue)
	{
		return NULL;
	}
	queue->header.next = NULL;
	queue->size = 0;
	queue->rear = &(queue->header);

	return queue;
}
//入队
void Push_LinkQueue(LinkQueue queue, void* data)
{
	if (NULL == queue)
	{
		return;
	}
	if (NULL == data)
	{
		return;
	}
	struct LQueue* myqueue = (struct LQueue*)queue;
	struct QueueNode* n = (struct QueueNode*)data;
	myqueue->rear->next = n;
	n->next = NULL;
	//更新尾指针
	myqueue->rear = n;
	myqueue->size++;
}
//出队
void Pop_LinkQueue(LinkQueue queue)
{
	if (NULL == queue)
	{
		return;
	}
	struct LQueue* myqueue = (struct LQueue*)queue;
	if (myqueue->size == 0)
	{
		return;
	}
	if (myqueue->size == 1)
	{
		myqueue->header.next = NULL;
		myqueue->rear = &(myqueue->header);
		myqueue->size--;
		return;
	}
	
	struct QueueNode* qFirstNode = myqueue->header.next;
	myqueue->header.next = qFirstNode->next;
	myqueue->size--;
}
//获得队头元素
void* Front_LinkQueue(LinkQueue queue)
{
	if (NULL == queue)
	{
		return NULL;
	}
	struct LQueue* myqueue = (struct LQueue*)queue;

	return myqueue->header.next;
}
//获得队尾元素
void* Back_LinkQueue(LinkQueue queue)
{
	if (NULL == queue)
	{
		return NULL;
	}
	struct LQueue* myqueue = (struct LQueue*)queue;

	return myqueue->rear;
}
//大小
int Size_LinkQueue(LinkQueue queue)
{
	if (NULL == queue)
	{
		return -1;
	}
	struct LQueue* myqueue = (struct LQueue*)queue;

	return myqueue->size;
}
//销毁队列
void Destroy_LinkQueue(LinkQueue queue)
{
	if (NULL == queue)
	{
		return;
	}
	free(queue);
	queue = NULL;
}

队列的链式存储.c

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"LinkQueue.h"

struct Person_01
{
	struct QueueNode node;
	char name[64];
	int age;
};

void test101()
{
	//初始化队列
	LinkQueue queue = Init_LinkQueue();

	//创建数据
	struct Person_01 p1 = { NULL,"aaa",10 };
	struct Person_01 p2 = { NULL,"bbb",20 };
	struct Person_01 p3 = { NULL,"ccc",30 };
	struct Person_01 p4 = { NULL,"ddd",40 };
	struct Person_01 p5 = { NULL,"eee",50 };
	struct Person_01 p6 = { NULL,"fff",60 };

	//获得队尾元素
	struct Person_01* pBack = (struct Person_01*)Back_LinkQueue(queue);
	printf("队尾元素:%s  %d\n", pBack->name, pBack->age);

	//插入队列
	Push_LinkQueue(queue, &p1);
	Push_LinkQueue(queue, &p2);
	Push_LinkQueue(queue, &p3);
	Push_LinkQueue(queue, &p4);
	Push_LinkQueue(queue, &p5);
	Push_LinkQueue(queue, &p6);

	while (Size_LinkQueue(queue) > 0)
	{
		//获得队头元素
		struct Person_01* person = (struct Person_01*)Front_LinkQueue(queue);
		//打印队头元素
		printf("Name:%s Age:%d\n", person->name, person->age);
		//弹出队头元素
		Pop_LinkQueue(queue);
	}

	//销毁队列
	Destroy_LinkQueue(queue);

}

int main(void)
{
	test101();

	system("pause");
	return EXIT_SUCCESS;
}

003.树和二叉树的概念

数的定义:

由一个或多个(n>=0)结点组成的有限集合T,有且仅有一个结点称为根(root),当n>1时,其余的结点分为m(m>=0)个互不相交的有限集合T1,T2,T3···Tm。每个集合本身又是棵树,被称作这个根的子树。

树的结构特点:

非线性结构,有一个直接前驱,但可能有多个直接后继(1:n)。
树的定义具有递归性,树中还有树。
树可以为空,即结点个数为0/

若干术语:

根:即根节点(没有前驱)
叶子:度为0的结点,即终端结点(没有后继)
森林:指m棵不想交的树的集合
有序树:结点各子树从左至右有序,不能互换(左为第一)
无序树:结点各子树可以互换位置
结点:即树的数据元素
结点的度:有几个直接后继,就是几度
结点的层次:从根到该节点的层数(根结点算第一层)
分支结点:除树根以外的结点
树的度:所有结点度中的最大值

完全二叉树

除最后一层外,每一层上的

004.二叉树的遍历

遍历方法:

牢记一个约定,对每个结点的查看都是“先左后右”

DLR				LDR				LRD
先序遍历			中序遍历			后序遍历
- DLR——>先序遍历,即先根再左再右
- LDR——>中序遍历,先左再根再右
- LRD——>后序遍历,先左再右再根
注:“先 中 后”的意思是指访问的结点D是先于还是后于子树出现,从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的,只是访问的时机不同。

二叉树的遍历,叶子结点计数,求高度,拷贝,释放(代码示例)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct BiNode
{
	char ch;
	struct BiNode* lchild;
	struct BiNode* rchild;
};

//二叉树递归遍历(先序遍历)
void recursion(struct BiNode* root)
{
	if (NULL == root)
	{
		return;
	}
	printf("%c ", root->ch);
	//递归遍历左子树
	recursion(root->lchild);
	//递归遍历右子树
	recursion(root->rchild);
}

//求二叉树的叶子结点数目
void calculateLeafNum(struct BiNode* root,int* pnum)
{
	if (NULL == root)
	{
		return;
	}
	if (root->lchild == NULL && root->rchild == NULL)
	{
		(*pnum)++;
	}
	calculateLeafNum(root->lchild,pnum);
	calculateLeafNum(root->rchild,pnum);
}

//求二叉树的高度
int getTreeHeight(struct BiNode* root)
{
	if (NULL == root)
	{
		return 0;
	}
	//求树的左子树的高度
	int lheight = getTreeHeight(root->lchild);
	//求树的右子树的高度
	int rheight = getTreeHeight(root->rchild);
	int height = lheight > rheight ? lheight + 1 : rheight + 1;
	return height;
}

//拷贝二叉树
struct BiNode* CopyBiTree(struct BiNode* root)
{
	if (NULL == root)
	{
		return NULL;
	}
	//先拷贝左子树
	struct BiNode* lchild = CopyBiTree(root->lchild);
	//再拷贝右子树
	struct BiNode* rchild = CopyBiTree(root->rchild);

	struct BiNode* newnode = malloc(sizeof(struct BiNode));
	if (NULL == newnode)
	{
		return NULL;
	}
	newnode->lchild = lchild;
	newnode->rchild = rchild;
	newnode->ch = root->ch;

	return newnode;

}

//释放拷贝的二叉树
void FreeCopiedBiTree(struct BiNode* root)
{
	if (NULL == root)
	{
		return;
	}
	//先释放左子树内存
	FreeCopiedBiTree(root->lchild);
	//再释放右子树内存
	FreeCopiedBiTree(root->rchild);
	
	free(root);
	root = NULL;
}

void test201()
{
	struct BiNode nodeA = { 'A',NULL,NULL };
	struct BiNode nodeB = { 'B',NULL,NULL };
	struct BiNode nodeC = { 'C',NULL,NULL };
	struct BiNode nodeD = { 'D',NULL,NULL };
	struct BiNode nodeE = { 'E',NULL,NULL };
	struct BiNode nodeF = { 'F',NULL,NULL };
	struct BiNode nodeG = { 'G',NULL,NULL };
	struct BiNode nodeH = { 'H',NULL,NULL };

	nodeA.lchild = &nodeB;
	nodeA.rchild = &nodeF;
	
	nodeB.rchild = &nodeC;

	nodeC.lchild = &nodeD;
	nodeC.rchild = &nodeE;

	nodeF.rchild = &nodeG;

	nodeG.lchild = &nodeH;
	
	//先序遍历
	recursion(&nodeA);
	printf("\n");

	//1.求二叉树的叶子节点数
	int num = 0;
	calculateLeafNum(&nodeA,&num);
	printf("叶子节点数:%d\n", num);

	//2.求二叉树的高度
	int height = getTreeHeight(&nodeA);
	printf("树的高度:%d\n", height);

	//拷贝二叉树
	struct BiNode* newnode = CopyBiTree(&nodeA);
	
	//先序遍历拷贝后的二叉树
	recursion(newnode);
	printf("\n");
	
	//释放拷贝的二叉树
	FreeCopiedBiTree(newnode);
}

int main(void)
{
	test201();

	system("pause");
	return EXIT_SUCCESS;
}

005.二叉树的非递归遍历

大致步骤:

1. 将根节点压入栈中,(并且给一个标识符(FALSE))
2. 开始while循环,size>0
	2.1 先从栈中弹出栈顶元素,判断元素的标志是TRUE还是FALSE,如果为TRUE,直接输出,本轮循环结束。如果是FALSE,就把当前结点以及左子树和右子树压栈。(第一次压栈的标识符为FALSE,第二次为TRUE)

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include"SeqStack.h"

struct BiNode
{
	char ch;
	struct BiNode* lchild;
	struct BiNode* rchild;
};

struct Info
{
	struct BiNode* node;
	bool flag;
};

struct Info* createInfo(struct BiNode* node, bool flag)
{
	struct Info* info = malloc(sizeof(struct Info));
	if (NULL == info)
	{
		return NULL;
	}
	info->flag = flag;
	info->node = node;

	return info;
}

void nonRecursion(struct BiNode* root)
{
	//初始化栈
	SeqStack stack = Init_SeqStack();
	//先把根节点压入栈中
	Push_SeqStack(stack, createInfo(root, false));

	while (Size_SeqStack(stack) > 0)
	{
		//获得栈顶元素
		struct Info* info = (struct Info*)Top_SeqStack(stack);
		//弹出栈顶元素
		Pop_SeqStack(stack);
		
		//判断标识符
		if (info->flag)
		{
			printf("%c ", info->node->ch);
			free(info);
			continue;
		}

		//将右子树压入栈中
		if (info->node->rchild != NULL)
		{
			Push_SeqStack(stack, createInfo(info->node->rchild, false));
		}

		//将左子树压入栈中
		if (info->node->lchild != NULL)
		{
			Push_SeqStack(stack, createInfo(info->node->lchild, false));
		}

		//将根节点压入栈中
		info->flag = true;
		Push_SeqStack(stack, info);
	}

	//销毁栈
	Destroy_SeqStack(stack);
}

void test301()
{
	struct BiNode nodeA = { 'A',NULL,NULL };
	struct BiNode nodeB = { 'B',NULL,NULL };
	struct BiNode nodeC = { 'C',NULL,NULL };
	struct BiNode nodeD = { 'D',NULL,NULL };
	struct BiNode nodeE = { 'E',NULL,NULL };
	struct BiNode nodeF = { 'F',NULL,NULL };
	struct BiNode nodeG = { 'G',NULL,NULL };
	struct BiNode nodeH = { 'H',NULL,NULL };

	nodeA.lchild = &nodeB;
	nodeA.rchild = &nodeF;

	nodeB.rchild = &nodeC;

	nodeC.lchild = &nodeD;
	nodeC.rchild = &nodeE;

	nodeF.rchild = &nodeG;

	nodeG.lchild = &nodeH;
	nonRecursion(&nodeA);
}

int main(void)
{
	test301();

	system("pause");
	return EXIT_SUCCESS;
}

006.插入排序

插入排序简单介绍:

插入排序算法是一种简单的排序算法,也称为直接插入排序算法,它是一种稳定的排序算法,对局部有序的数据具有较高的效率
插入排序算法是一个对少量元素进行排序的有效算法。
比如,打牌是我们使用插入排序方法最多的日常生活的例子,在我们摸牌时,一般会重复以下步骤。起初,我们手里没有牌,摸出第一张,随意放在左手上,以后每一次摸牌,都会按照数字由小到大排列,直到所有的牌都摸完。
插入排序算法采用类似的思路,每一次从无序序列中拿出一个数据,将它放在已排序的序列的正确的位置,如此重复,直到所有的无序序列中的数据都找到了正确的位置。

代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//打印数组
void printfArray(int arr[], int len)
{
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

//插入排序
void insertSort(int arr[],int len)
{
	for (int i = 0; i < len; ++i)
	{
		if (arr[i] < arr[i - 1])
		{
			int temp = arr[i];
			int j = i - 1;
			for (; j >= 0 && temp < arr[j]; --j)
			{
				arr[j + 1] = arr[j];
			}
			arr[j + 1] = temp;
		}

	}
}

void test401()
{
	int arr[] = { 5,3,9,2,1,3 };
	int len = sizeof(arr) / sizeof(arr[0]);
	printfArray(arr, len);
	insertSort(arr, len);
	printfArray(arr, len);
}

int main(void)
{
	test401();


	system("pause");
	return EXIT_SUCCESS;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值