C语言数据结构学习(clion版本)

栈的应用(就近匹配,逆波兰表达式)

先放一张clion设置截图,用于分割不同作用的文件,以及怎么定义路径和引用。
在这里插入图片描述
需要在main.c同级的CMakeLists.txt中定义source_fileheader_file
CMakeLists.txt加入设置:

include_directories("source_file")
include_directories("header_file")

在这里插入图片描述
在source_file中对header_file中的文件进行实现,在头文件那里,可以通过相对路径引入,也可以通过CMakeLists.txt文件,设置。
在这里插入图片描述
这样就让clion中的C语言工程和visual studio中的一样了。注意,clion中,尽量不要用中文定义文件夹,容易出现路径寻找不到的问题。

括号匹配

#include <stdio.h>
#include "stackFinishByArray.h"

int isLeft(char ch){
   return ch=='(';
}

int isRight(char ch){
    return ch==')';
}

void printError(char *str,char *errMsg,const char *pos){
    printf("错误的信息:%s\n",errMsg);
    printf("%s\n",str);

    //计算打印空格的数量
    long long num = pos-str; //char型指针地址(占一个字节)相减
    for (int i = 0; i < num; ++i) {
        printf(" ");
    }
    printf("|\n");
}
void test01()
{
    char *str = "5+5*(6)+*/3*1-(1+3(";
    char *p =str;   //定义一个指针指向字符串的头地址
    seqStack myStack = init_Stack();
    while (*p!='\0'){ //没有到字符串的终止符的时候

        //判断是不是左括号
        if(isLeft(*p)){ //
            push_SeqStack(myStack,p);
        }

        //判断是不是右括号
        if(isRight(*p)){
            if(size_SeqStack(myStack)>0){  //栈不为空,弹出栈
                pop_SeqStack(myStack);
            } else{  //栈为空,直接报错,打印错误信息
                printError(str,"右括号没有匹配到左括号",p);
                break;
            }
        }
        p++;
    }

    //判断是否为空栈
    while (size_SeqStack(myStack)>0){

        printError(str,"左括号没有匹配到右括号", top_SeqStack(myStack));
        pop_SeqStack(myStack);
    }

}
int main() {
    test01();
    //printf("Hello, World!\n");
    return 0;
}

在这里插入图片描述

中缀转后缀表达式

算法流程

在这里插入图片描述
在这里插入图片描述

中缀表达式 转后缀表达式

可以利用自己已经实现的栈

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define MAX 1024
//
定义栈的结构体
//struct SStack{
//    void * data[MAX]; //数组
//    int m_Size;
//};

typedef void* seqStack;
//开始编写stack的接口

//初始化栈
seqStack init_Stack();

//入栈
void push_SeqStack(seqStack stack,void * data);

//出栈
void pop_SeqStack(seqStack stack);
//获取栈顶元素
void* top_SeqStack(seqStack stack);

//返回栈大小
int size_SeqStack(seqStack stack);

//判断栈是否为空(0为空,1非空)
int isEmpty(seqStack stack);


//销毁栈
void destroy_SeqStack(seqStack stack);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stackFinishByArray.h" //(1)../header_file/通过相对路径引入  (2)通过CMakeLists.txt 定义路径
#define MAX 1024

//定义栈的结构体
struct SStack{
    void * data[MAX]; //数组
    int m_Size;
};

typedef void* seqStack;
//开始编写stack的接口

//初始化栈
seqStack init_Stack(){

    struct SStack *myStack=malloc(sizeof (struct SStack)); //先给管理栈的结构体开辟空间
    if(myStack==NULL){
        return NULL;
    }

    //清空数组中的每个元素
    memset(myStack->data,0, sizeof(void*)*MAX); //将数组中的每个元素赋值为0

    myStack->m_Size=0;

    return myStack;
}

//入栈
void push_SeqStack(seqStack stack,void * data){
    if(stack==NULL){
        return;
    }

    //判断栈是否已经满了,如果满了,不能入栈
    struct SStack *myStack = stack;
    if(myStack->m_Size==MAX){
        return;
    }

    myStack->data[myStack->m_Size]=data; //入栈插入
    myStack->m_Size++; //更新栈大小
}

//出栈
void pop_SeqStack(seqStack stack){
    if(stack==NULL){
        return ;
    }

    //如果是空栈,不执行出栈
    struct SStack *myStack = stack;
    if(myStack->m_Size==0){
        return ;
    }

//    void *data = myStack->data[myStack->m_Size-1];
//    memset(myStack->data[myStack->m_Size-1],0,sizeof(void *));
    myStack->data[myStack->m_Size-1]=NULL;
    myStack->m_Size--;
}
//获取栈顶元素
void* top_SeqStack(seqStack stack){
    if(stack==NULL){
        return NULL;
    }

    struct SStack *myStack=stack;
    if(myStack->m_Size<=0){
        return NULL;
    }

    return myStack->data[myStack->m_Size-1];
}

//返回栈大小
int size_SeqStack(seqStack stack){
    if(stack==NULL){
        return -1;
    }

    struct SStack *myStack=stack;
    return myStack->m_Size;
}

//判断栈是否为空(0为空,1非空)
int isEmpty(seqStack stack){
    if(stack==NULL){
        return -1; //真
    }

    struct SStack *myStack=stack;
    if(myStack->m_Size<=0){
        return 1; //真
    } else{
        return 0; //假
    }
}

//销毁栈
void destroy_SeqStack(seqStack stack){
    if(stack==NULL){
        return;
    }

    free(stack);
    stack=NULL;
}

中缀转后缀是可以正常实现的,但是还有问题,就是对数字和字符的转换有问题,同时通过后缀去计算大表达式的值也有点问题,先记录在这里,有空来解一下bug。

#include <stdio.h>
#include "stackFinishByArray.h"

//我的这种写法不对//很多清空不符合(表达式中的数字提取就有问题)
//遍历后缀表达式计算结果
float calculate(seqStack stack, char *arr){
    if(arr ==NULL){
        return -1;
    }
    char *myArr = arr; //数组的首地址

    //开始入栈
    while (*myArr!='\0'){
        if(*myArr!='+' &&  *myArr!='-' &&  *myArr!='*' &&  *myArr!='/'){
            //这里就应该将其转化成数字,压入栈中
            push_SeqStack(stack,myArr);  //这样压入栈中的就全是数字了
            myArr++;
        } else{

            int element = size_SeqStack(stack);
            //弹出第一个数
            char *temp_first = top_SeqStack(stack); //这里由于是由数组实现,char *temp_first相当于指向了一个字符串,我们只需要提取首地址即可
            int first = *temp_first-'0';
            pop_SeqStack(stack);


            //弹出第二个数
            char *temp_second = top_SeqStack(stack);
            int second = *temp_second-'0';
            pop_SeqStack(stack);

            if(*myArr=='+'){
                int result = first + second;
                push_SeqStack(stack,&result);
                myArr++;
            } else if(*myArr=='-'){
                int result = second - first;
                push_SeqStack(stack,result);
                myArr++;
            } else if(*myArr=='*'){
                int result = second * first;
                push_SeqStack(stack,&result);
                myArr++;
            } else{
                float result = second / first;
                push_SeqStack(stack,&result);
                myArr++;
            }

        }
    }
    float *result = top_SeqStack(stack);
    return *result;
}
//利用返回值比较优先级
int priority(const char *ch){
    if(*ch=='*' || *ch=='/'){
        return 1;
    } else if(*ch=='+' || *ch=='-') {
        return 0;
    }
    return -1;
}
//中缀转后缀表达式
void test02(){

    char *str = "8+(3-1)*5"; //该表达式可以通过键盘输入
    seqStack myStack = init_Stack(); //初始化一个栈
    char temp[10];
    int index=0;
    char *pStr = str; //获取首元素地址
    while (*pStr!='\0'){

        if( *pStr!='+' && *pStr!='-' && *pStr!='*' && *pStr!='/' && *pStr!='(' && *pStr!=')'){
            temp[index++]=*pStr; //将数字交给字符数组存储
            pStr++;
            continue; //结束此次循环,进入下一次
        }
        if(*pStr=='('){
            push_SeqStack(myStack,pStr);
            pStr++;
            continue;
        }
        if(*pStr=='+' || *pStr=='-' || *pStr=='*' || *pStr=='/'){
            if(isEmpty(myStack)){ //若栈为空,直接入栈
                push_SeqStack(myStack,pStr);
                pStr++;
                continue;
            } else{ //栈不为空就需要比较优先级
                if(priority(pStr) > priority(top_SeqStack(myStack))){
                    push_SeqStack(myStack,pStr);
                    pStr++;
                    continue;
                } else{
                    char *tempChar = top_SeqStack(myStack);
                    temp[index++]=*tempChar; //将栈顶元素给数组,并弹出栈
                    pop_SeqStack(myStack); //栈顶元素出栈
                    push_SeqStack(myStack,pStr); //进栈
                    pStr++;
                    continue;
                }
            }
        }

        if(*pStr==')'){
            while (size_SeqStack(myStack)>0){
                char *tempChar = top_SeqStack(myStack); //先获取栈顶元素
                if(*tempChar=='('){
                    pop_SeqStack(myStack);
                    pStr++;
                    break;
                } else{
                    temp[index++]=*tempChar;
                    pop_SeqStack(myStack);
                }
            }
        }

    }

    if(!isEmpty(myStack)){  //栈不为空,依次把剩余的元素加入到字符数组中
        while (size_SeqStack(myStack)>0){
            char *tempChar = top_SeqStack(myStack); //先获取栈顶元素
            temp[index++]=*tempChar;
            pop_SeqStack(myStack);
        }
    }

    //printf("%d", size_SeqStack(myStack)); //说明到这里的时候就是空栈了
    //字符数组剩余的未赋值的空间,是‘\0’
    for (int i = 0; i < strlen(str); ++i) {
        printf("%c  ",temp[i]);
    }

    //此时计算完后缀表达式的栈是空的,可以重复利用
    printf("%.1f",calculate(myStack,temp));
}



int main() {
    test02();
    //printf("Hello, World!\n");
    return 0;
}

二叉树(遍历,树高,叶子数,拷贝二叉树,释放二叉树)(递归法实现)

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

struct BinaryNode{
    char ch; //数据域
    struct BinaryNode *lChild; //左孩子
    struct BinaryNode *rChild; //右孩子
};

//遍历二叉树
void recursion(struct BinaryNode *root){
    if(root==NULL){
        return;
    }

    //前序遍历 根左右
    printf("当前节点的值为:%c\n",root->ch); //打印当前节点的值
    if(root->lChild!=NULL){
        recursion(root->lChild);
    }

    if(root->rChild!=NULL){
        recursion(root->rChild);
    }

    //中序遍历
//    recursion(root->lChild);
//    printf("当前节点的值为:%c\n",root->ch); //打印当前节点的值
//    recursion(root->rChild);
}

//二叉树的计算
void calculateLeafNum(struct BinaryNode *root,int *num){
    if(root==NULL){
       return ;
    }

    if(root->lChild==NULL && root->rChild==NULL){
        (*num)++;
    }
    calculateLeafNum(root->lChild,num);
    calculateLeafNum(root->rChild,num);
}

//树高
int getTreeHeight(struct BinaryNode *root){
    if(root==NULL){
        return 0;
    }

    int lHeight = getTreeHeight(root->lChild);
    int rHeight = getTreeHeight(root->rChild);

    int height = lHeight>rHeight? lHeight+1 : rHeight+1;
    return height;
}

struct BinaryNode *copyBinaryTree(struct BinaryNode * root){
    if(root==NULL){
        return NULL;
    }

    //先拷贝左子树
    struct BinaryNode *lchild = copyBinaryTree(root->lChild);

    //再拷贝右子树
    struct BinaryNode *rchild = copyBinaryTree(root->rChild);
    //创建新的节点
    struct BinaryNode *newNode = malloc(sizeof(struct BinaryNode)); //在堆空间中,开辟一个空间,存储节点

    newNode->ch=root->ch;
    newNode->lChild=lchild;
    newNode->rChild=rchild;

    //返回给用户
    return newNode;
}

void freeBinaryTree(struct BinaryNode *root){
    if(root==NULL){
        return;
    }

    //先释放左子树
    freeBinaryTree(root->lChild);
    //先释放右子树
    freeBinaryTree(root->rChild);
   //释放根节点
    free(root);
}

void test01(){

    struct BinaryNode NodeA={'A',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeB={'B',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeC={'C',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeD={'D',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeE={'E',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeF={'F',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeH={'H',NULL,NULL}; //初始化一个节点
    struct BinaryNode NodeG={'G',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);

    //统计二叉树中的叶子的数量
    int num=0; //局部遍历必须赋初值,只有全局,结构体这些,再堆空间或者常量池的中的,可以不用赋初值
    calculateLeafNum(&NodeA,&num);
    printf("%d\n",num);

    //获得树的高度
    int height=getTreeHeight(&NodeA);
    printf("%d\n",height);

    //拷贝一个二叉树
    printf("------------------------\n");
    struct BinaryNode * newNode = copyBinaryTree(&NodeA);
    recursion(newNode);

    //释放二叉树
    freeBinaryTree(newNode);
}


int main(){

    test01();
    printf("hello,world!");
    return 0;
}

非递归法,利用栈实现对二叉树的遍历

clion中,对多行进行编辑,同时按住alt+shift对多行进行编辑
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stackFinishByArray.h" //利用数组实现的栈来对二叉树进行遍历

struct BinaryNode{
    char ch; //数据域
    struct BinaryNode *lChild; //左孩子
    struct BinaryNode *rChild; //右孩子
    int flag;
};

void nonRecursionBinaryTree(struct BinaryNode *root){
    if(root==NULL){
        return;
    }

    seqStack myStack = init_Stack(); //先初始化一个空栈
    //将根节点压入栈中
    push_SeqStack(myStack,root);
    //当栈中元素大于0时,执行循环
    while (size_SeqStack(myStack)>0){
        //首先拿出栈顶元素
        struct BinaryNode *topNewNode = top_SeqStack(myStack);
        pop_SeqStack(myStack);
        //栈顶元素为真,直接输出
        if(topNewNode->flag==1){
            printf("输出的元素为:%c\n",topNewNode->ch);
            continue;
        }
        //如果栈顶元素为假,则改为真
        topNewNode->flag=1;
        //并将该节点的右子树,左子树,根压入栈中
        if(topNewNode->rChild!=NULL){
            push_SeqStack(myStack,topNewNode->rChild);
        }
        if(topNewNode->lChild!=NULL){
            push_SeqStack(myStack, topNewNode->lChild);
        }
        push_SeqStack(myStack,topNewNode);

    }

    //销毁这个栈
    destroy_SeqStack(myStack);
    myStack=NULL;
}

void test03(){
    struct BinaryNode NodeA={'A',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeB={'B',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeC={'C',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeD={'D',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeE={'E',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeF={'F',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeH={'H',NULL,NULL,0}; //初始化一个节点
    struct BinaryNode NodeG={'G',NULL,NULL,0}; //初始化一个节点

    //构建关系
    NodeA.lChild=&NodeB;
    NodeA.rChild=&NodeF;

    NodeB.rChild=&NodeC;

    NodeC.lChild=&NodeD;
    NodeC.rChild=&NodeE;

    NodeF.rChild=&NodeG;

    NodeG.lChild=&NodeH;

    nonRecursionBinaryTree(&NodeA);

}
int main(){
    test03();
    return 0;
}

在这里插入图片描述

更改文件的输出编码格式

在这里插入图片描述