目录
一、队列
1.1 队列的概念
队列的性质:先进先出(FIFO)
队列的操作:
入队
出队
1.2 链式队列
为了实现队列先进先出的原理,链式存储里可以采用头插法和尾删法或者尾插法和头删法实现功能,但是尾插和尾删需要将链表遍历一遍,效率太低
所以我们可以定义两个指针变量,一个front保存头结点的地址,一个rear保存最后一个结点的地址;入队操作时将新结点插入到rear对应的结点的后面,然后rear保存最后一个结点的地址,出队操作就是将头结点后面的结点删除。
1.2.1 定义结点结构体和队列结构体
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
//定义结点结构体
typedef struct node
{
DataType data;
struct node *next;
}node;
//定义队列结构体,便于操作链式操作队列的两个指针
typedef struct linkqueue
{
node *front; //保存头结点地址
node *rear; //保存最后一个的地址
}linkqueue;
#endif
1.2.2 创建一个空的队列
//创建一个空的队列
linkqueue *LinkeQueueCreate()
{
//给linkqueue结构体的两个指针分配空间
linkqueue *q = (linkqueue *)malloc(sizeof(linkqueue));
//给node指针分配空间
q->front = (node *)malloc(sizeof(node));
q->rear = q->front;
q->front->next = NULL;
return q;
}
1.2.3 入队
//入队
void LinkQueueInput(linkqueue *q,DataType value)
{
//申请一个新的结点并且赋值
node *tmp = (node *)malloc(sizeof(node));
tmp->data = value;
tmp->next = NULL;
//将新节点插入到对应的rear结点的后面
q->rear->next = tmp;
//将新节点的指针域保存NULL
tmp->next = NULL;
q->rear = tmp;
}
1.2.4 出队
//出队
DataType LinkeQueueOutput(linkqueue *q)
{
if(q->front->next == NULL)
{
printf("队列为空!\n");
return (DataType)-1;
}
//头删法删除数据
node *tmp = q->front->next;
q->front->next = tmp->next;
DataType value = tmp->data;
free(tmp);
tmp = NULL;
//当队列为空时,将rear指针指向头结点
if(q->front->next == NULL)
{
q->rear = q->front;
}
return value;
}
1.3 顺序队列(循环队列)
1.3.1 定义结点结构体
#ifndef _SEQUEUE_H_
#define _SEQUEUE_H_
#include <stdio.h>
#include <stdlib.h>
#define NUM 32
#define N (NUM + 1)
typedef int DataType;
typedef struct sequeue
{
DataType data[N];
int front;
int rear;
}sequeue;
#endif
1.3.2 创建一个空的循环队列
//创建一个空的循环队列
sequeue *SequeueCreate()
{
sequeue *q = (sequeue *)malloc(sizeof(sequeue));
q->rear = q->front = 0;
return q;
}
1.3.3 判断队列是否为满
//判断队列是否为满
int SequeueIsFull(sequeue *q)
{
return (q->rear + 1) % N == q->front ? 1 : 0;
}
1.3.4 入队
//入队
void SequeueInput(sequeue *q,DataType value)
{
if(SequeueIsFull(q))
{
printf("队列为满,无法入队!\n");
return;
}
else
{
q->data[q->rear] = value;
if(q->rear == NUM)
{
q->rear = 0;
}
else
{
q->rear++;
}
}
}
1.3.5 判断队列是否为空
//判断队列是否为空
int SequeueIsEmpty(sequeue *q)
{
return q->front == q->rear ? 1 : 0;
}
1.3.6 出队
//出队
DataType SequeueOutput(sequeue *q)
{
if(SequeueIsEmpty(q))
{
return (DataType)-1;
}
else
{
DataType value = q->data[q->front];
if(q->front == NUM)
{
q->front = 0;
}
else
{
q->front++;
}
//q->front = (q->front + 1) % N;
return value;
}
}
1.3.7 遍历队列
//遍历队列
void SequeuePrint(sequeue *q)
{
if (SequeueIsEmpty(q))
{
printf("队列为空!\n");
}
else
{
while ( q->rear != q->front)
{
printf("%d ",q->data[q->front]);
if (q->front == NUM)
{
q->front = 0;
}
else
{
q->front++;
}
}
return;
}
}
二、树和二叉树
2.1 概念
如果数据和数据之间满足一对多的关系,将其逻辑结构称之为树
最上层的第一个数据称之为根节点
如果一个结点有直接后继,将这些后继称之为子节点或子树,这个结点称之为这些子节点的父节点
度数:
一个结点的子树的个数称为该结点的度数
一颗树的度数是指该树中结点最大度数
边数:
一个结点系列,k1...ki 满足ki是ki+1的父节点,就称为一条从k1到ki的路径,路径的长度为i-1,即路径的边数
层数:
结点的层数等于父节点的层数加一,根节点的层数为1,树种结点层数的最大值称为该树的高度或者深度。
终端节点(叶子结点):
度数为零的节点称为树叶或终端节点,度数不为零的节点称为分支节 点,除根节点外的分支节点称为内部节点。
树的逻辑结构:
树种任何结点都可以有多个或者0个直接后继结点(子节点)
但是至多只能有一个直接前驱(父节点),根节点没有前驱结点
叶子结点没有后继结点。
2.2 二叉树
如果树种的每一个结点最多有两个,那么将这个树称为二叉树。
二叉树与普通树不同,二叉树严格区分左孩子和右孩子,即使只有一个子节点也要区分左右
满二叉树 :深度为k(k≥1)时有2^k-1个节点的二叉树。
完全二叉树 :只有最下面两层有度数小于2的节点,且最下面一层的 叶节点集中在最左边的若干位置上。
2.2.1 二叉树的性质
性质1:在二叉树的第i层至多有2^(i -1)个结点。
性质2:深度为K的二叉树最多有2^k - 1个结点。
性质3:对任何一个二叉树,如果其终端结点数为n,度为2的结点数为m,则:n = m+1;
性质4:具有n个结点的完全二叉树,其深度为(log2n)+1或『log2(n+1)。
性质5:
如果对1棵有n个饥饿点的二叉树的饥饿点按层序编号,对任何一个结点i
(1)如果i= 1,则结点i是二叉树的根,无双亲,如果,如果i > 1,则其双亲结点为 i/2 。
(2)如果2i > n,则结点无左孩子,否则,其左孩子为2i。
(3)如果2i+ 1 > n,则结点无右孩子,否则,其右孩子为2i+1.
2.2.2 二叉树的存储
完全二叉树的结点编号方法是从上到下,从左往右,根节点为1号结点
设完全二叉树的节点数为n,某结点编号为I
2i <= n,有左孩子,其编号为2i,否则没有左孩子,本身是叶子结点
2i+1 <= n,有右孩子,其编号为2i + 1,否则没有右孩子
2.3 二叉树的存储方式
2.3.1 二叉树的顺序存储
因为无法保存当前二叉树是一个满二叉树或者完全二叉树,所以,顺序存储时,需要将不存在的结点预留位置,这样做就会浪费空间,所以一般不使用顺序存储
2.3.2 二叉树的链式存储
二叉树使用链式存储时,每一个结点需要定义一个结构体,里面至少有三个成员,分别是一个数据域和两个指针域组成,数据域保存数据,指针域分别保存左右子树的地址。
2.4 二叉树的操作
2.4.1 创建结点结构体
#ifndef _BINARYTREE_H_
#define _BINARYTREE_H_
#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
typedef struct node
{
DataType data;
struct node *left;
struct node *right;
}bitree_t;
#endif
2.4.2 创建二叉树
//创建二叉树
bitree_t *CreateTree(DataType *a,int length)
{
bitree_t *array[11] = {0};
int i;
for(i = 0;i < length;i++ )
{
array[i] = (bitree_t *)malloc(sizeof(bitree_t));
if(array[i] == NULL)
{
return NULL;
}
array[i]->data = a[i];
array[i]->left = NULL;
array[i]->right = NULL;
}
for(i = 0;i < length / 2;i++)
{
array[i]->left = array[2*i + 1];
array[i]->right = array[2*i + 2];
}
return array[0];
}
2.4.3 遍历二叉树
先序遍历:先访问树根,再访问左子树,最后访问右子树 (根左右)
中序遍历:先访问左子树,再访问树根,最后访问右子树(左根右)
后序遍历:先访问左子树,再访问右子树,最后访问树根(左右根)
先序遍历
//先序遍历
void PreOrder(bitree_t *root)
{
if(root == NULL)
{
return;
}
else
{
printf("%d ",root->data);
PreOrder(root->left);
PreOrder(root->right);
}
}
中序遍历
//中序遍历
void MidOrder(bitree_t *root)
{
if(root == NULL)
{
return;
}
else
{
MidOrder(root->left);
printf("%d ",root->data);
MidOrder(root->right);
}
}
后序遍历
//后序遍历
void PostOrder(bitree_t *root)
{
if(root == NULL)
{
return;
}
else
{
PostOrder(root->left);
PostOrder(root->right);
printf("%d ",root->data);
}
}
2.5 二叉搜索树
定义:二叉搜索树,又被称为二叉查找树,其特点:左孩子比父节点小,右孩子比父节点大。
//创建二叉搜索树
bitree_t* CreateBSTree(bitree_t *root,DataType num)
{
if (NULL == root)
{
root = (bitree_t *)malloc(sizeof(bitree_t) * 1);
if(NULL == root)
{
return NULL;
}
root->data = num;
root->left = NULL;
root->right = NULL;
}
else
{
if(root->data > num)
{
root->left = CreateBSTree(root->left,num);
}
else
{
root->right = CreateBSTree(root->right,num);
}
return root;
}
}