数据结构的学习笔记
目录
1.用动态数组实现顺序表
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//用动态数组实现顺序表
typedef int ElemType;
//顺序表
typedef struct SqList {
ElemType* data;//指针,指向一块内存的起始地址
int length;//存储动态数组的长度
}SqList;
//顺序表初始化
void initSqList(SqList& L, int len) {
L.length = len;
L.data = (ElemType*)malloc(sizeof(ElemType) * L.length);//分配空间
srand(time(NULL));//随机数生成
for (int i = 0; i < L.length; i++)
{
L.data[i] = rand() % 100;//随机生成数字存入顺序表,对100取余是为了规范存入的数据是0-99
}
}
//顺序表打印
void printSqList(SqList L) {
for (int i = 0; i < L.length; i++)
{
printf("%3d", L.data[i]);
}
printf("\n");
}
//顺序查找顺序表中的元素
int searchElemInSqList(SqList L, ElemType e) {
int i;
for (i = 0; i < L.length; i++) {
if (L.data[i] == e)
{
break;
}
}
return i + 1;//返回的位置刚好是下标对应的位置
}
int main() {
SqList L;//定义一个顺序表
initSqList(L, 10);//初始化顺序表,分配10个空间
printSqList(L);//打印顺序表中的值
ElemType e;//需要查询的元素
printf("请输入要搜索的元素值:");
scanf_s("%d", &e);
int pos;//存储查询元素的位置
pos = searchElemInSqList(L, e);//查询元素
if (pos)
{
printf("查找成功,位置为:%d\n", pos);
}
else
{
printf("查找失败\n");
}
return 0;
}
这段代码实现了使用动态数组来创建和操作顺序表的功能,包括顺序表的初始化、打印,以及顺序查找功能。代码的功能和执行步骤如下:
1. 定义顺序表结构体
- 定义了一个结构体
SqList
,包含了一个指针data
,用于指向动态分配的内存空间,用于存储顺序表中的元素;另一个成员变量length
用于表示顺序表的当前长度。
2. 顺序表初始化函数
initSqList(SqList& L, int len)
函数用于初始化顺序表。它的步骤包括:- 设置顺序表的长度
length
为用户指定的长度len
。 - 使用
malloc
函数为顺序表的data
指针分配内存空间,以存储顺序表中的元素。 - 使用
srand(time(NULL))
初始化随机数生成器。 - 通过
rand()
函数生成随机数,并将其对 100 取余后的值(0-99)存入顺序表。
- 设置顺序表的长度
3. 顺序表打印函数
printSqList(SqList L)
函数用于打印顺序表中的所有元素。- 使用循环遍历顺序表的
data
数组,将每个元素按顺序输出到控制台。
- 使用循环遍历顺序表的
4. 顺序查找函数
searchElemInSqList(SqList L, ElemType e)
函数用于在顺序表中查找指定的元素e
。- 该函数通过遍历顺序表中的每个元素,如果找到匹配的元素,则返回其位置(下标加 1),如果没有找到,则返回
L.length + 1
,即大于顺序表长度的位置。
- 该函数通过遍历顺序表中的每个元素,如果找到匹配的元素,则返回其位置(下标加 1),如果没有找到,则返回
5. 主函数
main()
函数是程序的入口,包含以下步骤:- 定义一个顺序表
L
。 - 调用
initSqList(L, 10)
函数,初始化顺序表,并为其分配 10 个元素空间。 - 使用
printSqList(L)
函数打印顺序表中的所有元素。 - 通过控制台输入用户需要查找的元素值,并存储在
e
变量中。 - 调用
searchElemInSqList(L, e)
函数查找该元素,并返回其在顺序表中的位置pos
。 - 根据查找结果,输出成功或失败的消息。
- 定义一个顺序表
总结
这段代码通过动态内存分配,实现了一个简单的顺序表操作,并具备随机初始化、打印及顺序查找的功能。用户可以通过输入一个数字,查看其在顺序表中的位置。
2.折半查找
#include <stdio.h>
#include <stdlib.h>
//用动态数组实现顺序表
typedef int ElemType;
//顺序表
typedef struct SqList {
ElemType* data;//指针,指向一块内存的起始地址
int length;//存储动态数组的长度
}SqList;
//顺序表初始化
void initSqList(SqList& L, int len) {
L.length = len;
L.data = (ElemType*)malloc(sizeof(ElemType) * L.length);//分配空间
for (int i = 0; i < L.length; i++)
{
L.data[i] = i + 1;//从小到大放入一些数据
}
}
//顺序表打印
void printSqList(SqList L) {
for (int i = 0; i < L.length; i++)
{
printf("%3d", L.data[i]);
}
printf("\n");
}
//顺序查找顺序表中的元素
int searchElemInSqList(SqList L, ElemType e) {
int i;
for (i = 0; i < L.length; i++) {
if (L.data[i] == e)
{
return i + 1;//返回的位置刚好是下标对应的位置
}
}
return -1;//查找失败
}
//折半查找
int binarySearch(SqList L, ElemType e) {
int low = 0, high = L.length - 1;//指向第一个结点和最后一个结点
int mid;
while (low <= high)
{
mid = (low + high) / 2;
if (L.data[mid] == e) {
return mid + 1;//返回的是位置,下标+1
}
else if (L.data[mid] < e) {
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return -1;//查找失败
}
//折半查找顺序表中的元素
int main() {
SqList L;//定义一个顺序表
initSqList(L, 10);//初始化顺序表,分配10个空间
printSqList(L);//打印顺序表中的值
ElemType e;//需要查询的元素
printf("请输入要搜索的元素值:");
scanf_s("%d", &e);
int pos;//存储查询元素的位置
//pos = searchElemInSqList(L, e);//顺序查询元素
pos = binarySearch(L, e);//折半查询元素
if (pos != -1)
{
printf("查找成功,位置为:%d\n", pos);
}
else
{
printf("查找失败\n");
}
return 0;
}
这段代码实现了使用动态数组来创建和操作顺序表的功能,包含顺序表的初始化、打印、顺序查找和折半查找。代码的功能和执行步骤如下:
1. 定义顺序表结构体
- 定义了一个结构体
SqList
,包含一个指针data
,指向动态分配的内存空间,用于存储顺序表中的元素;另一个成员变量length
用于表示顺序表的当前长度。
2. 顺序表初始化函数
initSqList(SqList& L, int len)
函数用于初始化顺序表。它的步骤包括:- 设置顺序表的长度
length
为用户指定的长度len
。 - 使用
malloc
函数为顺序表的data
指针分配内存空间,以存储顺序表中的元素。 - 将顺序表中的数据从 1 开始按顺序填充,即
L.data[i] = i + 1
。
- 设置顺序表的长度
3. 顺序表打印函数
printSqList(SqList L)
函数用于打印顺序表中的所有元素。- 使用循环遍历顺序表的
data
数组,将每个元素按顺序输出到控制台。
- 使用循环遍历顺序表的
4. 顺序查找函数
searchElemInSqList(SqList L, ElemType e)
函数用于在顺序表中顺序查找指定的元素e
。- 通过遍历顺序表中的每个元素,如果找到匹配的元素,则返回其位置(下标加 1),如果没有找到,则返回
-1
。
- 通过遍历顺序表中的每个元素,如果找到匹配的元素,则返回其位置(下标加 1),如果没有找到,则返回
5. 折半查找函数
binarySearch(SqList L, ElemType e)
函数用于在顺序表中使用折半查找(即二分查找)来查找指定的元素e
。前提是顺序表中的数据必须是有序的。- 初始化
low
和high
指针分别指向顺序表的第一个和最后一个元素。 - 使用
while
循环进行查找,每次通过计算中间位置mid
来比较目标元素e
与中间元素L.data[mid]
的大小关系:- 如果
L.data[mid]
等于e
,则返回位置(下标加 1)。 - 如果
L.data[mid]
小于e
,则调整low
指针。 - 如果
L.data[mid]
大于e
,则调整high
指针。
- 如果
- 如果查找失败,返回
-1
。
- 初始化
6. 主函数
main()
函数是程序的入口,包含以下步骤:- 定义一个顺序表
L
。 - 调用
initSqList(L, 10)
函数,初始化顺序表,并为其分配 10 个元素空间。 - 使用
printSqList(L)
函数打印顺序表中的所有元素。 - 通过控制台输入用户需要查找的元素值,并存储在
e
变量中。 - 使用
binarySearch(L, e)
函数在顺序表中进行折半查找,并返回元素的位置pos
。 - 根据查找结果,输出成功或失败的消息。
- 定义一个顺序表
总结
这段代码通过动态内存分配实现了一个简单的顺序表操作,并具备初始化、打印、顺序查找和折半查找的功能。特别注意的是,在使用折半查找之前,顺序表中的数据必须是有序的。
3.二叉排序树建树
#include <stdio.h>
#include <stdlib.h>
// 二叉排序树建树
typedef int KeyType;
typedef struct BSTNode {
KeyType key;
struct BSTNode* lchild, * rchild;
}BSTNode, * BSTree;
//排序二叉树插入结点(非递归)
int BSTInsert(BSTree& T, KeyType key) {
BSTNode* newNode = (BSTNode*)calloc(1, sizeof(BSTNode)); //为新结点分配空间
newNode->key = key;
if (T == NULL) { //树为空,新结点作为树的根
T = newNode;
return 1; //插入成功
}
BSTNode* p, * parent; //p用来查找树结点,parent用来保存其父亲
p = parent = T; //从树根开始查
while (p)
{
parent = p;
if (key < p->key) //key小于当前结点的值,进入左孩子
{
p = p->lchild;
}
else if (key > p->key) //key大于当前结点的值,进入右孩子
{
p = p->rchild;
}
else //key等于当前元素的值,不插入
{
return 0;
}
}
if (key < parent->key)
{
parent->lchild = newNode; //新结点作为父亲的左孩子
}
else
{
parent->rchild = newNode; //新结点作为父亲的右孩子
}
return 1; //插入成功
}
//排序二叉树插入结点(递归)
int BSTInsert2(BSTree& T, KeyType key) {
if (T == NULL)
{ //给新结点分配空间,树为空,新结点作为树的根
T = (BSTree)calloc(1, sizeof(BSTNode));
T->key = key;
return 1; //插入成功
}
if (key == T->key) //相同元素,不插入
return 0;
else if (key < T->key)//key小于当前结点的值
return BSTInsert2(T->lchild, key);
else
return BSTInsert2(T->rchild, key);
}
//创建二叉排序树
void createBST(BSTree& T, KeyType* arr, int len) {
int i;
for (i = 0; i < len; i++)//把结点一个一个的放进二叉排序树
{
BSTInsert2(T, arr[i]);
}
}
// 查找结点
bool BSTSearch(BSTree T, KeyType key, BSTNode*& search, BSTNode*& parent) {
while (T != NULL && T->key != key)
{
parent = T; //获取查找结点的父结点
if (key < T->key) {
T = T->lchild;
}
else
{
T = T->rchild;
}
}
search = T; //获取查找到的树结点
if (search == NULL) //查找失败search一定等于NULL
{
return false;
}
return true;
}
//中序遍历,用来验证二叉排序树是否建立正确
void midOrder(BSTree T) {
if (T)
{
midOrder(T->lchild);
printf("%3d", T->key);
midOrder(T->rchild);
}
}
int main() {
BSTree bst = NULL; //定义二叉排序树
KeyType arr[7] = { 48,14,60,34,22,73,52 }; //将要放入二叉排序树的元素
createBST(bst, arr, 7); //建立二叉排序树
midOrder(bst); //中序遍历二叉排序树
printf("\n");
BSTNode* search = NULL, * parent = NULL;
bool result;
result = BSTSearch(bst, 48, search, parent);
if (result)
{
printf("%d查找成功\n", search->key);
}
else
{
printf("查找失败\n");
}
return 0;
}
这段代码实现了二叉排序树(Binary Search Tree, BST)的基本操作,包括插入节点、查找节点和中序遍历。代码的功能和执行步骤如下:
1. 定义二叉排序树节点结构体
- 定义了一个结构体
BSTNode
,包含以下成员:key
:存储节点的关键值。lchild
和rchild
:指向左右子节点的指针。
BSTree
是BSTNode*
的别名,表示树的根节点。
2. 排序二叉树插入节点(非递归)
BSTInsert(BSTree& T, KeyType key)
函数用于在二叉排序树中插入一个新的节点。步骤包括:- 为新节点分配内存,并设置其
key
值。 - 如果树为空,新节点作为树的根节点。
- 否则,从根节点开始查找合适的位置,将新节点插入到正确的位置。
- 如果找到相同的关键值,则不插入节点。
- 根据新节点的值决定将其插入到父节点的左子树还是右子树。
- 为新节点分配内存,并设置其
3. 排序二叉树插入节点(递归)
BSTInsert2(BSTree& T, KeyType key)
函数用于在二叉排序树中递归插入一个新的节点。步骤包括:- 如果树为空,新节点作为树的根节点。
- 如果关键值与当前节点相同,则不插入。
- 根据关键值的大小,递归地在左子树或右子树中插入新节点。
4. 创建二叉排序树
createBST(BSTree& T, KeyType* arr, int len)
函数用于从数组中创建一个二叉排序树。步骤包括:- 遍历数组中的每个元素,使用
BSTInsert2
函数将元素插入到树中。
- 遍历数组中的每个元素,使用
5. 查找节点
BSTSearch(BSTree T, KeyType key, BSTNode*& search, BSTNode*& parent)
函数用于在二叉排序树中查找指定的节点。步骤包括:- 从树的根节点开始查找,更新
parent
指针以记录当前节点的父节点。 - 如果找到匹配的节点,设置
search
指针指向该节点。 - 如果未找到节点,
search
将为NULL
,函数返回false
。
- 从树的根节点开始查找,更新
6. 中序遍历
midOrder(BSTree T)
函数用于中序遍历二叉排序树。步骤包括:- 递归遍历左子树,打印当前节点的关键值,然后递归遍历右子树。
- 中序遍历可以验证二叉排序树的节点是否按顺序排列。
7. 主函数
main()
函数是程序的入口,包含以下步骤:- 定义一个空的二叉排序树
bst
。 - 定义一个数组
arr
,包含要插入到二叉排序树中的元素。 - 调用
createBST(bst, arr, 7)
函数创建二叉排序树。 - 使用
midOrder(bst)
函数打印中序遍历的结果,以验证树的正确性。 - 查找关键值为
48
的节点,并输出查找结果。
- 定义一个空的二叉排序树
总结
这段代码实现了一个简单的二叉排序树的构建、节点插入、节点查找和中序遍历功能。二叉排序树的插入操作可以通过递归或非递归方式实现,查找操作通过中序遍历可以验证树的结构。
4.二叉排序树删除
#include <stdio.h>
#include <stdlib.h>
// 二叉排序树建树
typedef int KeyType;
typedef struct BSTNode {
KeyType key;
struct BSTNode* lchild, * rchild;
}BSTNode, * BSTree;
//排序二叉树插入结点(非递归)
int BSTInsert(BSTree& T, KeyType key) {
BSTNode* newNode = (BSTNode*)calloc(1, sizeof(BSTNode)); //为新结点分配空间
newNode->key = key;
if (T == NULL) { //树为空,新结点作为树的根
T = newNode;
return 1; //插入成功
}
BSTNode* p, * parent; //p用来查找树结点,parent用来保存其父亲
p = parent = T; //从树根开始查
while (p)
{
parent = p;
if (key < p->key) //key小于当前结点的值,进入左孩子
{
p = p->lchild;
}
else if (key > p->key) //key大于当前结点的值,进入右孩子
{
p = p->rchild;
}
else //key等于当前元素的值,不插入
{
return 0;
}
}
if (key < parent->key)
{
parent->lchild = newNode; //新结点作为父亲的左孩子
}
else
{
parent->rchild = newNode; //新结点作为父亲的右孩子
}
return 1; //插入成功
}
//排序二叉树插入结点(递归)
int BSTInsert2(BSTree& T, KeyType key) {
if (T == NULL)
{ //给新结点分配空间,树为空,新结点作为树的根
T = (BSTree)calloc(1, sizeof(BSTNode));
T->key = key;
return 1; //插入成功
}
if (key == T->key) //相同元素,不插入
return 0;
else if (key < T->key)//key小于当前结点的值
return BSTInsert2(T->lchild, key);
else
return BSTInsert2(T->rchild, key);
}
//创建二叉排序树
void createBST(BSTree& T, KeyType* arr, int len) {
int i;
for (i = 0; i < len; i++)//把结点一个一个的放进二叉排序树
{
BSTInsert2(T, arr[i]);
}
}
// 查找结点
bool BSTSearch(BSTree T, KeyType key, BSTNode*& search, BSTNode*& parent) {
while (T != NULL && T->key != key)
{
parent = T; //获取查找结点的父结点
if (key < T->key) {
T = T->lchild;
}
else
{
T = T->rchild;
}
}
search = T; //获取查找到的树结点
if (search == NULL) //查找失败search一定等于NULL
{
return false;
}
return true;
}
// 删除二叉排序树的结点
bool deleteBSTNode(BSTree& T, KeyType key) {
if (T == NULL) {
return false;
}
if (key < T->key) { //key小于当前树结点的值,往左子树找
deleteBSTNode(T->lchild, key);
}
else if (key > T->key) { //key大于当前树结点的值,往右子树找
deleteBSTNode(T->rchild, key);
}
else { //相等,找到要删除的结点
if (T->lchild == NULL) { //要删除的结点左子树为空,右子树直接顶上去
BSTNode* tempNode = T; //存放要删除的结点
T = T->rchild; //右子树顶上去
free(tempNode); //释放要删除结点的空间
return true;
}
else if (T->rchild == NULL) { //要删除的结点右子树为空,左子树直接顶上去
BSTNode* tempNode = T; //存放要删除的结点
T = T->lchild; //左子树顶上去
free(tempNode); //释放要删除结点的空间
return true;
}
else { //要删除的结点左右子树都不为空
//一般的删除策略是用左子树的最大数据(最右结点) 或 右子树的最小数据代替
//这里采用查找左子树最大数据来代替
BSTNode* tempNode = T->lchild; //保存要删除结点的左子树
while (tempNode->rchild != NULL) //循环找到左子树最右结点
{
tempNode = tempNode->rchild;
}
T->key = tempNode->key; //将左子树最右结点的值赋给要删除的结点
deleteBSTNode(T->lchild, tempNode->key); //递归删除左子树的最右结点
return true;
}
}
}
//中序遍历,用来验证二叉排序树是否建立正确
void midOrder(BSTree T) {
if (T)
{
midOrder(T->lchild);
printf("%3d", T->key);
midOrder(T->rchild);
}
}
int main() {
BSTree bst = NULL; //定义二叉排序树
KeyType arr[7] = { 48,14,60,34,22,73,52 }; //将要放入二叉排序树的元素
createBST(bst, arr, 7); //建立二叉排序树
midOrder(bst); //中序遍历二叉排序树
printf("\n");
BSTNode* search = NULL, * parent = NULL;
bool result;
result = BSTSearch(bst, 48, search, parent);
if (result)
{
printf("%d查找成功\n", search->key);
}
else
{
printf("查找失败\n");
}
result = deleteBSTNode(bst, 34);
if (result)
{
printf("删除成功\n");
}
else
{
printf("删除失败\n");
}
midOrder(bst);
return 0;
}
这段代码实现了一个二叉排序树(Binary Search Tree, BST)的基本操作,包括节点插入、查找、删除和中序遍历。代码的功能和执行步骤如下:
1. 定义二叉排序树节点结构体
BSTNode
结构体定义了二叉排序树的节点,包含以下成员:key
:存储节点的关键值。lchild
和rchild
:指向左子节点和右子节点的指针。
BSTree
是BSTNode*
的别名,表示树的根节点。
2. 排序二叉树插入节点(非递归)
BSTInsert(BSTree& T, KeyType key)
函数用于在二叉排序树中插入一个新的节点。步骤包括:- 为新节点分配内存,并设置其
key
值。 - 如果树为空,新节点作为树的根节点。
- 否则,从根节点开始查找合适的位置,将新节点插入到正确的位置。
- 如果找到相同的关键值,则不插入节点。
- 根据新节点的值决定将其插入到父节点的左子树还是右子树。
- 为新节点分配内存,并设置其
3. 排序二叉树插入节点(递归)
BSTInsert2(BSTree& T, KeyType key)
函数用于在二叉排序树中递归插入一个新的节点。步骤包括:- 如果树为空,新节点作为树的根节点。
- 如果关键值与当前节点相同,则不插入。
- 根据关键值的大小,递归地在左子树或右子树中插入新节点。
4. 创建二叉排序树
createBST(BSTree& T, KeyType* arr, int len)
函数用于从数组中创建一个二叉排序树。步骤包括:- 遍历数组中的每个元素,使用
BSTInsert2
函数将元素插入到树中。
- 遍历数组中的每个元素,使用
5. 查找节点
BSTSearch(BSTree T, KeyType key, BSTNode*& search, BSTNode*& parent)
函数用于在二叉排序树中查找指定的节点。步骤包括:- 从树的根节点开始查找,更新
parent
指针以记录当前节点的父节点。 - 如果找到匹配的节点,设置
search
指针指向该节点。 - 如果未找到节点,
search
将为NULL
,函数返回false
。
- 从树的根节点开始查找,更新
6. 删除节点
deleteBSTNode(BSTree& T, KeyType key)
函数用于删除二叉排序树中的指定节点。步骤包括:- 如果树为空,返回删除失败。
- 如果关键值小于当前节点的值,递归删除左子树中的节点。
- 如果关键值大于当前节点的值,递归删除右子树中的节点。
- 如果找到要删除的节点,处理删除操作:
- 如果要删除的节点没有左子树,将右子树顶替到要删除节点的位置。
- 如果要删除的节点没有右子树,将左子树顶替到要删除节点的位置。
- 如果要删除的节点有左右子树,找到左子树中的最大节点(最右节点),用其值替代要删除的节点,并递归删除该最大节点。
7. 中序遍历
midOrder(BSTree T)
函数用于中序遍历二叉排序树。步骤包括:- 递归遍历左子树,打印当前节点的关键值,然后递归遍历右子树。
- 中序遍历可以验证二叉排序树的节点是否按顺序排列。
8. 主函数
main()
函数是程序的入口,包含以下步骤:- 定义一个空的二叉排序树
bst
。 - 定义一个数组
arr
,包含要插入到二叉排序树中的元素。 - 调用
createBST(bst, arr, 7)
函数创建二叉排序树。 - 使用
midOrder(bst)
函数打印中序遍历的结果,以验证树的正确性。 - 查找关键值为
48
的节点,并输出查找结果。 - 删除关键值为
34
的节点,并输出删除结果。 - 再次使用
midOrder(bst)
函数打印中序遍历的结果,验证删除操作的正确性。
- 定义一个空的二叉排序树
总结
这段代码实现了二叉排序树的基本操作,包括节点的插入、查找、删除和中序遍历。通过这些操作,可以创建一个二叉排序树,维护树的结构,并执行基本的树操作。