二叉树、二叉搜索树+简单排序算法
此笔记为博主机考前抱佛脚所做,不管有没有用也一并上传记录下来罢,由于时间仓促笔记基本做在了代码注释中
以及特别鸣谢以下视频带我抱佛脚:
二叉树
实现:
- 初始化
- 清空树
- 按给定元素顺序插入树
- 查找树的值为x的结点
- 求树的高度
- 输出二叉树(前、中、后序遍历、层序遍历)
- 判树空
c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//二叉树
typedef struct treeNode {
int val;
struct treeNode* lchild,*rchild;
}BTNode;
typedef struct {
int num;//计算树的结点,可以判断是否是空树
BTNode* root;//树的根节点
}BTree;
//初始化二叉树
BTree* initBTree() {
BTree* btree = (BTree*)malloc(sizeof(BTree));
btree->num = 0;
btree->root=NULL;
return btree;
}
//清空树
//拆解成先清空孩子结点、再清空根节点的小任务
void clearAssist(BTNode* root,int* num ) {
if (!root) return;
clearAssist(root->lchild, num);
clearAssist(root->rchild, num);
(*num)--;
free(root);
}
bool clearBTree(BTree* t) {
if (!t || t->num == 0) return true;
clearAssist(t->root, &t->num);
// 清空后根节点置空(避免悬空指针)
t->root = NULL;
return true;
}
//查找结点(同样使用递归)
BTNode* searchAssist(BTNode* root, int x) {
if (!root) return NULL;
if (root->val == x) return root;
BTNode* left = searchAssist(root->lchild, x);
BTNode* right= searchAssist(root->rchild, x);
// 优先返回左子树的结果:左找到则返回左,否则返回右,都没找到返回NULL
return left ? left : right;
}
BTNode* searchXBTree(BTree* t, int x) {
if (!t||t->num==0) return NULL;
return searchAssist(t->root,x);
}
//求树的高度,依然使用递归,从底下往上数,左右分开数取大的
//依然是清楚:退出条件、重复部分和返回值
int heightAssist(BTNode* root) {
if (!root) return 0;
int left=heightAssist(root->lchild);
int right=heightAssist(root->rchild);
return left > right ? left + 1 : right + 1;
}
int getHBTree(BTree* t) {
if (!t || t->num == 0) return 0;
return heightAssist(t->root);
}
//判断树空
bool isEmptyBTree(BTree* t) {
if (!t || t->num == 0) return true;
else return false;
}
//按给定的元素插入树 arr[5]={1,2,3,4,5}
//层序遍历思想:把结点赋值后放入队列,每次取出头结点并把其左右子节点入队
//层序遍历就在取队头时输出元素
BTree* insertArrBTree(BTree* btree, int* arr,int n)//n是arr中元素数
{
if (!btree) return NULL;
if (btree->num == 0) {
btree->root = (BTNode*)malloc(sizeof(BTNode));
btree->root->lchild = btree->root->rchild = NULL;
//把头结点先按照arr赋值
btree->root->val = arr[0];
btree->num++;
}
//创建队列,注意存储的是结点的指针
BTNode* queue[1000] = { 0 };
//先把根节点入队
queue[0] = btree->root;
//队尾入列,队头出列
int in = 1, out = 0;
int i = 1;
while (i < n) {
//先取出队列的头节点,注意对out处理
BTNode* cur = queue[out++];
//如果左节点非空(赋过值了),直接入队.注意队列操作时对in、out的处理
if (cur->lchild) {
queue[in++] = cur->lchild;
}
//如果是空的要先赋值(注意包括需要开辟空间、给孩子置空、再赋值)
else {
cur->lchild = (BTNode*)malloc(sizeof(BTNode));
cur->lchild->lchild = cur->lchild->rchild = NULL;
cur->lchild->val = arr[i++];
queue[in++] = cur->lchild;
btree->num++;
}
if (i >= n) break;//如果此时已经加完元素了,就退出
//接着看右孩子
if (cur->rchild) {
queue[in++] = cur->rchild;
}
//如果是空的要先赋值(注意包括需要开辟空间、给孩子置空、再赋值)
else {
cur->rchild = (BTNode*)malloc(sizeof(BTNode));
cur->rchild->lchild = cur->rchild->rchild = NULL;
cur->rchild->val = arr[i++];
queue[in++] = cur->rchild;
btree->num++;
}
}
return btree;
}
//输出二叉树-前、中、后、层序遍历
//前中后都利用递归
void preAssist(BTNode* root)
{
if (!root) return;
printf("%d ", root->val);//根
preAssist(root->lchild);//左
preAssist(root->rchild);//右
}
void preShowBTree(BTree* btree)
{
if (!btree) return;
preAssist(btree->root);
}
void midAssist(BTNode* root)
{
if (!root) return;
midAssist(root->lchild);//左
printf("%d ", root->val);//根
midAssist(root->rchild);//右
}
void midShowBTree(BTree* btree)
{
if (!btree) return;
midAssist(btree->root);
}
void behAssist(BTNode* root)
{
if (!root) return;
behAssist(root->lchild);//左
behAssist(root->rchild);//右
printf("%d ", root->val);//根
}
void behShowBTree(BTree* btree)
{
if (!btree) return;
behAssist(btree->root);
}
//层序遍历
void leverShowBTree(BTree* btree) {
if (!btree) return;
BTNode* queue[1000] = {NULL};
queue[0] = btree->root;
int in = 1, out = 0;
int i = 0;
//循环的条件是队列非空
while (out!=in) {
BTNode* cur = queue[out++];
printf("%d ", cur->val);
if (cur->lchild) queue[in++] = cur->lchild;
if (cur->rchild) queue[in++] = cur->rchild;
}
printf("\n");
}
int main()
{
BTree* t = initBTree();
int arr[10] = { 3,1,2,5,4,6,7,11,9,5 };
insertArrBTree(t, arr, 10);
preShowBTree(t);
printf("\n");
leverShowBTree(t);
printf("%d \n", t->num);
clearBTree(t);
printf("%d \n", t->num);
return 0;
}
二叉搜索树
实现:
- 插入值为x的结点
- 按给定数组创建树
- 查找值为x的结点:利用二叉搜索树的特点
- 删除值为x的结点(第一个遇到的)
- 查询元素个数(递归)------二叉树通用
c
#include <stdio.h>
#include <stdlib.h>
//二叉搜索树,主要的功能是查找(相同元素可以不放进树)
//对3、1、2、5、4使用一定的规则插入,保证对每个根结点,比他小于等于的在左边,比他大的在右边
//结点------直接用二叉树的结点就行
typedef struct BTNode {
struct BTNode* lchild;
struct BTNode* rchild;
int val;
}BTNode;
typedef struct {
int num;//计算树的结点,可以判断是否是空树
BTNode* root;//树的根节点
}BTree;
//1. 插入值为x的结点
BTNode* insertBST(BTNode* root,int x) {
BTNode* cur = (BTNode*)malloc(sizeof(BTNode));
cur->lchild = cur->rchild = 0;
cur->val = x;
BTNode* in = root;//插入结点当前的父节点,初始化
//循环找cur的插入位置
while (1) {
//如果cur比in小或等于,放左边
if (cur->val <= in->val) {
//如果左边为空,直接插入退出循环
if (!in->lchild) {
in->lchild = cur;
break;
}
//如果左边不为空,继续往下走
else {
in = in->lchild;
continue;
}
}
//如果cur比root大,放右边
else {
//如果右边为空,直接插入退出循环
if (!in->rchild) {
in->rchild = cur;
break;
}
//如果右边不为空,继续往下走
else {
in = in->rchild;
continue;
}
}
}
return root;
}
//2. 给定数组创建树
BTNode* createBST(int* arr, int n) {
if (!arr || n <= 0) return NULL;//创建失败
//1.设置arr[0]为根节点
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
root->lchild = root->rchild = 0;
root->val = arr[0];
//2.从第二个元素开始插入
for (int i = 1;i < n;i++) {
root = insertBST(root, arr[i]);
}
return root;
}
//3. 查找值为x的结点:利用二叉搜索树的特点
//比根小的往左找、比根大的往右找
BTNode* searchXBST(BTNode* root, int x)
{
if (!root) return NULL;
BTNode* cur = root;
while (cur) {
if (x == cur->val) return cur;
else if (x < cur->val) {
cur = cur->lchild;
continue;
}
else if (x > cur->val) {
cur = cur->rchild;
continue;
}
}
//如果到了这里,肯定是因为没有找到
return NULL;
}
//4. 删除值为x的结点(第一个遇到的)
// 分情况讨论:
// 1、删除叶子结点 可以直接删除
// 2、删除只有一个子结点的结点 可以直接删除再把子结点接上父结点就行
// 3、删除有两个结点的结点(即有左右子树) 用中序前继或者后继替换这个结点(可以用中序遍历来理解)
//辅助函数1:查找目标节点的父节点
BTNode* findFatherXBST(BTNode* root, BTNode* target) {
//没有父节点的情况
if (!root || !target || root == target) return NULL;
BTNode* father = root;
while (father) {
if (father->lchild == target || father->rchild == target) return father;
if (target->val <= father->val) {
father = father->lchild;
}
else if (target->val > father->val) {
father = father->rchild;
}
}
return NULL;//未找到父节点(目标不在树中)
}
//辅助函数2:找节点的中序后继
//Successor:后继,中序后继是该节点右子树的最左侧结点
BTNode* findMidSuccessor(BTNode* node) {
//由于找中序后继替换的场景只在有左右子树的情况下用到,所以这里默认没有右子树就是没有中序后继
if (!node || !node->rchild) return NULL;
BTNode* cur = node->rchild;
while (cur->lchild) {
cur = cur->lchild;
}
return cur;
}
//核心:删除值为x的结点
BTNode* deleteXBST(BTNode* root, int x) {
//1.找到待删除节点和父节点
BTNode* delNode= searchXBST(root, x);
if (!delNode) return root;//找不到x直接返回原树
BTNode* father = findFatherXBST(root, delNode);
//2.分情况删除结点
////(1)删除叶子结点(结点没有子结点)
if (!delNode->lchild && !delNode->rchild) {
if (father->lchild== delNode) {
father->lchild = NULL;
}
else {
father->rchild = NULL;
}
free(delNode);
}
////(2)删除只有一个子节点的结点
//只有左子树
else if (delNode->rchild == NULL) {//全无的已经在上面处理完了
if (father == NULL) { // 待删节点是根节点
BTNode* newRoot = delNode->lchild;
free(delNode);
return newRoot;
}
BTNode* cur = delNode->lchild;
// 把左子树接给父节点
if (father->lchild == delNode) {
father->lchild = delNode->lchild;
}
else {
father->rchild = delNode->lchild;
}
free(delNode);
}
//只有右子树
else if (delNode->lchild == NULL) {//全无的已经在上面处理完了
if (father == NULL) { // 待删节点是根节点
BTNode* newRoot = delNode->rchild;
free(delNode);
return newRoot;
}
BTNode* cur = delNode->lchild;
// 把右子树接给父节点
if (father->lchild == delNode) {
father->lchild = delNode->rchild;
}
else {
father->rchild = delNode->rchild;
}
free(delNode);
}
////(3)删除有两个子节点的结点
else {
//找中序后继
BTNode* successor = findMidSuccessor(delNode);
int successorVal = successor->val; // 暂存后继值(只用知道值就可以实现替换了)
//删除中序后继,其必是叶子或是只有一个右子节点的结点,所以可以递归调用删除函数自身,走前两种情况解决
root = deleteXBST(root, successorVal);
// 用后继值替换待删节点的值
delNode->val = successorVal;
}
return root;
}
//5. 查询元素个数(递归)------二叉树通用
//重复小问题:一棵树的结点数=左子树结点个数+右子树结点个数+1(根节点)
int countBST(BTNode* root) {
if (!root) return 0;
int left = countBST(root->lchild);
int right = countBST(root->rchild);
return left + right + 1;
}
//前序遍历
void preAssist(BTNode* root)
{
if (!root) return;
printf("%d ", root->val);//根
preAssist(root->lchild);//左
preAssist(root->rchild);//右
}
//中序遍历
void midAssist(BTNode* root)
{
if (!root) return;
midAssist(root->lchild);//左
printf("%d ", root->val);//根
midAssist(root->rchild);//右
}
int main() {
int arr[] = { 3,1,2,5,4 };
BTNode* bst = createBST(arr, 5);
printf("初始中序遍历:");
midAssist(bst); // 输出:1 2 3 4 5
printf("\n");
insertBST(bst, 9);
printf("插入9后中序遍历:");
midAssist(bst); // 输出:1 2 3 4 5 9
printf("\n");
BTNode* search = searchXBST(bst, 2);
if (search) printf("找到节点2:get\n");
// 测试删除场景1:删除叶子节点(9)
bst = deleteXBST(bst, 9);
printf("删除叶子节点9后中序遍历:");
midAssist(bst); // 输出:1 2 3 4 5
printf("\n");
// 测试删除场景2:删除单孩子节点(1,只有右孩子2)
bst = deleteXBST(bst, 1);
printf("删除单孩子节点1后中序遍历:");
midAssist(bst); // 输出:2 3 4 5
printf("\n");
// 测试删除场景3:删除双孩子节点(3,有左右子树)
bst = deleteXBST(bst, 3);
printf("删除双孩子节点3后中序遍历:");
midAssist(bst); // 输出:2 4 5
printf("\n");
}
手写笔记辅助理解:



排序算法
选择排序
c
//交换函数
//
void swap(int* arr, int i,int j) {
if (!arr || i < 0 || j < 0) return;
if (i == j) return;
int temp = arr[i];
ar r[i] = arr[j];
arr[j] = temp;
}
//选择排序(升序)
//双层循环 O(n^2),逐个比较
void selectionSort(int* arr, int n) {
if (!arr || n <= 1) return;
for (int i = 0;i < n;i++) {
for (int j = 0;j < n;j++) {
if (arr[i] > arr[j]) swap(arr, i, j);
}
}
}
冒泡排序
c
//冒泡排序(升序)
//每轮循环停止条件是i==j
//j就是本轮冒泡需要排序的位置
// 冒泡排序就是指每一轮循环中待排序的数会像冒泡泡一样移动到j-1的位置
void bubbleSort(int* arr, int n) {
if (!arr || n <= 1) return;
for (int j = n - 1;j >= 0;j--) {
for (int i = 0;i < j;i++) {
if (arr[i] > arr[i + 1]) swap(arr,i,i+1);
}
}
}
