第四章--栈

特点

后进先出
单端操作  只能从栈顶插入删除

栈的定性应用

逆序输出-进制转换
递归版本

void convert(int n, stack <char> &s, int base) {
    if (n < 1) return;
    char digit[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
    s.push(digit[n%base]);
    convert(n / base, s, base);
}

栈混洗

A,B,S三个栈,A含有n个元素,B,S初始为空。只允许S.push(A.pop())弹出栈A的顶元素并随即压入栈S中,B.push(S.pop))弹出栈S的顶元素并随即压入B中,经过n次操作之后,A,S栈已为空,原A中的元素均已转入到B中,B中的一个可能的序列为A的一个栈混洗。

对于任何的1<=i <j<k<=n,...k...i...j必为“禁行”。

括号匹配

bool paren(char exp[], int lo, int hi) {
    stack<char> s;
    for (int i = lo; i<hi; i++) {
        switch (exp[i]) {
        case '(': case '{': case '[':s.push(exp[i]);
        case ')':if (!s.empty() && s.top() == '(') s.pop(); else return false; break;
        case ']':if (!s.empty() && s.top() == ']') s.pop(); else return false; break;
        case '}':if (!s.empty() && s.top() == '}') s.pop(); else return false; break;
        }
    }
    return s.empty();
}

延迟缓冲-表达式求值

#define N_OPTR 9//运算符总数
typedef enum {ADD,SUB,MUL,DIV,FAC,POW,L_P,R_P,EOE} Operator;//运算符集合
const char pri[N_OPTR][N_OPTR]={//运算符
    /*----------------------------当前运算符-----------------*/
    /*              +       -       *       /       POW     !       (       )       \0  */
    /*  +   */      '>',    '>',    '<',    '<',    '<',    '<',    '<',    '>',    '>',
    /*栈 -   */      '>',    '>',    '<',    '<',    '<',    '<',    '<',    '>',    '>',
    /*顶*    */      '>',    '>',    '>',    '>',    '<',    '<',    '<',    '>',    '>',
    /*运 /   */      '>',    '>',    '>',    '>',    '<',    '<',    '<',    '>',    '>',
    /*算 pow */      '>',    '>',    '>',    '>',    '>',    '<',    '<',    '>',    '>',    
    /*符 !   */      '>',    '>',    '>',    '>',    '>',    '>',    ' ',    '>',    '>',
    /*  (   */      '<',    '<',    '<',    '<',    '<',    '<',    '<',    '=',    ' ',
    /*  )   */      ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',    ' ',
    /*  \0  */      '<',    '<',    '<',    '<',    '<',    '<',    '<',    ' ',    '=',

};

bool isDigit(char a){
    return a>='0'&&a<='9'?true:false;
}

void readNumber(char*& p,Stack<float>& s){
    s.push((float)(*p-'0'));
    while(isDigit(*(++p)))
        {
            float tmp=s.top()*10+(*p-'0');
            s.pop();
            s.push(tmp);
        }
        if('.'!=*p) return;//此后非小数点,书名已经完成操作数的解析
        float fraction=1;
        while(isDigit(*(++p)))
        {
            float tmp=s.top()+(*p-'0')*(fraction/=10);
            s.pop();
            s.push(tmp);
        }

}

Operator optr2rank(char op){//由运算符转译出编号
    switch(op){
        case '+':return ADD;
        case '-':return SUB;
        case '*':return MUL;
        case '/':return DIV;
        case '^':return POW;
        case '!':return FAC;
        case '(':return L_P;
        case ')':return R_P;
        case '\0':return EOE;//起始符和终止符
        default:exit(-1);//未知运算符
    }
}

char orderBetween(char op1,char op2){//比较两个运算符之间的优先级
    return pri[optr2rank(op1)][optr2rank(op2)];
}

void append(char*& rpn,float opnd){//将操作数接至末尾
    int n=strlen(rpn);
    char buf[64];
    if(opnd!=(float)(int)opnd) sprintf(buf,".2f\0",opnd);//浮点格式,或
    else sprintf(buf,"%d\0",(int)opnd);//整数格式
    rpn=(char*)realloc(rpn,sizeof(char)*(n+strlen(buf)+1));//扩展空间
    strcat(rpn,buf);//RPN加长
}

void append(char*& rpn,char optr){
    int n=strlen(rpn);//RPN当前长度(以\0结尾,长度n-1)
    rpn=(char*)realloc(rpn,sizeof(char)*(n+3));//扩展空间
    sprintf(rpn+n,"%c",optr);rpn[n+2]='\0';//接入指定的运算符
}

float evalate(char* S,char* &RPN){//已剔除空白字符的表达式S求值,并转换为逆波兰式RPN
    stack<float> opnd;stack<char> optr;//运算数栈,运算符栈
    optr.push('\0');//尾哨兵'\0'作为头哨兵入栈
    while(!optr.empty()){//在运算符非空之前逐个处理表达式中的各个字符
        if(isDigit(*S)){//若当前字符为操作数,则
            readNumber(S,opnd);append(RPN,opnd.top());//读入操作数,并将其接至RPN末尾
        }else{//若当前字符为操作符
            switch(orderBetween(optr.top(),*S)){//视其余栈顶元素的优先级操作
                case '<'://栈顶元素的优先级更低时
                    optr.push(*S);S++;
                    break;
                case '>'://栈顶元素更高。可实施相应的计算,并将结果重新入栈
                    {
                        char op=optr.top();optr.pop();append(RPN,op);//栈顶运算符出栈并续接至RPN末尾
                        if('!'==op)//若属于一元运算符
                            {
                                float pOpnd=opnd.top();opnd.pop();//只取出一个数并实施计算
                                opnd.push(calcu(op,pOpnd));
                            }else{//对于其它二元运算
                                float pOpnd2=opnd.top();opnd.pop();
                                float pOpnd1=opnd.top();opnd.pop();
                                opnd.push(calcu(pOpnd1,op,pOpnd2));
                            }
                            break;
                    }
                default:exit(-1);//逢语法错误,不做处理直接退出
            }
        }
    }
    return opnd.pop();
}
当栈顶操作符大于当前操作符(例如“2*3+1”),则进行运算。‘)’大于任何操作符;
当栈顶操作符小于当前操作符(例如“1+2*3”),则将当前操作符入栈。‘(’小于任何操作符;
当栈顶操作符等于当前操作符(例如“()”),则将栈顶操作符弹出。‘(’==‘)’和‘\0’==‘\0’.

以上算法假设公式语法正确。
逆波兰数

语法规则:操作符紧邻与对应的(最后一个)操作数之后。
常用于表达式计算,因为运算符被执行的次序与其在RPN数中的次序完全一致
具体算法:凡遇到操作数,即追加到rpn,而运算符只有在从栈中弹出并执行时才被追加。

八皇后问题

#include <iostream>          
#include <vector>       
using namespace std;
class Solution {
public:
    const static int QUEENFREE = INT_MAX;
    bool eightQueens(vector<int> &vi, int n ) {
        vi.clear();
        vi.resize(n, QUEENFREE);
        int k = 0;
        while (k >= 0) {
            //k代表即将需要填入皇后的行数
            int i = 0;
            for (; i<n; i++) {//i代表在这一行填入的行数
                if (islegalMove(vi, k, i)) {
                    //如果vi[k]不等于QUEENFREE那么就是说明这是从下一层返回来的k值
                    //第一次进入该层就应该满足条件就填写了,但是如果是从下一层返回来的话
                    //就不能填写比以前更小的值了,这也是Backtracking算法的一个特征
                    //是按一定循环的值循环探索适合的解。
                    //这里不用栈,只用k执行了差不都是栈的功能
                    if (vi[k] == QUEENFREE || vi[k]<i) {
                        vi[k] = i;
                        //判断已经填到了最后一行,那么就是成功了
                        if (k == n - 1) return true;
                        k++;
                        break;
                    }
                }
            }
            if (i == n) {
                //k行填写了所以可能的值都不符合要求,那么就返回上一层
                //这时候需要把k行还原为原始状态,代表返回了上一层,这样方便上面判断
                vi[k] = QUEENFREE;
                k--;
            }
        }
        //最后Backtracking到了根,那么就是没有解了。
        return false;
    }

    bool islegalMove(vector<int>& vi, int row, int col) {
        //判断列是否符合规定
        for (int i = 0; i<vi.size(); i++) {
            if (i != row&&vi[i] == col)
                return false;
        }
        //判断对角线是否符合规定,后面的哦安定公式费点神
        for (int i = 0; i<vi.size(); i++) {
            if (i != row && (vi[i] - i == col - row || vi[i] + i == col + row)) {
                return false;
            }
        }
        //不用判断行了,因为默认是一行只天一个queen的
        return true;
    }
};

int main() {
    vector<int>queen;
    Solution solu;
    if (solu.eightQueens(queen, 4))
        for (auto x : queen) {
            for (int i = 0; i<queen.size(); i++) {
                if (i == x)
                    cout << x << "QUEEN" << "\t";
                else
                    cout << "#" << "\t";
            }
            cout << endl;
        }
    else cout << "No Solution";
    cout << endl;
    system("pause");
    return 0;
}

递归版本

//递归版本的八皇后
#include<iostream>
using namespace std;
#define QUEEN_MAX 20
int nSolu=0;//解的总数
int nCheck=0;//尝试的总次数

void displaySolution(int *solu,int n){//输出n*n的可行布局
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            printf((j==solu[i])?"█":"[]");
        }
        printf("\n");
    }
    cout<<nSolu<<"solution(s) found after"<<nCheck<<"check(s)\a";
    printf("\n");
}

bool collide(int *solu,int k){//判断是否可以在当前行的第k列放置下一个皇后
    nCheck++;
    for(int i=0;i<k;i++){
        if(solu[k]==solu[i]) return true;//l列
        if(solu[k]-solu[i]==k-i) return true;//对角线
        if(solu[k]-solu[i]==i-k) return true;//反对角线
    }
    return false;
}

void placeQueens(int nQueen,int k){//放置n皇后中的第k个
    static int solu[QUEEN_MAX];//解法
    if(nQueen<=k){//若所有皇后都已经就位,则
        nSolu++;displaySolution(solu,nQueen);//输出可行解
    }else//否则
    for(int i=0;i<nQueen;i++){//依次
        solu[k]=i;//试着将当前皇后放在(当前行的各列上
        if(!collide(solu,k))//若没有冲突,则
            placeQueens(nQueen,k+1);//进而考虑下一个皇后
    }
}//如何回溯???

int main(){
    int nQueen=8;
    placeQueens(nQueen,0);//启动算法
    cout<<nSolu<<"solution found after"
    <<nCheck<<"check for"
    <<nQueen<<"queen(s)\n";
    return 0;
}

迷宫寻径

#include<iostream>
#include<stack>
#include<time.h>
using namespace std;
//迷宫寻径

typedef enum { AVAILABLE, ROUTE, BACKTRACKED, WALL } Status;//迷宫单元状态
                                                            //原始可用的,在当前路径上的,所有方向均尝试失败后回溯过得,不可使用的(墙)
typedef enum { UNKNOWN, EAST, SOUTH, WEST, NORTH, NO_WAY } ESWN;//单元的相对邻接方向
                                                                //未定    东   南   西   北   无路可通

inline ESWN nextESMN(ESWN eswn) { return ESWN(eswn + 1); }//依次转至下一邻接方向

struct Cell
{//迷宫格点
    int x, y; Status status;//x坐标 y坐标 类型
    ESWN incoming, outgoing;//进入 走出方向
};

int labySize;
Cell* startCell;
Cell* goalCell;

#define LABY_MAX 24//最大迷宫尺寸
Cell laby[LABY_MAX][LABY_MAX];//迷宫

void randLaby() {//生成随机的迷宫
    labySize = LABY_MAX / 2 + rand() % (LABY_MAX / 2);
    printf("Using a laby of size %d...\n", labySize);
    for (int i = 0; i < labySize; ++i)
        for (int j = 0; j < labySize; ++j)
        {
            laby[i][j].x = i;
            laby[i][j].y = j;
            laby[i][j].incoming = laby[i][j].outgoing = UNKNOWN;
            laby[i][j].status = WALL;//边界格点必须是墙
        }
    for (int i = 1; i<labySize - 1; i++)
        for (int j = 1; j<labySize - 1; j++)
            if (rand() % 4) laby[i][j].status = AVAILABLE;//75%的格点为空可用
    startCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
    goalCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
    startCell->status = goalCell->status = AVAILABLE;//起始格点必须可用
}

inline Cell* neighbor(Cell*cell) {//查询当前位置的相邻格点
    switch (cell->outgoing) {
    case EAST:return cell + LABY_MAX;//向东
    case SOUTH:return cell + 1;//向南
    case WEST:return cell - LABY_MAX;//向西
    case NORTH:return cell - 1;//向北
    default:exit(-1);
    }

}

inline Cell* advance(Cell* cell) {//从当前位置转入相邻节点
    Cell*next;
    switch (cell->outgoing) {
    case EAST:next = cell + LABY_MAX; next->incoming = WEST; break;//向东
    case SOUTH:next = cell + 1; next->incoming = NORTH; break;//向南
    case WEST:next = cell - LABY_MAX; next->incoming = EAST; break;//向西
    case NORTH:next = cell - 1; next->incoming = SOUTH; break;//向北
    default:exit(-1);
    }
    return next;
}

//输出某一迷宫的信息
void printLabyCell(Cell* elem) {
    printf("%d->(%d,%d)->%d\n",
        ((Cell*)elem)->incoming,
        ((Cell*)elem)->x,
        ((Cell*)elem)->y,
        ((Cell*)elem)->outgoing);
}

//显示迷宫
void displayLaby() { //┘└┐┌│─
    static char*   pattern[5][5] = {
        "┼", "┼", "┼", "┼", "┼",
        "┼", "  ", "┌", "─", "└",
        "┼", "┌", "  ", "┐", "│",
        "┼", "─", "┐", "  ", "┘",
        "┼", "└", "│", "┘", "  "
    };
    system("cls");
    printf("  ");
    for (int j = 0; j < labySize; j++)
        (j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
    printf("\n");
    for (int j = 0; j < labySize; j++) {
        (j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
        for (int i = 0; i < labySize; i++)
            if (goalCell == &laby[i][j])
                printf("﹩");
            else
                switch (laby[i][j].status) {
                case WALL:  printf("█");   break;
                case BACKTRACKED: printf("○");   break;
                case AVAILABLE: printf("  ");   break;
                default: printf("%s", pattern[laby[i][j].outgoing][laby[i][j].incoming]);   break;
                }
        printf("\n");
    }//for
}

//迷宫寻径算法:在格单元s至t之间规划一条通路(如果的确存在)
bool labyrinth(Cell laby[LABY_MAX][LABY_MAX], Cell*s, Cell*t) {
    if ((AVAILABLE != s->status) || (AVAILABLE != t->status)) return false;//退化情况
    stack<Cell*> path;//用栈记录通路
    s->incoming = UNKNOWN; s->status = ROUTE; path.push(s);//起点
    do {//从起点出发不断试探,回溯,直到抵达终点,或者穷尽所有可能
        displayLaby();
        Cell* c = path.top();//检查当前位置(栈顶)
        if (c == t) return true;//若已抵达终点,则找到一条通路;否则,沿尚未试探的方向继续试探
        while (NO_WAY>(c->outgoing = nextESMN(c->outgoing)))//逐一检查所有方向
            if (AVAILABLE == neighbor(c)->status) break;//视图找到尚未试探的方向
        if (NO_WAY <= c->outgoing)//若所有方向都已尝试过
        {
            c->status = BACKTRACKED; c = path.top(); path.pop();
        }//则向后回溯一部
        else//否则,向前试探一步
        {
            path.push(c = advance(c)); c->outgoing = UNKNOWN; c->status = ROUTE;
        }
    } while (!path.empty());
    return false;
}


int main(int argc, char *argv[])
{
    /* code */
    srand((unsigned int)time(NULL));//设置随机种子
    randLaby();//随即生成
    labyrinth(laby, startCell, goalCell) ?//启动算法
        printf("\nRoute found\a\n") :
        printf("\nNo route found\a\n");
    system("pause");
    return 0;
}

队列

特点

单端操作-从对头取出,从队尾插入
先进先出
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
递归函数是在函数内部调用自身的一种编程技巧。它是一种简单且强大的算法思维工具,适用于解决许多问题,比如数学问题、字符串问题、树形问题等等。 递归函数通常包括两个部分:基本情况和递归情况。基本情况是指递归函数停止调用自身的条件,也就是递归的出口。递归情况是指递归函数在每次调用自身时所执行的操作。 递归函数的一个重要特点是它可以处理具有无限深度的数据结构,比如树或链表。递归函数可以像深度优先搜索一样遍历整个树或链表,并且可以使用递归函数来解决许多树形问题,比如求树的高度、计算树中节点的个数、求树的直径等等。 递归函数的另一个应用是在排序算法中。比如快速排序就是一种基于递归的排序算法,它利用了递归函数的特性,在每次递归调用中将问题分解成更小的子问题,并且将这些子问题分别排序,最终得到整个序列的有序结果。 虽然递归函数是一种强大的算法思维工具,但是它也有一些缺点。递归函数通常需要更多的内存空间和运行时间,因为每一次递归调用都会在内存中创建一个新的函数帧。此外,递归函数可能会导致溢出的问题,因为每一次递归调用都会将函数帧压入堆中,如果递归的深度太大,就会导致堆溢出。 因此,在编写递归函数时,需要考虑好递归的出口条件,并且需要测试递归函数的性能和内存使用情况,以确保程序的正确性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值