回溯法基本思想_LeetCode--回溯法心得

4e554ab86316dac777b82d223b261818.png

这两天在刷LeetCode37题解数独时,被这个回溯法折腾的不要不要的,于是我疼定思疼发誓一定要找个能解决这类回溯法的套路出来,方便以后快速解决此类题目。于是我在网上找了两个很经典的回溯法题目--八皇后问题和迷宫问题,认真总结了一番,发现其中还真的有一些共同之处,我会在下面好好讲述。

首先,回溯法所用到的核心思想就是递归法,虽然其过程逻辑很清楚,而且执行效率很高。但缺点也是与之对应的,逻辑清楚,必然其抽象性很高,所以有时候就是这样的情况:看它的解题过程很容易看懂,但要是让你自己动手写这个递归过程,发现很难下笔。当时我就是这样的情况,于是我想在网上找找看看能不能有哪位大佬分享一些解题心得供我们这些算法渣渣思考学习的,然而网上的一些csdn博客(这儿不是刻意贬低csdn的哈)写的东西都是千篇一律,都是在把一些旧的不能再旧的东西炒来炒去,没有一点营养!别人靠不住,那就只能靠自己呗!于是我自己找例子,自己琢磨,终于是有些小收获,本着慈悲为怀的精神,故想拿出来供大家参考,避免大家少趟坑,保证不打马虎眼,不炒现饭。当然了,若有不当之处,请各位笔友指出,面的误人子弟。

1 八皇后问题

问题描述:

该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

看完问题描述后,大家之前要是熟悉此题目的也可以先动手做做,看看还能不能解出来。再正式回答此题目之前我还是先把我写的答案贴出来,让大家对整个处理过程有个大致的印象。

八皇后问题代码如下:

# 检测皇后之间的位置关系
def conflict(queen_str, current_queen):""":param queen_str: str-->指代当前皇后存放之前的所有皇后的集合:param current_queen: int-->指代当前皇后想要存放的位置:return:Flag: boolean-->指代当前位置的皇后是否与之前所有位置的皇后有冲突"""# 此处的queen_length既是之前保存的queen_list集合的长度,也可以理解为当前current_queen皇后的行下标queen_length = len(queen_str)# 定义是否有位置冲突的标签Flag = Falsefor index in range(queen_length):# queen_length - index主要是控制相邻两行的皇后不能处于对角线上,其他的就没要求if abs(current_queen-int(queen_str[index])) in(0, queen_length-index):Flag = Truebreakreturn Flag# 定义执行皇后问题的主函数
def queens(nums=8, queen_str=""):""":param nums: int-->指代整个棋盘中想要存放皇后的个数:param queen_str: str-->指代当前皇后存放之前的所有皇后的集合:return:final_queens: List[int]-->指代最后符合要求的皇后的位置"""final_queens = []# 定义递归函数,获取所有八皇后的值def back(queen_str):# 出口条件if len(queen_str) == nums:final_queens.append(queen_str)returnfor index in range(nums):Flag = conflict(queen_str, index)# 如果当前位置的皇后是否与之前所有位置的皇后没有冲突,则执行下述代码if Flag is False:back(queen_str+str(index))back(queen_str)return final_queensif __name__ == "__main__":final_queens = queens()print(final_queens)print(len(final_queens))

写的应该还是比较清楚的,大家也可以再看看官方给的回溯法的描述

描述:

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

其实我总结起来就3点:

1 出口。一个递归算法一定要有出口,否则就是一个死循环了。出口语句一般都挺好写的,但 是出口语句该放在哪儿了,这个就是关键了,这儿容许我先卖个关子。

2 递归函数的参数。一般情况下,递归函数是要带参数的,因为递归操作都是用来处理下一次的过程,如果没有参数的话,那么就很难从下一次的操作回溯到当前操作了。这么说,可能会有点迷糊,别急,后面我会举例子,这儿还是卖个关子。

3 递归函数的处理过程。这个自不必多说,重中之重,需要好好理解其过程

上面3点就是我总结的关于回溯法的关键点了,我觉得只要真正的把这3步吃透,一般的回溯法题目是ok的(这可不是我吹牛哈)下面我就这3点仔细讲讲,大家可要竖起耳朵通清楚了哈。

1 出口

关于这个出口条件,就像我上面说的,它的关键是出口语句放置的位置,因为这个语句其实挺好写的,一般也就2-3行代码,大多数人都能想出来。但我觉得大多数人苦恼的就是不知道该把它放在哪儿,我刚开始也是这样,后面总结了2-3题之后,我发现了一个万能规律,就是把出口语句放在递归函数的第一行就行,大家可以看看八皇后问题的递归函数back()以及迷宫问题的递归函数back(),我这儿就直接贴出来。

八皇后问题的递归函数back()

# 定义递归函数,获取所有八皇后的值def back(queen_str):# 出口条件if len(queen_str) == nums:final_queens.append(queen_str)returnfor index in range(nums):Flag = conflict(queen_str, index)# 如果当前位置的皇后是否与之前所有位置的皇后没有冲突,则执行下述代码if Flag is False:back(queen_str+str(index))

迷宫问题的递归函数back()

    def back(position=start, pos_list=[start]):# 该递归函数的出口if position == final_position:route.append(pos_list)print("successful")returnpos_x = position[0]pos_y = position[1]for direction in walk_route:next_position = [pos_x+direction[0], pos_y+direction[1]]if isValid(nums, next_position):# 记住,这儿一定要用另一个list集合保存当前路线pos_list以及该路线下一个位置,方便回溯找到pos_list# 如果直接对pos_list添加next_position,则不能回溯找到之前的pos_listpos_list_copy = []pos_list_copy.extend(pos_list)pos_list_copy.append(next_position)nums[pos_x, pos_y] = 0back(next_position, pos_list_copy)# 如果没有找到出口,则将当前上一个位置0重置为1,回溯nums[pos_x, pos_y] = 1

大家一对比就很清楚的看到,出口语句都是写在最前面的,其实最主要的就是不能把出口语句放在for和while循环语句里面,因为出口语句一定要方便整个函数退出,大家听不懂的强行记住没问题的,要是出了问题,也别来找我,啊哈哈哈哈哈。

2 递归函数的参数

这个递归函数的参数的设置也是有很大门道的,设置的好就很容易得到答案,否则弄大半天可能还是没有一点反应。大家一定要记住一点:这个参数是随着每一次的递归操作而发生改变的。而回溯法很关键的一点就是:如果当前操作行不通,如何回溯到上一步操作。大家继续看上面贴的两个递归函数的参数,会发现其参数都是要改变的,既然参数会发生改变,那么我们要如何保存其上一步操作的值呢?大家可以再细细看看上述两个函数的传值操作。

八皇后问题的传值操作

for index in range(nums):Flag = conflict(queen_str, index)# 如果当前位置的皇后是否与之前所有位置的皇后没有冲突,则执行下述代码if Flag is False:back(queen_str+str(index))

大家可以看到back(queen_str+str(index))这一步,其传的参数就是queen_str+str(index) 其实想法就是不破坏当前参数的值,直接把当前值加上一个值(大家可以理解为定义了另一个非queen_str当前值的值给传到下一次函数),只要不破坏当前值,函数就能回溯。这一步很关键,大家可以好好品味。

for index in range(nums):Flag = conflict(queen_str, index)# 如果当前位置的皇后是否与之前所有位置的皇后没有冲突,则执行下述代码if Flag is False:queen_str = queen_str+str(index)back(queen_str )

如果大家还有些疑惑的话,可以再把传值操作改成这样试试,你会发现结果会大相径庭的,这里就是破坏了当前值。

迷宫问题的传值操作

if isValid(nums, next_position):# 记住,这儿一定要用另一个list集合保存当前路线pos_list以及该路线下一个位置,方便回溯找到pos_list# 如果直接对pos_list添加next_position,则不能回溯找到之前的pos_listpos_list_copy = []pos_list_copy.extend(pos_list)pos_list_copy.append(next_position)nums[pos_x, pos_y] = 0back(next_position, pos_list_copy)# 如果没有找到出口,则将当前上一个位置0重置为1,回溯nums[pos_x, pos_y] = 1

大家再可以参考迷宫操作的传值操作理解。

关于参数,我还有一点就是强调:就是结果一定是要有一个全局参数来保存,这个全局参数不会随着每一次的递归操作而随时改变,它只是用来保存每一次递归操作成功时的结果,其它的不关它的事。你仔细看看这两个程序也会发现:它们在一开始就定义了一个List空列表。大家也可以照搬的,凡是结果需要保存的题目90%以上就是要预先定义一个List空列表(不要问我这个90%数据是怎么得来的哈,问了我也不知道,哈哈哈哈哈)

八皇后问题的List空列表

# 定义执行皇后问题的主函数
def queens(nums=8, queen_str=""):""":param nums: int-->指代整个棋盘中想要存放皇后的个数:param queen_str: str-->指代当前皇后存放之前的所有皇后的集合:return:final_queens: List[int]-->指代最后符合要求的皇后的位置"""# 定义一个保存结果的List列表final_queens = []

迷宫问题的List空列表

"""
迷宫问题,使用回溯法
"""
def maze(nums, start):""":param nums: List[List[int]]-->指代所给的迷宫:param start: List[int X, Y]-->指代起始点位置:return: route: List[]"""# 定义最终路线的集合route = []

3 递归函数的处理过程

这个过程是最关键的了,但是也很少有人能把它说清楚,当然也包括我。我想来想去,总结起来一句话就是:如果当前递归过程的处理参数符合要求,则执行相关赋值或其它操作,然后转入下一次递归,如果下一次递归不能找到出口,则把之前相关赋值或其它操作重置为初始状态。说的有些抽象,但我目前确实是说的这么样了,还需要自己好好看几个题目,好好做几道题目才能理解这层意思。大家也可以好好看看下述迷宫问题的处理过程。

迷宫问题的处理过程

nums[pos_x, pos_y] = 0
back(next_position, pos_list_copy)
# 如果没有找到出口,则将当前上一个位置0重置为1,回溯
nums[pos_x, pos_y] = 1

2 迷宫问题

问题描述:

定义一个二维数组:

int maze[5][5] = {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0,};

它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

解题思路:

本题我才用的方法也是很常规的广度优先搜索(BFS)也就是定义四个方向,即上下左右,对迷宫内每个可以通过的点执行该四个方向的操作。具体的操作我这儿就不说了,直接贴代码吧!

迷宫问题代码如下:

mport numpy as np# 检查当前位置是否有效
# 如果当前位置为0,则表示不能通过;
# 如果当前位置表示为1,则表示可以继续通过
def isValid(nums, current_position):''':param nums: List[List[int]]-->指代所给的迷宫:param current_position: List[int X, Y]-->指代当前坐标点位置:return: boolean-->指代当前位置是否有效'''pos_x = current_position[0]pos_y = current_position[1]if pos_x in range(len(nums)) and pos_y in range(len(nums)) and nums[pos_x, pos_y] == 1:return Trueelse:return False"""
迷宫问题,使用回溯法
"""
def maze(nums, start):""":param nums: List[List[int]]-->指代所给的迷宫:param start: List[int X, Y]-->指代起始点位置:return: route: List[]"""# 定义最终路线的集合route = []# 定义当前点上下左右移动方向的集合walk_route = [[-1, 0], [0, -1], [1, 0], [0, 1]]# 获取迷宫的终点nums_length = len(nums)final_position = [nums_length-1, nums_length-1]def back(position=start, pos_list=[start]):# 该递归函数的出口if position == final_position:route.append(pos_list)print("successful")returnpos_x = position[0]pos_y = position[1]for direction in walk_route:next_position = [pos_x+direction[0], pos_y+direction[1]]if isValid(nums, next_position):# 记住,这儿一定要用另一个list集合保存当前路线pos_list以及该路线下一个位置,方便回溯找到pos_list# 如果直接对pos_list添加next_position,则不能回溯找到之前的pos_listpos_list_copy = []pos_list_copy.extend(pos_list)pos_list_copy.append(next_position)nums[pos_x, pos_y] = 0back(next_position, pos_list_copy)# 如果没有找到出口,则将当前上一个位置0重置为1,回溯nums[pos_x, pos_y] = 1back()return routeif __name__ == "__main__":nums = [[1, 0, 0, 1, 0, 1], [1, 1, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0], [0, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1],[1, 0, 0, 0, 1, 1]]nums = np.array(nums)print(nums)current_position = [0, 0]print(maze(nums, current_position))

该讲的我在八皇后问题那儿都讲到了,本题大家就用作检验方法和思考作用吧。

3 解数独

问题描述

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 '.' 表示。

27c281479d0022f3e36d2fd845065c41.png

一个数独。

5e5db6c7813e045a23de597167db9e15.png

答案被标成红色。

Note:

  • 给定的数独序列只包含数字 1-9 和字符 '.' 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

思路:

这一题是LeetCode上面的第37题,确实是挺恶心的,当时做了两三天。但如果你把它解答出来了,我觉得你对应回溯法的理解应该是差不多了。下面我还是直接贴代码了,相关注解我都在代码里写的很清楚了,大家应该很容易看得懂!

代码如下:

import numpy as npclass Solution(object):# 本题采用回溯法解决# 当时在area3x3检查时栽了跟头def solveSudoku(self, board):""":type board: List[List[str]]:rtype: void Do not return anything, modify board in-place instead."""# board = np.array(board)def back(board, position=[0, 0]):# 如果当前数独中没有空白元素'.',则说明查找成功了if position == [-1, -1]:print("successful")return True# 获取当前位置的横纵坐标pos_x = position[0]pos_y = position[1]# 获取当前位置的值pos_value = board[pos_x][pos_y]if pos_value == '.':for index in range(1, 10):value = str(index)if self.isValid(board, position, value) is True:board[pos_x][pos_y] = valuenext_position = self.getNextPosition(board, position)if back(board, next_position) is True:return Trueelse:board[pos_x][pos_y] = '.'else:next_pos = self.getNextPosition(board, position)back(board, next_pos)return Falseback(board)return board# 获取下一个有效点的坐标位置def getNextPosition(self, board, position):next_x = position[0]next_y = position[1]while board[next_x][next_y] != '.':next_y += 1if next_y >= len(board):next_x += 1next_y = 0if next_x not in range(len(board)) or next_y not in range(len(board)):return [-1, -1]return [next_x, next_y]# 判断当前位置是否有效def isValid(self, board, position, value):""":param board: array[[]]-->指代所给的数独列表:param position: List[int x, y]-->指代所给的当前位置:param value: str-->指代当前位置的值:return: boolean-->若返回为True,则表示当前位置有效;反之,则无效"""board = np.array(board)# 获取当前位置的横纵坐标pos_x = position[0]pos_y = position[1]# 获取当前位置横纵坐标所对应的每一行每一列元素pos_row = board[pos_x]pos_col = board[:, pos_y]# 如果当前位置的值value与其所在的每一行或者每一列的值重复,则表示当前值无效,返回Falseif value in pos_row or value in pos_col:return False# 获取当前位置点所在的3x3区域的位置area3x3_x = pos_x//3*3area3x3_y = pos_y//3*3area3x3_batch = board[area3x3_x:area3x3_x+3, area3x3_y:area3x3_y+3]# 如果当前位置的值value与其所在的3x3区域的值重复,则表示当前值无效,返回Falseif value in area3x3_batch:return Falsereturn Trueif __name__ == "__main__":board = [['5', '3', '.', '.', '7', '.', '.', '.', '.'],['6', '.', '.', '1', '9', '5', '.', '.', '.'],['.', '9', '8', '.', '.', '.', '.', '6', '.'],['8', '.', '.', '.', '6', '.', '.', '.', '3'],['4', '.', '.', '8', '.', '3', '.', '.', '1'],['7', '.', '.', '.', '2', '.', '.', '.', '6'],['.', '6', '.', '.', '.', '.', '2', '8', '.'],['.', '.', '.', '4', '1', '9', '.', '.', '5'],['.', '.', '.', '.', '8', '.', '.', '7', '9']]result = Solution().solveSudoku(board)print(np.array(result))

暂时就补充这么多了,如果有更好的想法,我也会及时补充的,当然了,各位读者如果有更nice的解题套路也希望大家积极分享!!!


2019/3/19 19:13补充

昨天有位朋友给我出了道题,也是涉及回溯法的,我觉得很有意思,所以想分享出来。

题目如下:

bb578618ad258bebc666d7ebf1437049.png

思路:

因为这个方格是不规则的方格,所以我首先想到的是将它填充成一个规则方格,便于我们插值。 如图所示:

7e32670c9f4af36555ce4697698d4dfd.png

之所以这样,是因为我们就可以直接对每个方格内的元素的相邻方格内元素作比较了。

如图所示:

1b44f469245ae94da57fd251ef76369e.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/245650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言 把最大值放入max 把最小值放入min_Excel求最大值地球人都知道,那要求出第2、第3、第N大值呢?...

只要稍稍懂点excel的人,都会轻松地求出最大值、最小值。那要求出第2大值、第3大值……第N大值呢?往下看,掌握了方法,排序又变得很简单了。最大最小值最大值:max(B:B),最小值:min(B:B)其中B:B为最…

python3 for_python3 for循环-range遍历

for循环、range遍历 for循环range遍历 range(1,10) 注:是1-9,而不包括最后的10。实例: 九九乘法表: # -*- coding: UTF-8 -*- # 乘法表 for i in range(1,10): for j in range(1,10): print(‘{}*{}{}‘.fo…

php撒意思,php表示是什么意思

php表示超文本预处理器,它是一种通用的开源脚本语言。php是常用的网站编程语言,有着开源性、免费性、快捷性、数据库连接的广泛性、面向过程和面向对象并用等特点。php介绍:PHP即“超文本预处理器”,是一种通用开源脚本语言。PHP是…

python暂停和恢复_python-线程的暂停, 恢复, 退出

我们都知道python中可以是threading模块实现多线程, 但是模块并没有提供暂停, 恢复和停止线程的方法, 一旦线程对象调用start方法后, 只能等到对应的方法函数运行完毕. 也就是说一旦start后, 线程就属于失控状态. 不过, 我们可以自己实现这些. 一般的方法就是循环地判断一个标志…

织梦直接写php标签,怎么在自己的php页面中使用dedecms标签

怎么在自己的php页面中使用dedecms标签?这篇文章主要介绍了在自己的php页面中使用dedecms标签的代码示例,需要的朋友可以参考下推荐学习:织梦cms第一步:外部php页面中加入如下代码:require_once (dirname(__FILE__)./../include/c…

xshell搭建宝塔没有远程命令密码框框弹出来_服务器安装宝塔控制面板+wordpress搭建个人网站...

准备工作服务器一台:服务器购买域名一个:随便买一个就行软件环境:宝塔面板第1步:SSH远程连接服务器通过ssh远程连接工具进行服务器主机连接(Xshell、Putty等,百度下载)需要更加详细的关于ssh远程…

python类有什么用_python 定制类 有什么用

展开全部 C.__init__(self[, arg1, ...]) 构造2113器(带一5261些可选的参数) C.__new__(self[, arg1, ...]) 构造器(带一些可选的参数);通常用在设置不变数4102据类型的子类。 C.__del__(self) 解构1653器 C.__str__(self) 可打印的字符输出;内建 str()及 print 语句 C.__repr_…

php列目录设置密码,PHP输入密码并列出目录文件生成超链接代码

一个可以加密文件夹,并且可以展示文件夹内文件的php代码:$password "123456"; // 这里是密码$p "";if(isset($_COOKIE["isview"]) and $_COOKIE["isview"] $password){$isview true;}else{if(isset($_POST[…

php8vsgo,vscode编辑好go语言代码要怎么运行

vscode运行go语言代码需要安装vscode-go插件。然后使用F5(continue)、F10(step over)、F11(step into)等快捷键运行go语言代码。安装vscode-go 插件进入vscode界面,打开命令面板Ctrl Shift P,输入install,插件特性包括:Completion Lists (using gocode)Signature Help (using…

python送心小人_使用Python画出小人发射爱心的代码

我就废话不多说了,直接上代码吧! #2.14 from turtle import * from time import sleep def go_to(x, y): up() goto(x, y) down() def head(x,y,r): go_to(x,y) speed(1) circle(r) leg(x,y) def leg(x,y): right(90) forward(180) right(30) forward(10…

c 函数多次声明_C++核心准则C.132:不要没有理由就将函数声明为虚函数

岫玉C.132: Dont make a function virtual without reasonC.132:不要没有理由就将函数声明为虚函数Reason(原因)Redundant virtual increases run-time and object-code size. A virtual function can be overridden and is thus open to mistakes in a derived class. A virtu…

anaconda3卸载python_机器学习Python编程环境:VSCode+Anaconda

机器学习Python编程环境(Windows):VSCodeAnaconda安装顺序:Anaconda ->VSCode (不必下载Python)->机器学习常用Python包为什么选择VSCode ?Anaconda pycharm是大家都熟悉的Python编程环境,但是pycharm比较笨重&…

bat窗口大小设置_8-Flink中的窗口

戳原文:1-Flink入门2-本地环境搭建&构建第一个Flink应用3-DataSet API 4-DataSteam API5-集群部署6-分布式缓存7-重启策略8-Flink中的窗口9-Flink中的Time窗口窗口类型flink支持两种划分窗口的方式(time和count) 如果根据时间划分窗口&am…

php中pandans,Python地信专题 | 基于geopandas的空间数据分析-文件IO篇

本文对应代码和数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes1 简介在上一篇文章中我们对geopandas中的坐标参考系有了较为深入的学习,而在日常空间数据分析工作中矢量文件的读入和写出,是至关重要的环节。作为基于geo…

ros创建工作空间_ROS入门学习之七Moveit机械臂控制

1.MoveIt!系统架构MoveIt是什么:一个易于使用的集成化开发平台由一系列移动操作的功能包组成:运动规划、操作控制、3D感知、运动学、控制与导航算法提供友好的GUI可应用于工业、商业、研发和其他领域ROS社区中使用度排名前三的功能包系统架构用户接口(Us…

常用于评价回归模型优劣的统计量包括( )。_第四十一讲 R-判断回归模型性能的指标...

当回归模型建立好以后,如何评价该回归模型是否与另一个回归模型有区别,如何比较两个回归模型的性能?这一讲中,我们将给大家介绍几个评价回归模型性能的统计指标。1. 模型性能指标在回归模型中,最常用的评估指标包括&am…

python 常用包_Python常用指引

Python常用指引Python常用指引的形式来源于Linux文档项目的常用指引章节,是一系列独立、指定主题并尝试完全覆盖该主题的文章集合。致力于提供比Python库参考帮助更详尽的文档。Python 3 是 Python 的未来,但 Python 2 仍处于活跃使用阶段,最…

读取当前linux进程内存_(笔记)Linux上的内存分配

作者: LemonNan原文: https://juejin.im/post/5ee3c34a518825430c3ad31d前言本篇是对Linux内存分配的一个学习笔记.程序内存结构下面是在 Linux/x86-32 中典型的一个进程内存结构文本段包含了进程运行的程序机器语言指令. 文本段具有只读属性, 以防止进程通过勘误指针意外修改自…

php改成IP连接数据库,thinkphp,pdo连接数据库,host自动被替换成了本机ip

class DBAccess extends PDO{ private $charset; // 数据库字符集public $cacheDir_cache_$98sdf29fw!d#s4fef/; public $prename; public $time; function __construct($dsn, $user, $password){ try{ parent::__construct($dsn, $user, $pa…

django开源电子文档管理系统_「开源推荐」BookStack v2.8 发布,简洁美观的在线文档管理系统

程序介绍BookStack,基于 Mindoc、使用Go语言的Beego框架开发的功能类似GitBook和看云的在线文档管理系统,拥有简洁美观的页面布局,实现了文档采集、导入、电子书生成以及版本控制等强大的文档功能,并推出了配套的开源微信小程序 B…