From 21d8f23b165104a8937874ebf93edf56d315cab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Mon, 3 Sep 2018 23:28:57 +0800 Subject: [PATCH 01/35] Set theme jekyll-theme-minimal --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2f7efbe --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file From ee4c036a45506e60f885ee20b3e442908fd5cee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Mon, 3 Sep 2018 23:30:23 +0800 Subject: [PATCH 02/35] Set theme jekyll-theme-tactile --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 2f7efbe..259a24e 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-minimal \ No newline at end of file +theme: jekyll-theme-tactile \ No newline at end of file From 8bdec3b63886cd5159a6c03bbf665a53bd5f5dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Mon, 3 Sep 2018 23:30:45 +0800 Subject: [PATCH 03/35] Set theme jekyll-theme-architect --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 259a24e..3397c9a 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-tactile \ No newline at end of file +theme: jekyll-theme-architect \ No newline at end of file From 6ffa9e7ef84368fd1b0ee5e2bd9962ad6e1f3598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Mon, 3 Sep 2018 23:34:18 +0800 Subject: [PATCH 04/35] Set theme jekyll-theme-tactile --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 3397c9a..259a24e 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-architect \ No newline at end of file +theme: jekyll-theme-tactile \ No newline at end of file From f365c2e508a55b2e59186c153d8cd60179b94d74 Mon Sep 17 00:00:00 2001 From: xianyunyh Date: Mon, 1 Oct 2018 13:33:20 +0800 Subject: [PATCH 05/35] edit readme --- README.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb5af24..d6b62fa 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,10 @@ 这个项目是自己准备面试整理的资料。可能包括PHP、MySQL等资料。方便自己以后查阅,会不定期更新,如果错误,请指出,谢谢。欢迎大家提交PR,谢谢大家的star -有童鞋提议整理成gitbook的版本的。于是我又开了一个gitbook的分支。来完善这个。目前还没整理完成。 - 可以通过[https://xianyunyh.gitbooks.io/php-interview/](https://xianyunyh.gitbooks.io/php-interview/)预览。欢迎有精力的朋友完善一下。谢谢。 -[GITbooK分支](https://github.com/xianyunyh/PHP-Interview/tree/gitbook) + +### 目录 - [Linux](Linux/REAMDE.md) @@ -101,8 +100,18 @@ - [面试](面试/README.md) - + + +## 生成自己的Gitbook + +```bash +$ npm install gitbook-cli -g +$ git clone https://github.com/xianyunyh/PHP-Interview +$ cd PHP-Interview +$ gitbook serve # 本地预览 +$ gitbook build # 生成静态的html +``` ##推荐阅读资料 - [PHP函数库](http://overapi.com/php) @@ -122,4 +131,4 @@ 如果这个系列的文章,对您有所帮助,您可以选择打赏一下作者。谢谢! -![](mm_reward_qrcode.jpg) \ No newline at end of file +![qrcode](mm_reward_qrcode.jpg) \ No newline at end of file From d3c1a57001094dbeca362aa70104145c95a80b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Sun, 18 Nov 2018 19:07:34 +0800 Subject: [PATCH 06/35] =?UTF-8?q?Update=20=E7=AC=94=E8=AF=95=E9=A2=984.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\347\254\224\350\257\225\351\242\2304.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" index c945417..b2237af 100644 --- "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\2304.md" @@ -10,7 +10,7 @@ 3. `$a = 3; echo "$a",'$a',"\\\$a","$a"."$a","$a"+"$a"` - 33$a\$a336 + 3$a\$a336 4. require include区别 @@ -65,4 +65,4 @@ 19. 项目中遇到了什么问题, 20. 之前的项目有没有其他的优化方案? 21. 平时怎么学习的? -22. 未来的职业的职业方向? \ No newline at end of file +22. 未来的职业的职业方向? From 4e3d3124c5e1611347cb706942a1a3987fcfaf1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Sun, 18 Nov 2018 19:09:14 +0800 Subject: [PATCH 07/35] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcrontale=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Linux/crontab.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Linux/crontab.md b/Linux/crontab.md index b79a303..e0f7273 100644 --- a/Linux/crontab.md +++ b/Linux/crontab.md @@ -19,7 +19,7 @@ ```shell * * * * * echo "hello" #每1分钟执行hello -3,15 * * * * myCommand #每小时第三分钟和第五分钟执行 +3,15 * * * * myCommand #每小时第三分钟和第十五分钟执行 3,15 8-11 * * * myCommand# 在上午8点到11点的第3和第15分钟执行 3,15 8-11 */2 * * myCommand #每隔两天的上午8点到11点的第3和第15分钟执行 30 21 * * * /etc/init.d/smb restart #每晚的21:30重启smb @@ -34,4 +34,4 @@ ``` $service cron restart -``` \ No newline at end of file +``` From 04dc8b10ab165a67810783cad7fa607cbeb16beb Mon Sep 17 00:00:00 2001 From: Kael Li Date: Sat, 29 Dec 2018 14:40:15 +0800 Subject: [PATCH 08/35] =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E5=8A=A0=E6=A0=BC=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kael Li --- "Linux/LinuxIO\346\250\241\345\236\213.md" | 32 +-- "Linux/Linux\345\221\275\344\273\244.md" | 6 +- "Mysql/MySQL\344\274\230\345\214\226.md" | 3 +- ...45\350\257\242\344\274\230\345\214\226.md" | 200 ++++++++++-------- 4 files changed, 127 insertions(+), 114 deletions(-) diff --git "a/Linux/LinuxIO\346\250\241\345\236\213.md" "b/Linux/LinuxIO\346\250\241\345\236\213.md" index 17b3c0d..c1d34d8 100644 --- "a/Linux/LinuxIO\346\250\241\345\236\213.md" +++ "b/Linux/LinuxIO\346\250\241\345\236\213.md" @@ -6,7 +6,7 @@ ### 2. 进程切换 -为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的 +为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的 从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化: @@ -17,23 +17,23 @@ > 5. 更新内存管理的数据结构。 > 6. 恢复处理机上下文。 - + ### 3. 进程的阻塞 -> 正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。`当进程进入阻塞状态,是不占用CPU资源的`。 +> 正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。`当进程进入阻塞状态,是不占用CPU资源的`。 ### 4. 进程缓存区、内核缓冲区 缓冲区的出现是为了减少频繁的系统调用,由于系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要回复之前的信息,为了减少这种耗时耗性能的调用于是出现了缓冲区。在linux系统中,每个进程有自己独立的缓冲区,叫做**进程缓冲区**,而系统内核也有个缓冲区叫做**内核缓冲区**。 - **操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区 复制到内核缓冲区中** + **操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中** ### 5. 文件描述符fd 文件描述符(File descriptor)是计算机科学中的一个术语,`是一个用于表述指向文件的引用的抽象化概念`。 文件描述符在形式上是一个非负整数。实际上,`它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表`。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开 -## Linx/Unix 5种IO模型 +## Linx/Unix 5种IO模型 当一个io发生时候的,涉及到的步骤和对象 @@ -46,12 +46,12 @@ 经历的步骤 -- 等待数据准备,比如accept(), recv()等待数据 -- 将数据从内核拷贝到进程中, 比如 accept()接受到请求,recv()接收连接发送的数据后需要复制到内核,再从内核复制到进程**用户空间** +- 等待数据准备,比如accept(), recv()等待数据 +- 将数据从内核拷贝到进程中, 比如 accept()接受到请求,recv()接收连接发送的数据后需要复制到内核,再从内核复制到进程**用户空间** ### 阻塞IO -![img](https://upload-images.jianshu.io/upload_images/1446087-9522cafa9e14abd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/552) +![img](https://upload-images.jianshu.io/upload_images/1446087-9522cafa9e14abd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/552) > 当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到**操作系统内核的缓冲区**中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会**将数据从kernel中拷贝到用户内存**,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。 @@ -59,7 +59,7 @@ ### 非阻塞IO -![img](https://upload-images.jianshu.io/upload_images/1446087-0c604ff4a2d8dc5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/603) +![img](https://upload-images.jianshu.io/upload_images/1446087-0c604ff4a2d8dc5d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/603) 当用户进程发出read操作时,如果kernel中的数据还没有准备好,**那么它并不会block用户进程,而是立刻返回一个error**。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回 @@ -67,27 +67,27 @@ IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。 -![img](https://upload-images.jianshu.io/upload_images/1446087-3b0399b077daf0a8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/609) +![img](https://upload-images.jianshu.io/upload_images/1446087-3b0399b077daf0a8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/609) -在一个调用中阻塞`select`,等待数据报套接字可读。当`select` 返回套接字可读时,我们然后调用`recvfrom` 将数据报复制到我们的应用程序缓冲区中 .使用`select`需要两次系统调用而不是一次 +在一个调用中阻塞`select`,等待数据报套接字可读。当`select` 返回套接字可读时,我们然后调用`recvfrom` 将数据报复制到我们的应用程序缓冲区中 .使用`select`需要两次系统调用而不是一次 在IO multiplexing Model中,实际中,**对于每一个socket,一般都设置成为non-blocking,因为只有设置成non-blocking 才能使单个线程/进程不被阻塞(或者说锁住),可以继续处理其他socket。如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。** ### 异步 I/O -![img](https://upload-images.jianshu.io/upload_images/1446087-e707464b299104e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/572) +![img](https://upload-images.jianshu.io/upload_images/1446087-e707464b299104e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/572) 用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了 ### 异步、同步、阻塞、非阻塞 -同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列 +同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列 -异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了 +异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了 -阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务 +阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务 -非阻塞调用指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回 +非阻塞调用指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回 异步、同步是发生在用户空间内,当用户发起一个IO的调用的时候,同步的时候,如果这个操作比较耗时间,会阻塞后面的流程 diff --git "a/Linux/Linux\345\221\275\344\273\244.md" "b/Linux/Linux\345\221\275\344\273\244.md" index e26973b..c59a44a 100644 --- "a/Linux/Linux\345\221\275\344\273\244.md" +++ "b/Linux/Linux\345\221\275\344\273\244.md" @@ -134,7 +134,7 @@ sed '/^$/d' file #删除空白行 详细教程可以查看 http://awk.readthedocs.io/en/latest/chapter-one.html ```shell -awk ' BEGIN{ statements } statements2 END{ statements } ' +awk ' BEGIN{ statements } statements2 END{ statements } ' ``` 工作流程 @@ -161,11 +161,11 @@ $2:第二个字段的文本内容; awk '{print $2, $3}' file # 日志格式:'$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"' #统计日志中访问最多的10个IP -awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log +awk '{a[$1]++}END{for(i in a)print a[i],i|"sort -k1 -nr|head -n10"}' access.log ``` -### 排序 port +### 排序 sort - -n 按数字进行排序 VS -d 按字典序进行排序 - -r 逆序排序 diff --git "a/Mysql/MySQL\344\274\230\345\214\226.md" "b/Mysql/MySQL\344\274\230\345\214\226.md" index bcc8aa4..4531e19 100644 --- "a/Mysql/MySQL\344\274\230\345\214\226.md" +++ "b/Mysql/MySQL\344\274\230\345\214\226.md" @@ -34,9 +34,8 @@ select * from user where name like '%a' - 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描 -- 很多时候用 exists 代替 in 是一个好的选择: +- 很多时候用 exists 代替 in 是一个好的选择 -- ## btree索引 diff --git "a/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" "b/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" index 46caa30..2fcb85e 100644 --- "a/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" +++ "b/Mysql/MySQL\347\264\242\345\274\225\345\216\237\347\220\206\345\217\212\346\205\242\346\237\245\350\257\242\344\274\230\345\214\226.md" @@ -4,14 +4,14 @@ MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为 ```sql select - count(*) + count(*) from - task + task where - status=2 - and operator_id=20839 - and operate_time>1371169729 - and operate_time<1371174603 + status=2 + and operator_id=20839 + and operate_time>1371169729 + and operate_time<1371174603 and type=2; ``` @@ -42,10 +42,10 @@ various-system-software-hardware-latencies ### 详解b+树 -![b+树](https://tech.meituan.com/img/mysql_index/btree.jpg) +![b+树](https://tech.meituan.com/img/mysql_index/btree.jpg) b+树 -如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。 +如上图,是一颗b+树,关于b+树的定义可以参见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。 ### b+树的查找过程 @@ -82,12 +82,12 @@ select count(*) from task where status = 0 ; ### 慢查询优化基本步骤 0. 先运行看看是否真的很慢,注意设置SQL_NO_CACHE -1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的 where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 -1. explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) -2. order by limit 形式的sql语句让排序的表优先查 -3. 了解业务方使用场景 -4. 加索引时参照建索引的几大原则 -5. 观察结果,不符合预期继续从0分析 +1. where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的 where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高 +2. explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询) +3. order by limit 形式的sql语句让排序的表优先查 +4. 了解业务方使用场景 +5. 加索引时参照建索引的几大原则 +6. 观察结果,不符合预期继续从0分析 几个慢查询案例 下面几个例子详细解释了如何分析和优化慢查询 @@ -97,32 +97,32 @@ select count(*) from task where status = 0 ; ```sql select - distinct cert.emp_id + distinct cert.emp_id from - cm_log cl + cm_log cl inner join ( select emp.id as emp_id, - emp_cert.id as cert_id + emp_cert.id as cert_id from - employee emp + employee emp left join - emp_certificate emp_cert - on emp.id = emp_cert.emp_id + emp_certificate emp_cert + on emp.id = emp_cert.emp_id where emp.is_deleted=0 - ) cert + ) cert on ( - cl.ref_table='Employee' + cl.ref_table='Employee' and cl.ref_oid= cert.emp_id - ) + ) or ( - cl.ref_table='EmpCertificate' + cl.ref_table='EmpCertificate' and cl.ref_oid= cert.cert_id - ) + ) where - cl.last_upd_date >='2013-11-07 15:03:00' + cl.last_upd_date >='2013-11-07 15:03:00' and cl.last_upd_date<='2013-11-08 16:00:00'; ``` @@ -131,8 +131,9 @@ where 先运行一下,53条记录 1.87秒,又没有用聚合语句,比较慢 53 rows in set (1.87 sec) -1.explain +1.explain +```sql +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ @@ -141,42 +142,43 @@ where | 2 | DERIVED | emp | ALL | NULL | NULL | NULL | NULL | 13317 | Using where | | 2 | DERIVED | emp_cert | ref | emp_certificate_empid | emp_certificate_empid | 4 | meituanorg.emp.id | 1 | Using index | +----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+ +``` 简述一下执行计划,首先mysql根据idx_last_upd_date索引扫描cm_log表获得379条记录;然后查表扫描了63727条记录,分为两部分,derived表示构造表,也就是不存在的表,可以简单理解成是一个语句形成的结果集,后面的数字表示语句的ID。derived2表示的是ID = 2的查询构造了虚拟表,并且返回了63727条记录。我们再来看看ID = 2的语句究竟做了写什么返回了这么大量的数据,首先全表扫描employee表13317条记录,然后根据索引emp_certificate_empid关联emp_certificate表,rows = 1表示,每个关联都只锁定了一条记录,效率比较高。获得后,再和cm_log的379条记录根据规则关联。从执行过程上可以看出返回了太多的数据,返回的数据绝大部分cm_log都用不到,因为cm_log只锁定了379条记录。 如何优化呢?可以看到我们在运行完后还是要和cm_log做join,那么我们能不能之前和cm_log做join呢?仔细分析语句不难发现,其基本思想是如果cm_log的ref_table是EmpCertificate就关联emp_certificate表,如果ref_table是Employee就关联employee表,我们完全可以拆成两部分,并用union连接起来,注意这里用union,而不用union all是因为原语句有“distinct”来得到唯一的记录,而union恰好具备了这种功能。如果原语句中没有distinct不需要去重,我们就可以直接使用union all了,因为使用union需要去重的动作,会影响SQL性能。 优化过的语句如下 ```sql select - emp.id + emp.id from - cm_log cl + cm_log cl inner join - employee emp - on cl.ref_table = 'Employee' - and cl.ref_oid = emp.id + employee emp + on cl.ref_table = 'Employee' + and cl.ref_oid = emp.id where - cl.last_upd_date >='2013-11-07 15:03:00' - and cl.last_upd_date<='2013-11-08 16:00:00' - and emp.is_deleted = 0 + cl.last_upd_date >='2013-11-07 15:03:00' + and cl.last_upd_date<='2013-11-08 16:00:00' + and emp.is_deleted = 0 union select - emp.id + emp.id from - cm_log cl + cm_log cl inner join - emp_certificate ec - on cl.ref_table = 'EmpCertificate' - and cl.ref_oid = ec.id + emp_certificate ec + on cl.ref_table = 'EmpCertificate' + and cl.ref_oid = ec.id inner join - employee emp - on emp.id = ec.emp_id + employee emp + on emp.id = ec.emp_id where - cl.last_upd_date >='2013-11-07 15:03:00' - and cl.last_upd_date<='2013-11-08 16:00:00' + cl.last_upd_date >='2013-11-07 15:03:00' + and cl.last_upd_date<='2013-11-08 16:00:00' and emp.is_deleted = 0  ``` -不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致 +4.不需要了解业务场景,只需要改造的语句和改造之前的语句保持结果一致 5.现有索引可以满足,不需要建索引 @@ -188,12 +190,12 @@ where ```sql select from -stage_poi sp +stage_poi sp where -sp.accurate_result=1 +sp.accurate_result=1 and ( - sp.sync_status=0 - or sp.sync_status=2 + sp.sync_status=0 + or sp.sync_status=2 or sp.sync_status=4 ); @@ -202,6 +204,7 @@ and ( 0.先看看运行多长时间,951条数据6.22秒,真的很慢 951 rows in set (6.22 sec) + 1.先explain,rows达到了361万,type = ALL表明是全表扫描 2.所有字段都应用查询返回记录数,因为是单表查询 0已经做过了951条 @@ -210,6 +213,7 @@ and ( 看一下accurate_result = 1的记录数 +```sql select count(*),accurate_result from stage_poi group by accurate_result; +----------+-----------------+ | count(*) | accurate_result | @@ -218,12 +222,14 @@ select count(*),accurate_result from stage_poi group by accurate_result; | 2114655 | 0 | | 972815 | 1 | +----------+-----------------+ +``` 我们看到accurate_result这个字段的区分度非常低,整个表只有-1,0,1三个值,加上索引也无法锁定特别少量的数据 再看一下sync_status字段的情况 +```sql select count(*),sync_status from stage_poi group by sync_status; +----------+-------------+ | count(*) | sync_status | @@ -231,6 +237,7 @@ select count(*),sync_status from stage_poi group by sync_status; | 3080 | 0 | | 3085413 | 3 | +----------+-------------+ +``` 同样的区分度也很低,根据理论,也不适合建立索引 @@ -241,15 +248,17 @@ select count(*),sync_status from stage_poi group by sync_status; 5.根据建立索引规则,使用如下语句建立索引 -alter table stage_poi add index idx_acc_status(accurate_result,sync_status); +```alter table stage_poi add index idx_acc_status(accurate_result,sync_status);``` 6.观察预期结果,发现只需要200ms,快了30多倍。 952 rows in set (0.20 sec) + 我们再来回顾一下分析问题的过程,单表查询相对来说比较好优化,大部分时候只需要把where条件里面的字段依照规则加上索引就好,如果只是这种“无脑”优化的话,显然一些区分度非常低的列,不应该加索引的列也会被加上索引,这样会对插入、更新性能造成严重的影响,同时也有可能影响其它的查询语句。所以我们第4步调差SQL的使用场景非常关键,我们只有知道这个业务场景,才能更好地辅助我们更好的分析和优化查询语句。 无法优化的语句 +```sql select c.id, c.name, @@ -266,37 +275,37 @@ select c.data_source, from_unixtime(c.created_time) as created_time, from_unixtime(c.last_modified) as last_modified, - c.last_modified_user_id + c.last_modified_user_id from - contact c + contact c inner join - contact_branch cb - on c.id = cb.contact_id + contact_branch cb + on c.id = cb.contact_id inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 10802 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 10802 + and oei.org_category = - 1 order by c.created_time desc limit 0 , 10; - +``` 还是几个步骤 0.先看语句运行多长时间,10条记录用了13秒,已经不可忍受 10 rows in set (13.06 sec) + 1.explain ```sql - +----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+-------------------------------------+-------------------------+---------+--------------------------+------+----------------------------------------------+ @@ -314,22 +323,22 @@ rows返回的都非常少,看不到有什么异常情况。我们在看一下 select count(*) from - contact c + contact c inner join - contact_branch cb - on c.id = cb.contact_id + contact_branch cb + on c.id = cb.contact_id inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 10802 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 10802 + and oei.org_category = - 1 +----------+ | count(*) | +----------+ @@ -390,6 +399,7 @@ c.created_time desc limit 0 , ``` 10 rows in set (0.00 sec) + 本以为至此大工告成,但我们在前面的分析中漏了一个细节,先排序再join和先join再排序理论上开销是一样的,为何提升这么多是因为有一个limit!大致执行过程是:mysql先按索引排序得到前10条记录,然后再去join过滤,当发现不够10条的时候,再次去10条,再次join,这显然在内层join过滤的数据非常多的时候,将是灾难的,极端情况,内层一条数据都找不到,mysql还傻乎乎的每次取10条,几乎遍历了这个数据表! 用不同参数的SQL试验下 @@ -410,45 +420,49 @@ select c.data_source, from_unixtime(c.created_time) as created_time, from_unixtime(c.last_modified) as last_modified, - c.last_modified_user_id + c.last_modified_user_id from - contact c + contact c where exists ( select - 1 + 1 from - contact_branch cb + contact_branch cb inner join - branch_user bu - on cb.branch_id = bu.branch_id + branch_user bu + on cb.branch_id = bu.branch_id and bu.status in ( 1, - 2) + 2) inner join - org_emp_info oei - on oei.data_id = bu.user_id - and oei.node_left >= 2875 - and oei.node_right <= 2875 - and oei.org_category = - 1 + org_emp_info oei + on oei.data_id = bu.user_id + and oei.node_left >= 2875 + and oei.node_right <= 2875 + and oei.org_category = - 1 where - c.id = cb.contact_id - ) + c.id = cb.contact_id + ) order by c.created_time desc limit 0 , 10; Empty set (2 min 18.99 sec) ``` -2 min 18.99 sec!比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。 +2 min 18.99 sec! + +比之前的情况还糟糕很多。由于mysql的nested loop机制,遇到这种情况,基本是无法优化的。这条语句最终也只能交给应用系统去优化自己的逻辑了。 通过这个例子我们可以看到,并不是所有语句都能优化,而往往我们优化时,由于SQL用例回归时落掉一些极端情况,会造成比原来还严重的后果。所以,第一:不要指望所有语句都能通过SQL优化,第二:不要过于自信,只针对具体case来优化,而忽略了更复杂的情况。 慢查询的案例就分析到这儿,以上只是一些比较典型的案例。我们在优化过程中遇到过超过1000行,涉及到16个表join的“垃圾SQL”,也遇到过线上线下数据库差异导致应用直接被慢查询拖死,也遇到过varchar等值比较没有写单引号,还遇到过笛卡尔积查询直接把从库搞死。再多的案例其实也只是一些经验的积累,如果我们熟悉查询优化器、索引的内部原理,那么分析这些案例就变得特别简单了。 -写在后面的话 +##写在后面的话 + 本文以一个慢查询案例引入了MySQL索引原理、优化慢查询的一些方法论;并针对遇到的典型案例做了详细的分析。其实做了这么长时间的语句优化后才发现,任何数据库层面的优化都抵不上应用系统的优化,同样是MySQL,可以用来支撑Google/FaceBook/Taobao应用,但可能连你的个人网站都撑不住。套用最近比较流行的话:“查询容易,优化不易,且写且珍惜!” -参考 -参考文献如下: +##参考文献如下 + 1.《高性能MySQL》 + 2.《数据结构与算法分析》 From c8c6d95df332636872980a94bdbdd7032face6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=B2=BD=E5=AD=90?= <862275679@qq.com> Date: Thu, 24 Jan 2019 15:12:21 +0800 Subject: [PATCH 09/35] isset(false)=true --- .../\347\254\224\350\257\225\351\242\230.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" index a2a792a..91caf33 100644 --- "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" @@ -217,7 +217,7 @@ function getPoint(Node $a,Node $b) { 17、**isset(null) isset(false) empty(null) empty(false)输出** -​ false,false,true,true +​ false,true,true,true,[参考](http://php.net/manual/zh/types.comparisons.php) 18、**优化MYSQL的方法** From 642dc13c3896ad0031082017e4e0bc34731df0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=B2=BD=E5=AD=90?= <862275679@qq.com> Date: Thu, 24 Jan 2019 16:33:12 +0800 Subject: [PATCH 10/35] =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\347\254\224\350\257\225\351\242\230.md" | 69 +++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" index 91caf33..ac44e8a 100644 --- "a/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" +++ "b/\351\235\242\350\257\225/\347\254\224\350\257\225\351\242\230.md" @@ -391,15 +391,76 @@ type 记录变量的类型。然后根据不同的类型,找到不同的value ![img](01.png) -38、有两个文件文件,大小都超过了1G,一行一条数据,每行数据不超过500字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP最大允许内内为255M。 +38、有两个文本文件,大小都超过了1G,一行一条数据,每行数据不超过500字节,两文件中有一部分内容是完全相同的,请写代码找到相同的行,并写到新文件中。PHP最大允许内内为255M。 + +```php +// 思路:使用协程yield +function readFieldFile($fileName) +{ + $fp = fopen($fileName, "rb"); + while (!feof($fp)) { + yield fgets($fp); + } + fclose($fp); +} + +$file1 = readFieldFile('big1.txt'); +$file2 = readFieldFile('big2.txt'); + +$file1->rewind(); +$file2->rewind(); +while ($file1->valid() && $file2->valid()) { + if ($file1->current() == $file2->current()) { + file_put_contents('big.txt', $file1->current(), FILE_APPEND); + } + $file1->next(); + $file2->next(); +} +``` 39、请写出自少两个支持回调处理的PHP函数,并自己实现一个支持回调的PHP函数 -preg_matacth_callback. call_user_func +preg_match_callback. call_user_func + +```php +function myCallBack(Closure $closure, $a, $b) +{ + return $closure($a, $b); +} + +myCallBack(function ($a, $b) { + return $a + $b; +}, 1, 2); + +``` -40、请写出自少两个获取指定文件夹下所有文件的方法(代码或思路)。 +40、请写出至少两个获取指定文件夹下所有文件的方法(代码或思路)。 + +```php +// 递归获取,排除.和..,除了文件夹就是文件 +function myScanDir($dir) +{ + $files = array(); + if (is_dir($dir)) { + if ($handle = opendir($dir)) { + while (($file = readdir($handle)) !== false) { + if ($file != "." && $file != "..") { + if (is_dir($dir . "/" . $file)) { + $files[$file] = myScanDir($dir . "/" . $file); + } else { + $files[] = $dir . "/" . $file; + } + } + } + closedir($handle); + return $files; + } + } +} + +``` -41、请写出自少三种截取文件名后缀的方法或函数(PHP原生函数和自己实现函数均可) +41、请写出至少三种截取文件名后缀的方法或函数(PHP原生函数和自己实现函数均可) basename expload() strpos From 6f966ba0d01959a115720720cb7b3f3c54705e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Fri, 25 Jan 2019 14:57:43 +0800 Subject: [PATCH 11/35] Create Git_removeCommits.md --- .../Git_removeCommits.md" | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 "\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" diff --git "a/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" new file mode 100644 index 0000000..a4abb5e --- /dev/null +++ "b/\347\211\210\346\234\254\346\216\247\345\210\266\345\231\250/Git_removeCommits.md" @@ -0,0 +1,100 @@ +在 Git 开发中通常会控制主干分支的质量,但有时还是会把错误的代码合入到远程主干。 虽然可以 直接回滚远程分支](https://harttle.land/2018/03/12/reset-origin-without-force-push.html), 但有时新的代码也已经合入,直接回滚后最近的提交都要重新操作。 那么有没有只移除某些 Commit 的方式呢?可以一次 [revert](https://git-scm.com/docs/git-revert) 操作来完成。 + +# 一个例子 + +考虑这个例子,我们提交了 6 个版本,其中 3-4 包含了错误的代码需要被回滚掉。 同时希望不影响到后续的 5-6。 + +``` +* 982d4f6 (HEAD -> master) version 6 +* 54cc9dc version 5 +* 551c408 version 4, harttle screwed it up again +* 7e345c9 version 3, harttle screwed it up +* f7742cd version 2 +* 6c4db3f version 1 +``` + +这种情况在团队协作的开发中会很常见:可能是流程或认为原因不小心合入了错误的代码, 也可能是合入一段时间后才发现存在问题。 总之已经存在后续提交,使得直接回滚不太现实。 + +下面的部分就开始介绍具体操作了,同时我们假设远程分支是受保护的(不允许 Force Push)。 思路是从产生一个新的 Commit 撤销之前的错误提交。 + +# git revert + +使用 `git revert ` 可以撤销指定的提交, 要撤销一串提交可以用 `..` 语法。 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。 + +``` +git revert --no-commit f7742cd..551c408 +git commit -a -m 'This reverts commit 7e345c9 and 551c408' +``` + +其中 `f7742cd` 是 version 2,`551c408` 是 version 4,这样被移除的是 version 3 和 version 4。 注意 revert 命令会对每个撤销的 commit 进行一次提交,`--no-commit` 后可以最后一起手动提交。 + +此时 Git 记录是这样的: + +``` +* 8fef80a (HEAD -> master) This reverts commit 7e345c9 and 551c408 +* 982d4f6 version 6 +* 54cc9dc version 5 +* 551c408 version 4, harttle screwed it up again +* 7e345c9 version 3, harttle screwed it up +* f7742cd version 2 +* 6c4db3f version 1 +``` + +现在的 HEAD(`8fef80a`)就是我们想要的版本,把它 Push 到远程即可。 + +# 确认 diff + +如果你像不确定是否符合预期,毕竟批量干掉了别人一堆 Commit,可以做一下 diff 来确认。 首先产生 version 4(`551c408`)与 version 6(`982d4f6`)的 diff,这些是我们想要保留的: + +``` +git diff 551c408..982d4f6 +``` + +然后再产生 version 2(`f7742cd`)与当前状态(HEAD)的 diff: + +``` +git diff f7742cd..HEAD +``` + +如果 version 3, version 4 都被 version 6 撤销的话,上述两个 diff 为空。 可以人工确认一下,或者 grep 掉 description 之后做一次 diff。 下面介绍的另一种方法可以容易地确认 diff。 + +# 另外一种方式 + +类似 [安全回滚远程分支](https://harttle.land/2018/03/12/reset-origin-without-force-push.html), 我们先回到 version 2,让它合并 version 4 同时代码不变, 再合并 version 5, version 6。 + +``` +# 从 version 2 切分支出来 +git checkout -b fixing f7742cd +# 合并 version 4,保持代码不变 +git merge -s ours 551c408 +# 合并 version 5, version 6 +git merge master +``` + +上述分支操作可以从 [分支管理](https://harttle.land/2016/09/02/git-workflow-branch.html) 一文了解。 至此,`fixing` 分支已经移除了 version 3 和 version 4 的代码,图示如下: + +``` +* 3cb9f8a (HEAD -> v2) Merge branch 'master' into v2 +|\ +| * 982d4f6 (master) version 6 +| * 54cc9dc version 5 +* | c669557 Merge commit '551c408' into v2 +|\ \ +| |/ +| * 551c408 version 4, harttle screwed it up again +| * 7e345c9 version 3, harttle screwed it up +|/ +* f7742cd version 2 +* 6c4db3f version 1 +``` + +可以简单 diff 一下来确认效果: + +``` +# 第一次 merge 结果与 version 2 的 diff,应为空 +git diff f7742cd..c669557 +# 第二次 merge 的内容,应包含 version 5 和 version 6 的改动 +git diff c669557..3cb9f8a +``` + +现在的 `HEAD`(即 `fixing` 分支)就是我们想要的版本,可以把它 Push 到远程了。 注意由于现在处于 `fixing` 分支, 需要 [Push 时指定远程分支](https://harttle.land/2016/09/05/git-workflow-remote.html) 为 `master`。 From fffb9f4e87f6fc801c687725d7097a21f32a5133 Mon Sep 17 00:00:00 2001 From: fymmx Date: Mon, 28 Jan 2019 18:10:09 +0800 Subject: [PATCH 12/35] Update php7.md --- PHP/php7.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/PHP/php7.md b/PHP/php7.md index 0eebc9c..12941c8 100644 --- a/PHP/php7.md +++ b/PHP/php7.md @@ -35,7 +35,7 @@ if(isset($_GET['a'])) { $a = isset($_GET['a']) ? $_GET['a'] : 'none'; #PHP 7 -$a = isset($_GET['a']) ?? 'none'; +$a = $_GET['a'] ?? 'none'; ``` @@ -50,7 +50,7 @@ function sumOfInts(int ...$ints) { return array_sum($ints); } -ar_dump(sumOfInts(2, '3', 4.1)); // int(9) +var_dump(sumOfInts(2, '3', 4.1)); // int(9) # 严格模式 declare(strict_types=1); @@ -352,10 +352,8 @@ function handler(Throwable $e) { ... } list 会按照原来的顺序进行赋值。不再是逆序了 ```php -list($a,$b,$c) = [1,2,3]; -var_dump($a);//1 -var_dump($b);//2 -var_dump($c);//3 +list($array[], $array[], $array[]) = [1, 2, 3]; +var_dump($array); // [1, 2, 3] ``` list不再支持解开字符串、 From afd48f544e43e20be09b796ba414729231b6910b Mon Sep 17 00:00:00 2001 From: xianyunyh Date: Mon, 18 Feb 2019 22:51:27 +0800 Subject: [PATCH 13/35] =?UTF-8?q?=E5=A2=9E=E5=8A=A0php7.3=E6=96=B0?= =?UTF-8?q?=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP/php7.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/PHP/php7.md b/PHP/php7.md index 12941c8..756cbd4 100644 --- a/PHP/php7.md +++ b/PHP/php7.md @@ -662,3 +662,111 @@ var_dump( ); ``` +## PHP 7.3 + +#### 1. 灵活的heredoc 和nowdoc + +在php 7.3 之前我们定义一大段的字符串。需要用到heredoc + +```php +'1','b'=>'2']; +#php 7.3之前 +$firstKey = key(reset($array)); +# php 7.3 +$firstKey = array_key_first($array);//a +$lastKey = array_key_last($array);//b +``` + +### 6.废除并移除大小写不敏感的常量 + +你可以同时使用大小写敏感和大小写不敏感的常量。但大小写不敏感的常量会在使用中造成一点麻烦。所以,为了解决这个问题,PHP 7.3 废弃了大小写不敏感的常量。 + +原先的情况是: + +- 类常量始终为「大小写敏感」。 +- 使用 `const` 关键字定义的全局常量始终为「大小写敏感」。注意此处仅仅是常量自身的名称,不包含命名空间名的部分,PHP 的命名空间始终为「大小写不敏感」。 +- 使用 `define()` 函数定义的常量默认为「大小写敏感」。 +- 使用 `define()` 函数并将第三个参数设为 `true` 定义的常量为「大小写不敏感」。 + +如今 PHP 7.3 提议废弃并移除以下用法: + +- In PHP 7.3: 废弃使用 `true` 作为 `define()` 的第三个参数。 +- In PHP 7.3: 废弃使用与定义时的大小写不一致的名称,访问大小写不敏感的常量。`true`、`false` 以及 `null` 除外。 \ No newline at end of file From de1c31c1a9d41cfd3f1853410bca3ab46bb3cab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Mon, 4 Mar 2019 21:01:42 +0800 Subject: [PATCH 14/35] Update README.md --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d6b62fa..456b7dd 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,6 @@ - [面试](面试/README.md) - - ## 生成自己的Gitbook ```bash @@ -112,8 +110,8 @@ $ cd PHP-Interview $ gitbook serve # 本地预览 $ gitbook build # 生成静态的html ``` -##推荐阅读资料 +## 推荐阅读资料 - [PHP函数库](http://overapi.com/php) - [PHP7内核剖析](https://github.com/pangudashu/php7-internal) - [php7-internal](https://github.com/laruence/php7-internal) @@ -127,8 +125,13 @@ $ gitbook build # 生成静态的html - [数据结构和算法](http://www.cnblogs.com/skywang12345/p/3603935.html) - [剑指offer-PHP实现](https://blog.csdn.net/column/details/15795.html) +## 致谢 + +- [OMGZui](https://github.com/OMGZui) +- [fymmx](https://github.com/fymmx) + 如果这个系列的文章,对您有所帮助,您可以选择打赏一下作者。谢谢! -![qrcode](mm_reward_qrcode.jpg) \ No newline at end of file +![qrcode](mm_reward_qrcode.jpg) From 710a603353829c2f732398406449c504e90b0b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Wed, 6 Mar 2019 22:54:07 +0800 Subject: [PATCH 15/35] =?UTF-8?q?Create=2005=E8=B0=88=E8=96=AA=E8=B5=84.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...05\350\260\210\350\226\252\350\265\204.md" | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 "\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" diff --git "a/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" "b/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" new file mode 100644 index 0000000..e6964a4 --- /dev/null +++ "b/\351\235\242\350\257\225/05\350\260\210\350\226\252\350\265\204.md" @@ -0,0 +1,27 @@ +谈薪资的时候,基本上是我们到了面试的最后的步骤了,这个时候,你的报价决定你以后的工作的工资的多少。也是很关键的一步,我之前面试的就是由于缺乏经验,导致自己报价低了。然后入职之后再往上加,就很难了。 + + + +在我们面试的时候,我们**尽量不要先出牌**,比如我们在填表格的时候,期望薪资, 可以写上面谈,或者写一个范围区间。不要写一个具体的数字,因为很多hr人员都会压低你的工资。比如你写的是20k,可能hr会给你18k。 + +- 你期望的薪资是多少? + +**了解该公司所在地区、所属行业、公司规模等信息**,你的薪水要求应该在该公司所在地 区、行业、公司规模相应的薪水范围之内。**尽可能提供一个你期望的薪水范围,而不是具体的薪金数。** 了解具体的福利情况。好的福利可以让你的薪资加上不少。比如有的公司是15薪,然后公司稍低点,有的工资高,但是福利少。你可以权衡一下。 + + + +- 你的上一份的薪资是多少? + + 问你这个问题的时候,是想知道和你当前薪资对比,比如你上一份是10k ,这次你报价20k,别人应该只会给你15k。所以上一份的薪资,应该和本次的报价,不是特别差别大。但是也不能一样。不然你跳槽就没不涨薪,上一份的薪资,你就应该把你的福利也加上。比如你有绩效奖金,薪资调整都加上,得到一个范围值,不要告诉具体的数值。比如你薪资15k,你加上乱七八糟的,你可以说15-18k之间。 + +- 你认为每年的加薪幅度是多少? + + 提示:通常, 比较可靠的回答是: 你希望收入的增长和生活水平的提高保持一致。你还应该提到, + + 你的业绩将是加薪的主要因素 + + **求职者** : 总体来说,取决于我个人的业绩和公司的业绩(盈利状况)。但一般而言,至少和生活水平的提高保持一致。 + + + +在面试过程中,我们做的准备与实际遇到的问题总会有一些出入。记得大致的原则,巧妙的随机应变。懂得自己在市场中的定位,将对薪资的确定更为有利。 From 43afcca4662001eb4e3170401304b9c06bd30f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=97=B2=E4=BA=91=E9=87=8E=E9=B9=A4?= Date: Sun, 7 Apr 2019 23:13:44 +0800 Subject: [PATCH 16/35] mq question --- MQ/question.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 MQ/question.md diff --git a/MQ/question.md b/MQ/question.md new file mode 100644 index 0000000..f9ed9ec --- /dev/null +++ b/MQ/question.md @@ -0,0 +1,25 @@ +1. 消息队列的作用 +- 流量消峰 + 并发量大的时间,所有的请求直接怼到数据库,造成数据库连接异常,将请求写进消息队列,后面的系统再从消息队列依次来取出。 +- 异步 + 一些非必要的业务逻辑以同步的方式运行,太耗费时间。改成异步,可以提高系统的响应时间。 +- 解耦 + 将消息写入消息队列,需要消息的系统自己从消息队列中订阅。从而使该系统不需要改代码。 +2. 如何保证消息队列高可用 +集群 +3. 如何保证消息不被重复消费 +那造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。 +消费前做检测,比如写库成功的时候,写入到redis中,再次消费的时候如果redis已存在,则不进行消费 +4. 如何保证消费的可靠性传输? +其实这个可靠性传输,每种MQ都要从三个角度来分析: + + - 生产者弄丢数据 + + 从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息 + - 消息队列弄丢数据 + + 处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用 + - 消费者弄丢数据 + + 消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rabbitMQ会立即将消息删除,这种情况下,如果消费者出现异常而未能处理消息,就会丢失该消息。 + 手动确认消息 From bdf07abc6b848c7c9d8b414f3e536b224f7a55db Mon Sep 17 00:00:00 2001 From: haolong Date: Thu, 11 Apr 2019 11:02:22 +0800 Subject: [PATCH 17/35] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E7=AE=97=E6=B3=95=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\345\255\227\347\254\246\344\270\262.md" | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000..3c54298 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,30 @@ +##旋转字符串 +给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。 +###解法一:暴力移位法 +```php +function move(&$str , $n){ + for ($i = 0; $i<$n; $i++){ + LeftShiftOne($str); + } +} + +function LeftShiftOne(&$str){ + $len = strlen($str); + $last = $str[$len - 1]; + for ($i = 0; $i<$len-1; $i++){ + $str[$i - 1] =$str[$i]; + } + $str[$len-2] = $last; +} +$str = "abcd"; +move($str, 4); + +``` +时间复杂度为O(m n),空间复杂度为O(1),空间复杂度符合题目要求,但时间复杂度不符合,所以,我们得需要寻找其他更好的办法来降低时间复杂度。 +###解法二:三步反转法 +对于这个问题,换一个角度思考一下。 +将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。 +例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可: +首先将原字符串分为两个部分,即X:abc,Y:def; +将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。 +反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。 \ No newline at end of file From a395ad84740f018cf400a5392283d00a5247c9cd Mon Sep 17 00:00:00 2001 From: haolong Date: Thu, 11 Apr 2019 11:47:01 +0800 Subject: [PATCH 18/35] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E7=AE=97=E6=B3=95=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\345\255\227\347\254\246\344\270\262.md" | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" index 3c54298..6040c2d 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\255\227\347\254\246\344\270\262.md" @@ -25,6 +25,41 @@ move($str, 4); 对于这个问题,换一个角度思考一下。 将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。 例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可: -首先将原字符串分为两个部分,即X:abc,Y:def; -将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。 -反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。 \ No newline at end of file +- 首先将原字符串分为两个部分,即X:abc,Y:def; +- 将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。 +- 反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。 +```php + +/** + * 旋转字符串 + * @param $str + * @param $start + * @param $end + */ +function ReverseString(&$str, $start, $end){ + while($start < $end){ + $t = $str[$start]; + $str[$start++] = $str[$end]; + $str[$end--] = $t; + } +} + + +/** + * @param $str + * @param $n 字符串长度 + * @param $m 移动位数 + */ +function LeftRotateString(&$str, $n, $m){ + $m %= $n; + ReverseString($str, 0, $m-1); + ReverseString($str, $m, $n-1); + ReverseString($str, 0, $n-1); + +} + +$str = "abcd"; +LeftRotateString($str, strlen($str), 1); +``` + +这就是把字符串分为两个部分,先各自反转再整体反转的方法,时间复杂度为O(n),空间复杂度为O(1),达到了题目的要求。 \ No newline at end of file From 32320e55125d215688fad11f6b1006c4c28a1766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E4=BA=B2=E5=BC=BA?= Date: Fri, 5 Jul 2019 17:41:32 +0800 Subject: [PATCH 19/35] leetcode-php --- ...30\347\233\256\351\233\206\345\220\210.md" | 620 ++++++++++++++++++ ...72\346\234\254\346\223\215\344\275\234.md" | 223 +++++++ .../\345\240\206\346\240\210.md" | 115 ++++ .../\346\225\243\345\210\227\350\241\250.md" | 111 ++++ ...14\345\210\206\346\237\245\346\211\276.md" | 156 +++++ ...50\346\200\201\350\247\204\345\210\222.md" | 259 ++++++++ 6 files changed, 1484 insertions(+) create mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" create mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" create mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" create mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" create mode 100644 "\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" create mode 100644 "\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" new file mode 100644 index 0000000..1cb0b19 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" @@ -0,0 +1,620 @@ +## :pencil2:Leetcode经典二叉树题目集合 + +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +**** +### :pencil2:1.二叉树的前序遍历(leetcode144) + + +​ + + +**前序遍历,先访问根结点,然后在访问左子树,最后访问右子树。可以利用栈的特点,这里我结合了队列和栈的特点来实现。先压入树,取出根节点。先把根节点值push到队列中,然后把右子树压入栈中,最后压入左子树。返回队列。当然你可以调整成你想要的实现方式。(只要前中后序顺序理解正确即可)** + +```php + +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ + +class Solution { + + /** + * @param TreeNode $root + * @return Integer[] + */ + function preorderTraversal($root) { + $res=[]; + $list=[]; + array_unshift($res,$root); + while(!empty($res)){ + $current=array_shift($res); + if($current==null) continue; + array_push($list,$current->val); + array_unshift($res,$current->right); + array_unshift($res,$current->left); + } + return $list; + } +} + +``` + +**** + +### :pencil2:2.二叉树的中序遍历(leetcode94) + + +​ + + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function inorderTraversal($root) { + $res=[]; + $this->helper($root,$res); + return $res; + } + function helper($root,&$res){ + if($root !=null){ + if($root->left !=null) $this->helper($root->left,$res); + array_push($res,$root->val); + if($root->right !=null) $this->helper($root->right,$res); + } + + } +``` + +**或者不用递归** + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function inorderTraversal($root) { + $res=[]; + $list=[]; + while(!empty($list) || $root !=null){ + while($root != null){ + array_unshift($list,$root); + $root=$root->left; + } + $root=array_shift($list); + array_push($res,$root->val); + $root=$root->right; + } + return $res; + } +``` +**** + + +### :pencil2:3.二叉树的后序遍历(leetcode145) + + +​ + + +```php + /** + * @param TreeNode $root + * @return Integer[] + */ + function postorderTraversal($root) { + $list=[]; + $res=[]; + array_push($list,$root); + while(!empty($list)){ + $node=array_shift($list); + if(!$node) continue; + array_unshift($res,$node->val); + array_unshift($list,$node->left); + array_unshift($list,$node->right); + } + return $res; + } + } +``` +**** + +### :pencil2:4.二叉树的层次遍历(leetcode102) + + +​ + + +**DFS和BFS都可以解,竟然已经要我们按照层打印了,那么先使用BFS,思路就是先判断树是否是空,不是空加入一个队列的结构中,如果队列不为空,取出头元素,那么当前元素表示的就是当前这一层了,所以只需要遍历这一层里的所有的元素即可,然后下一层....** + +```php + class Solution { + + /** + * @param TreeNode $root + * @return Integer[][] + */ + function levelOrder($root) { + if(empty($root)) return []; + $result = []; + $queue = []; + array_push($queue,$root); + while(!empty($queue)){ + $count = count($queue); + $leveQueue = []; + for($i = 0;$i<$count;$i++){ + $node = array_shift($queue); + array_push($leveQueue,$node->val); + if($node->left) array_push($queue,$node->left); + if($node->right) array_push($queue,$node->right); + } + array_push($result,$leveQueue); + } + return $result; + } + } +``` + +**如果使用DFS的话,就是一条路走到黑,然后再重新一路路的退回来再找下一路,所以这样的话,每一次我们需要记录一下当前他所在的这个点属于哪一层即可,代码用递归实现。** +```php + +class Solution { + + /** + * @param TreeNode $root + * @return Integer[][] + */ + function levelOrder($root) { + if(empty($root)) return []; + $result=[]; + $this->helper($result,$root,0); + return $result; + } + + function helper(&$result,$node,$level){ + if(empty($node)) return ; + if(count($result)<$level+1){ + array_push($result,[]); //说明当前行没有结果 + } + array_push($result[$level],$node->val); + $this->helper($result,$node->left,$level+1); + $this->helper($result,$node->right,$level+1); + } +} + +``` +**** + +### :pencil2:5.二叉树的最大深度(leetcode104) + + +​ + + +**DFS和BFS都可以解,竟然已经要我们按照层打印了,那么先使用BFS,思路就是先判断树是否是空,不是空加入一个队列的结构中,如果队列不为空,取出头元素,那么当前元素表示的就是当前这一层了,所以只需要遍历这一层里的所有的元素即可,然后下一层....** + +```php + + /** + * @param TreeNode $root + * @return Integer + */ + function maxDepth($root) { + if(empty($root)) return 0; + $left = $this->maxDepth($root->left); + $right = $this->maxDepth($root->right); + return $left<$right? $right+1:$left+1; + return max($left,$right)+1; + } +``` +**** + +### :pencil2:6.二叉树的最小深度(leetcode111) + + +​ + + +**DFS和BFS都可以求解** + +```php + + //BFS + /** + * @param TreeNode $root + * @return Integer + */ + function minDepth($root) { + if(empty($root)) return 0; + if(!$root->right) return $this->minDepth($root->left)+1; + if(!$root->left) return $this->minDepth($root->right)+1; + $left=$this->minDepth($root->left); + $right=$this->minDepth($root->right); + return min($left,$right)+1; + + } +``` + +```php + +//DFS + /** + * @param TreeNode $root + * @return Integer + */ + function minDepth($root) { + if(empty($root)) return 0; + $left=$this->minDepth($root->left); + $right=$this->minDepth($root->right); + if($left==0 || $right==0) return $left+$right+1; + return min($left,$right)+1; + } +``` +**** + + +### :pencil2:7.判断是否是平衡二叉树(leetcode110) + + +​ + + +**每一节点的两个子树的深度相差不能超过1。如果是空树,直接true。** + +```php + + +class Solution { + + /** + * @param TreeNode $root + * @return Boolean + */ + private $result=true; + function isBalanced($root) { + if(empty($root)) return true; + $this->helper($root); + return $this->result; + } + function helper($root) +{ + if(!$root) return ; + $left=$this->helper($root->left); + $right=$this->helper($root->right); + if(abs($left-$right)>1) $this->result=false; + return max($left,$right)+1; + } +} +``` +**** + + +### :pencil2:8.判断是否是对称二叉树(leetcode101) + + +​ + + +**1.两个子节点都是空,那说明他们是对称的返回true** + +**2.一个子节点为空,另一个子节点不为空,false** + +**3.两个子节点都不为空,但是他们不相等,false** + +**4.两个子节点不为空且相等,继续判断他们的左子树和右子树,把左子树的左子节点和右子树的右子节点进行比较,把左子树的右子节点和右子树的左子节点进行比较** + +```php + /** + * @param TreeNode $root + * @return Boolean + */ + function isSymmetric($root) { + if(empty($root)) return true; + return $this->helper($root->left,$root->right); + } + function helper($l,$r){ + if(!$l && !$r) return true; + if(!$l || !$r || $l->val != $r->val) return false; + return $this->helper($l->left ,$r->right) && $this->helper($l->right,$r->left); + } +``` +**** + +### :pencil2:9.反转二叉树(leetcode226) + + +​ + + +```php + + /** + * @param TreeNode $root + * @return TreeNode + */ + function invertTree($root) { + if(!$root) return null; + $list=[]; + array_push($list,$root); + while(!empty($list)){ + $node=array_shift($list); + $temp=$node->left; + $node->left=$node->right; + $node->right=$temp; + if($node->left) array_push($list,$node->left); + if($node->right) array_push($list,$node->right); + } + return $root; + } +``` +**递归解** +```php + /** + * @param TreeNode $root + * @return TreeNode + */ + function invertTree($root) { + if(empty($root)){ + return null; + } + $right=$this->invertTree($root->right); + $left=$this->invertTree($root->left); + $root->left=$right; + $root->right=$left; + return $root; + } +``` +**** + + +### :pencil2:10.给定单链表(值有序)转化成平衡二叉查找树(leetcode109) + + +​ + + +**先将链表数据转换成有序数组,然后利用二分查找的特性,构建左右子树。** + +```php + +/** + * Definition for a singly-linked list. + * class ListNode { + * public $val = 0; + * public $next = null; + * function __construct($val) { $this->val = $val; } + * } + */ +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ +class Solution { + + /** + * @param ListNode $head + * @return TreeNode + */ + function sortedListToBST($head) { + $data=[]; + while($head){ + array_push($data,$head->val); + $head=$head->next; + } + return $this->helper($data); + } + + function helper($data) +{ + if(!$data) return ; + $middle=floor(count($data)/2); + $node=new TreeNode($data[$middle]); + $node->left=$this->helper(array_slice($data,0,$middle)); + $node->right=$this->helper(array_slice($data,$middle+1)); + return $node; + } +} +``` +**** + + +### :pencil2:11.强盗打劫版本3(leetcode337) + + +​ + + +**最后的目的算出最多能抢金额数而不触发报警器。除了根节点,每一个结点只有一个父节点,能直接相连的两个节点不能同时抢,比如图1,抢了根节点,直接相连的左右子结点就不能抢。所以要么抢根节点的左右子结点,要么根结点+根结点->left->right+根结点->right->right。** + + +```php + +//递归 +/** + * @param TreeNode $root + * @return Integer + */ + function rob($root) { + if($root==null){ + return 0; + } + $res1=$root->val; + if($root->left !=null) { + $res1 +=$this->rob($root->left->left)+$this->rob($root->left->right); + } + if($root->right !=null){ + $res1 +=$this->rob($root->right->left)+$this->rob($root->right->right); + } + + $res2=$this->rob($root->left)+$this->rob($root->right); + return max($res1,$res2); + + } +``` + +**上面那种大量的重复计算,改进一下。** + + **如果结点不存在直接返回0,对左右结点分别递归,设置了4个变量,ll和lr分别表示左子结点的左右子结点的最大金额数,rl和rr分别表示右子结点的左右子结点的最大金额数。所以我们最后比较的还是两种情况,第一种就是当前结点+左右子结点的左右子结点的值(即这里定义的ll,lr,rl,rr).第二种是当前结点的左右子结点的值(也就是说我只抢当前结点的子结点,不抢当前结点和孙子结点),再通俗的说就是如果树的层数是3层,要么抢中间一层,要么抢上下两层,谁钱多抢谁。** + + ```php +/** + * @param TreeNode $root + * @return Integer + */ + function rob($root) { + $l=0;$r=0; + return $this->countMax($root,$l,$r); + } + function countMax($root,&$l,&$r){ + if($root==null) return 0; + $ll=0;$lr=0;$rl=0;$rr=0; + $l=$this->countMax($root->left,$ll,$lr); + $r=$this->countMax($root->right,$rl,$rr); + return max($root->val+$ll+$lr+$rl+$rr,$l+$r); + } +``` +**** + +### :pencil2:12.判断二叉树路径和是否存在(leetcode112) + + +​ + + +**只要使用深度优先算法思想遍历每一条完整的路径,如果是个空树直接false,如果结点没有左右子树(说明此时已然是叶子结点,判断值是否是给定值,这个条件正好是递归终止的条件),相等直接返回true,根据这个推导出递归公式。** + +```php +/** + * @param TreeNode $root + * @param Integer $sum + * @return Boolean + */ + function hasPathSum($root, $sum) { + if($root==null){ + return false; + } + if($root->left ==null && $root->right==null && $root->val==$sum) return true; + return $this->hasPathSum($root->left,$sum-$root->val) || $this->hasPathSum($root->right,$sum-$root->val); + } +``` + +**改成迭代** + +```php + +/** + * @param TreeNode $root + * @param Integer $sum + * @return Boolean + */ + function hasPathSum($root, $sum) { + if($root==null){ + return false; + } + $res=[]; + array_push($res,$root); + while(!empty($res)){ + $node=array_shift($res); + if(!$node->left && !$node->right ){ + if($node->val==$sum) return true; + } + if($node->left){ + $node->left->val +=$node->val; + array_push($res,$node->left); + } + if($node->right){ + $node->right->val +=$node->val; + array_push($res,$node->right); + } + } + return false; + } +``` +**** + + +### :pencil2:13.判断是否是二叉查找树(leetcode98) + + +​ + + +**思路有两种,二叉查找树的特点就是左子树上的结点都小于根结点,右子树上的结点都大于根节点。所以有两个方向,可以分别递归的判断左子树,右子树。或者拿左子树上的最大值,右子树上的最小值分别对应根结点进行判断。** + +```php + +/** + * Definition for a binary tree node. + * class TreeNode { + * public $val = null; + * public $left = null; + * public $right = null; + * function __construct($value) { $this->val = $value; } + * } + */ +class Solution { + + /** + * @param TreeNode $root + * @return Boolean + */ + function isValidBST($root) { + return $this->helper($root,null,null); + } + function helper($root,$lower,$upper){ + if($root==null) return true; + $res=$root->val; + if($lower !==null && $res<=$lower) return false; + if($upper !==null && $res>=$upper) return false; + if(!$this->helper($root->left,$lower,$res)) return false; + if(!$this->helper($root->right,$res,$upper)) return false; + return true; + } +} +``` +**** + + +### :pencil2:14.找出二叉树最后一层最左边的值(leetcode513) + + +​ + + +**思路有两种,二叉查找树的特点就是左子树上的结点都小于根结点,右子树上的结点都大于根节点。所以有两个方向,可以分别递归的判断左子树,右子树。或者拿左子树上的最大值,右子树上的最小值分别对应根结点进行判断。** + +```php + /** + * @param TreeNode $root + * @return Integer + */ + function findBottomLeftValue($root) { + $data=[]; + array_push($data,$root); + while(!empty($data)){ + $node = array_shift($data); + if($node->right) array_push($data,$node->right); + if($node->left) array_push($data,$node->left); + } + return $node->val; + } +``` +**** + + + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" new file mode 100644 index 0000000..0a052e5 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" @@ -0,0 +1,223 @@ +## :数据结构线性表之二叉树1 +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + + +​ + + +### :pencil2:一.二叉树的定义 +**既然都叫二叉树了,那么二叉树的特点是每个至多只有两棵子树。换句话来说,每个结点的度不超过两个,并且二叉树的子树还存在左右之分,它的次序不能任意颠倒。** +### :pencil2:二叉树的表现形态 + + +​ + + +**1.满二叉树:棵深度为k有且有2^k-1结点的树称之为满二叉树,比如图中的a(深度为4,拥有15个结点)。这里我们就可以得到二叉树的一个特性:在二叉树的第i层中至多有2^i-1个结点(i>=1)。当然了他不止这一个特性。。。** + +**2.完全二叉树:深度为k,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n一一对应时,才是完全二叉树。这里图c不是完全二叉树,假设根节点A是1,那么图c在n等于12这个点上就没有对应。所以不是完全二叉树。所以完全二叉树的特点:(1)叶子结点只可能在层次最大的两层上出现。(2)对任意一个结点,若其右子树的最大层次是l,那么其左子树的最大层数必须是l或者l+1。** + +**三·二叉树的存储树这种结构的存储不像线性结构那样简单,并不能直接用前后关系来描述。一个树有有很多的子树,一个结点不止一个后继。下面用图来说明用数组存储和链表存储。** + +### :pencil2:三.二叉树的存储 + + +​ + + +**按照顺序存储的结构。用一组连续的存储单元至上而下,至左到右存储,图中的0表示不存在该结点,每一个不存在的点我们都需要浪费一个空间。所以在最坏的情况下,一个深度为k且只有k个结点的单支树(树中不存在度为2的结点)却需要长度为2^k-1的一维数组。所以这种顺序存储适合完全二叉树。** + + +​ + + +**链式存储的结构是由一个数据元素和分别指向其左右子树的两个分支构成。包含了三个域:数据域,左右指针域。链表的头指向树的根节点。** + +### :pencil2:四·二叉树的前中后序遍历 +```php + +//二叉树 +class Tree{ + public $res=''; + public $left=null; + public $right=null; + public function __construct($res) +{ + $this->res=$res; + } + +} +//前序遍历 + function front($tree){ + if($tree==null){ + return ; + } + echo $tree->res.'
'; + front($tree->left); + front($tree->right); +} + +//中序遍历 + function middle($tree) +{ + if($tree==null){ + return ; + } + middle($tree->left); + echo $tree->res.'
'; + middle($tree->right); + } + +//后序遍历 + function backend($tree) +{ + if($tree==null){ + return ; + } + backend($tree->right); + backend($tree->left); + echo $tree->res.'
'; +} + +$treeA=new Tree('a'); +$treeB=new Tree('b'); +$treeC=new Tree('c'); +$treeA->left=$treeB; +$treeA->right=$treeC; +// +//front($treeA); +//echo "-------------------".'
'; +//middle($treeA); +//echo "-------------------".'
'; +//backend($treeA); +``` + +### :pencil2:五.二叉排序树的插入,查找,删除. +**我主要说明一下删除操作,删除操作主要分三种情况:(1)如果待删除结点中,只有一个左子结点,那么只要把待删除结点的父结点的left指向待删除结点的左子结点。(2)如果待删除结点只有一个右子结点,那么只要把带删除结点的父结点的right指向带删除结点的右子结点。(3)如果待删除结点有左右结点,那么需要找到这个结点右子树中最小的结点,把他替换到待删除的结点上面。** + + + ​ + + +```php + +class Tree{ + public $res=''; + public $left=null; + public $right=null; + public function __construct($res) +{ + $this->res=$res; + } + +} + +class BinarySortTree +{ + public $tree; + + public function getTree() +{ + return $this->tree; + } + + //插入 + public function insertTree($data) + { + if(!$this->tree){ + $this->tree=new Tree($data); + return ; + } + $p=$this->tree; + while($p){ + if($data<$p->res){ //如果插入结点当前结点 + if(!$p->left){ //并且不存在左子结点 + $p->left=new Tree($data); + return ; + } + $p=$p->left; + }elseif ($data>$p->res){ + if(!$p->right){ + $p->right=new Tree($data); + return ; + } + $p=$p->right; + }else{ + return ; + } + } + } + + + + //删除 + public function deleteTree($data) +{ + if (!$this->tree) { + return; + } + $p = $this->tree; + $fatherP = null; + while ($p && $p->res !== $data) { + $fatherP=$p; //结点的父结点 + if ($data > $p->res) { + $p = $p->right; + }else{ + $p=$p->left; + } + } + + //如果二叉树不存在 + if($p==null){ + var_dump('当前树中没有此结点');return; + } + + //待删除待有两个子结点 + if($p->left && $p->right){ + $minR=$p->right; + $minRR=$p;// 最小结点的父结点 + //查找右子树的最小结点 + while($minR->left){ + $minRR=$minR; + $minR=$minR->left; + } + $p->res=$minR->res;//把右子树上最小结点的值赋值给待删除结点 + $p=$minR; + $fatherP=$minRR; + + } + $child=null; + if($p->left){ + $child=$p->left; + }elseif($p->right){ + $child=$p->right; + }else{ + $child=null; + } + + if(!$fatherP){ //待删除结点是根结点 + $this->tree=$child; + }elseif ($fatherP->left==$p){ //待删除结点只有一个左结点,把待删除结点的父结点的left指向待删除结点的子节点 + $fatherP->left=$child; + }else{ //待删除结点只有一个右结点,把待删除结点的父结点的right指向待删除结点的子节点 + $fatherP->right=$child; + } + + } +} + +$sortTree=new BinarySortTree(); +$sortTree->insertTree(9); +$sortTree->insertTree(8); +$sortTree->insertTree(10); +$sortTree->insertTree(5); +$sortTree->insertTree(6); +$sortTree->insertTree(4); +//$sortTree->search(1); +//$sortTree->deleteTree(8); +//var_dump($sortTree->getTree()); +``` + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" new file mode 100644 index 0000000..a298935 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" @@ -0,0 +1,115 @@ +## :数据结构线性表之堆栈 + +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + + +​ + + +### :pencil2:一.栈的特点 +**栈是仅限在栈顶进行插入和删除的线性表.对栈来说,表尾端称之为栈顶,表头端称之为栈底.假设栈S=(a1,a2,a3,a4,a5....an),那么a1为栈的栈底,an为栈顶元素.对栈的操作是按照后进先出的原则,因此栈又称之为(LIFO)数据结构.** + +### :pencil2:二.栈的表示和实现 +**和线性表类似,栈也有两种存储的方式.顺序栈,即栈的存储是利用一组连续的存储单元依次存放自栈底到栈顶的元素.同时还要定义指针top指向栈顶元素在顺序栈中的位置.通常的做法是top=0表示空栈.一般在使用栈的过程中难以预料所需要的空间大小.所以预先分配大小的容量,当栈的使用空间不够时,再进行动态的扩容.top作为栈顶的指针,初始的时候指向栈底,当有元素入栈时,top自增,当从栈中删除一个元素时,top--,然后删除元素,所以top始终指向栈顶的下一个空位置.** + ```php +LOC(ai+1)=LOC(ai)+L //申明 这里的i,i+1是下标 +``` +**线性结构的顺序表示以元素在计算机中的物理位置相邻来表示逻辑位置相邻,相邻两个元素之间的位置以物理位置的角度分析就是相差一位,只要确定了存储线性表的起始位,那么我们就能任意存储线性表中元素,下图具体表示** + + +​ + + +### :pencil2:三.栈的使用场景 +**栈的使用场景很多,例如idea的撤销回退功能,浏览器前进和后退功能.数制的转换,表达式求值,八皇后.......** +### :pencil2:四.用栈实现代码 +```php + +/** + * 使用栈实现十进制转8精制,其他转换类似 + */ +function tenChageEight($num) +{ + $data=[]; + while($num) { + $val = $num % 8; + $num = intval(floor($num / 8)); + array_unshift($data, $val); + } + $data2=[]; + while(!empty($data)){ + array_push($data2,array_shift($data)); + } + return implode($data2); +} +var_dump(tenChageEight(1348)); +``` +**** +```php + +class Stack +{ + private $stack=[]; + private $size=0; + public function __construct($size) +{ + $this->size=$size; + } + + /** + *推入栈顶 + */ + public function push($value) +{ + if(count($this->stack)>=$this->size){ + return false; + } + array_unshift($this->stack,$value); + + } + /** + *出栈 + */ + public function pop() +{ + if($this->size==0){ + return false; + } + array_shift($this->stack); + } + /** + *获取栈顶元素 + */ + public function top() +{ + if($this->size==0){ + return false; + } + return current($this->stack); + } + + public function data() +{ + return $this->stack; + } + +} + +$stack=new Stack(10); +$stack->push(2); +$stack->push(10); +$stack->push(8); +$stack->pop(); +var_dump($stack->top()); +var_dump($stack->data()); +``` +### 联系 + + +​ + + + + + + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" new file mode 100644 index 0000000..a5c2117 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" @@ -0,0 +1,111 @@ +## 数据结构之散列表 +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +​ + + +### :pencil2:一.散列表的介绍 +**散列表也叫哈希表,在前面的各种数据结构中(数组,链表,树等),记录在结构中的相对位置是随机的,和记录的关键字不存在确定的关系。在顺序结构中,顺序查找,比较的结果是'='或者'!=',在二分查找,二叉排序树比较的关系是'>','=‘,’<‘,查找的效率依赖于查找过程中比较的次数。** +**什么样的方法能使得理想情况下不需要进行任何比较,通过一次存储就能得到所查的记录。答案就是记录存储位置和关键字之间的对应关系。** + + +​ + + +**我们记录了存储位置和关键字的对应关系h,因此在查找的过程中,只要根据对应关系h找到给定值k的像h(k),如果结构中存在关建字和k相等的记录,那么肯定是存储在h(k)的位置上,就像上图k1..k5的一样,我们称这个对应关系h为散列函数或者是哈希函数。** +**** +**从上面的例子可以看出:** + +**1.散列函数是一个映像,因此设定散列函数可以非常灵活。** + +**2.从图中可以看出,k2 != k5 ,但是h(k2)===h(k5),这就是散列冲突。** + +### :pencil2:二.散列冲突 + + +​ + + +**我们看当前例子,关键字 John Smith !=Sandra Dee,但是通过散列函数之后h(John Smith)===h(Sandra Dee),他们的位置都在散列表01这个位置,但是这个位置只能存储一个记录,那多出来的记录存放在哪里?** + +**正因为散列函数是一个映像,我们在构造散列函数的时候,只能去分析关键字的特性,选择一个恰当的散列函数来避免冲突的发生。一般情况下,冲突只能尽可能的减少,而不能完全避免。** + +### :pencil2:三.散列函数的构造 +**先来看看什么是好的散列函数。如果对于关键字集合中任意一个,经过散列函数映射到地址集合中任何一个位置的概率都是相等的,就可以称为均匀散列函数。** + +**1.直接定址法** + +**取关键字或者关键字的某个线性函数值为哈希地址。** + +**h(key)=key或者h(key)=a*key+b a,b为常量。比如说做一个地区年龄段的人数统计,我们就可以把年龄1-100作为关键字,现在你要查38岁的人口数时只需要h(38)=???。** +**** + +**2.数字分析法** + +**要通过具体的数字分析,找出关键字集合的特点和关系,通过这组关系构造一个散列函数。** +**** +**3.平方取中法** + +**取关键字平方后的中间几位为哈希地址,因为一般情况下,选定哈希函数时并不一定知道关键字的全部情况,取哪几位也不一定合适,而一个平方后的中间几位数和数的每一位都有关,由此使随机分布的关键字得到的哈希地址也是随机的。** +**** +**4.折叠法** + +**将关键字分割成位数相同的几部分,然后取这几部分的叠加和,也就是舍去它的进位,作为他的哈希地址。** +**** +**5.除留余数法** +**取关键字被某个不大于散列表表长m的数(暂且称之为p)除后所得余数为哈希地址。** +```php +h(key)=key MOD p, p<=m +``` +**以上都偏于理论,具体的使用需要视情况而定,通常,考虑的因素有以下几点** + +**1.计算散列函数所需的时间** + +**2.关键字的长度** + +**3.哈希表的大小** + +**4.关键字的分布情况** + + **5.记录查找的频率** + + ### :pencil2:四.处理冲突 + **之前说过,散列函数可以减少冲突,但是不能避免,这时候我们就需要处理冲突。** + + **1.开放寻址法** + + **简单的来说,下图中当前哈希表的长度是8,现在已填入关键字1,9,3,4,5的记录,假如当前我们需要再填入关键字10这条记录。通过散列函数得出h(3)===h(10),但是此时哈希地址2已被3占领。现在我们咋么处理?** + + **线性探测再散列得到下一个地址3,发现3也被占领,再求下一个地址4,还被占领,直到5这个位置时,处理冲突过程结束,把当前h(10)记录在地址为5的位置即可。也可以使用二次探测再散列或者伪随机再散列都是属于开放寻址法。** + + + ​ + + +**** + +**2.再散列法** + +**即在同义词产生地址冲突时计算另一个散列函数的地址,直到冲突不再发生,这种方法毋庸置疑,增加了计算的时间。** +**** +**3.链地址法** + +**将所有关键字为同义词的记录存储在同一个线性链表中。初始状态是一个空指针。凡是通过关键字计算出地址一样的记录都插入到当前位置的链表中,在链表中插入的位置可以是表头,表尾,或者表中,以保持同义词在同一个线性表中按关键字有序。** + + + ​ + + + **** + + **4.建立一个公共溢出区** + + **假设散列函数的值域为[0,m-1],则设置向量HashTable[0,m-1]为基本表,每一个分量存储一个记录,另外设置向量OverTable[0,v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表中。** +**** + +### 联系 + + + + + diff --git "a/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" new file mode 100644 index 0000000..697f3c1 --- /dev/null +++ "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" @@ -0,0 +1,156 @@ +## :算法之排序二分查找 +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +### :pencil2:二分查找 + + +​ + + + +**二分查找是一种常见的查找方式。在生活中有一个猜大小的例子。比如给定一个0-100的范围,让你猜出事先我所设置的数字,每次把数字的范围缩小到一半。第一次猜50,大了还是小了,继续缩小数字的范围。但是有一个前提,我们得保证我们查找的是一个有序的范围。** + +**** + +### :pencil2:查找思想 + +**二分查找针对的是一个有序的集合。它的查找算法有点类型分治的思想,就像上面所说的。每次我通过中间的数和指定的数进行比较,判断大小,缩小猜测的区间,最多到区间等于0的时候,结果也就是最终指定的答案。二分查找是一种高效的查找算法,它的时间复杂度是O(logn).** + +**** + +### :pencil2:php实现二分查找 + +**1.我们先实现一个最基础的,在一个有序数组中(数组没有重复的值)查找给定值。(迭代)** + +```php +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + // $middle=floor(($l+$r)/2); + // $middle=$l+floor(($r-$l)/2); + $middle=$l+(($r-$l)>>1); //使用位运算查找更高效 + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$res) $r=$middle-1; + else $l=$middle+1; + } + return -1; +} +$data=[2,5,6,7,12,34]; +$res=12; +var_dump(binarySerach($data,$res)); +``` +**** + +**使用递归实现刚才的操作。** + +```php +function binarySerach($data,$res){ + return recursion($data,0,count($data)-1,$res); +} + +function recursion($data,$l,$r,$res) +{ + if($l>$r){ + return -1; + } + $middle=$l+(($r-$l)>>1); + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$res) return recursion($data,$l,$middle-1,$res); + else return recursion($data,$middle+1,$r,$res); +} +$data=[2,5,6,7,12,34]; +$res=12; +var_dump(binarySerach($data,$res)); +``` +**** + + +### :pencil2:二分查找的变形问题 + +**上面的那个注释是在数字没有重复的情况下,现在我们来实现数组中有重复值的情况下,如何查找出第一个等于给定值的元素。其实就是在做等于判断的时候如果索引是0那么是第一个等于给定值的数,或者当前等于给定值的上一个索引值不等于给定值。** + +```php + +function binarySerach($data,$res){ + $l=0; + $r=count($data); + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]>$res) $r=$middle-1; + elseif($data[$middle]<$res) $l=$middle+1; + else{ + if($middle==0 || $data[$middle-1] !==$res) return $middle; + else $r=$middle-1; + } + } +} +$data=[2,5,6,7,8,8,10]; +$res=8; +var_dump(binarySerach($data,$res)); +``` +**** +**查找第一个大于等于给定值的元素。** +```php + +//查找第一个大于等于给定值的数 +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]<$res) $l=$middle+1; + else{ + if($middle==0 || $data[$middle-1]<$res ) return $middle; + else $r=$middle-1; + } + } + +} +$data=[2,5,6,7,8,8,10]; +$res=9; +var_dump(binarySerach($data,$res)); +``` +**** + +**针对数组是一个循环有序的数组Leetcode35题** +```php + +function binarySerach($data,$res) +{ + $l=0; + $r=count($data)-1; + while($l<=$r){ + $middle=$l+(($r-$l)>>1); + if($data[$middle]==$res) return $middle; + elseif ($data[$middle]>$data[$r]){ + if($data[$l]<=$res && $data[$middle]>$res) $r=$middle-1; + else $l=$middle+1; + }else{ + if($data[$middle]<=$res && $data[$r] >$res) $r=$middle-1; + else $l=$middle+1; + } + } +} +$data=[5,6,7,8,1,2,3,4]; +$res=7; +var_dump(binarySerach($data,$res)); +``` + +**当然二分查找还有很多的应用场景,我就总结到这了。** + + + + +### 联系 + + +​ + + + + + + diff --git "a/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" new file mode 100644 index 0000000..30c3dd4 --- /dev/null +++ "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" @@ -0,0 +1,259 @@ +## :算法算法之动态规划 + +####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) + +***动态规划需要花很长时间去练习巩固的。其实在理解动态规划之前正确的顺序应该是先写递归,因为很多动态规划的转移方程都是通过递归加记忆法从而推导出公式,但是我随性,所以就不按套路出牌了。接下来就是简单介绍一下动态规划以及应用场景,最后把之前刷过的动态规划的题目全放出来,方便一次性查看。*** +**** + +### :pencil2:动态规划的介绍 + +***动态规划背后的思想简单概括就是,若要解一个指定的问题,我们需要解它的不同部分问题(子问题),再合并子问题求出最终想要的解。*** + +***在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖于当前面临的状态,又影响以后的发展。当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线。这种把一个问题看做是一个前后关联具有链状结构的多阶段过程就称为多阶段决策过程,这种问题称为多阶段决策最优化问题。每个阶段中,都求出本阶段的各个初始状态到过程终点的最短路径和最短距离,当逆序倒推到过程起点时,便得到了全过程的最短路径及最短距离,同时附带得到了一组最优结果。*** + + +**** + +### :pencil2:动态规划的步骤 + + ***1.递归+记忆化 ->反向推出递推公式*** + + ***2.状态的定义 opt[n],dp[n].*** + + ***3.状态转移的方程dp[n]=dp[n-1]+dp[n-2]*** + + ***4.最优子结构*** +****** +**最经典的爬楼梯问题,给定一个数字代表着楼梯的层数,每次你可以走一步或者两步,求最终你可以有几种方式到达顶峰。递归是自上而下,一层层调用,到了递归的出口,又一层层的回来。而动态规划是从下而上的去思考,比如还是这个爬楼梯问题,如果是动态规划的思想就是** +```php + +f[n]=f[n-1]+f[n+2]; +//一个斐波那契数列 +``` + +***动态规划需要大量的实战练习,一般你只要记住两步,定义好状态,有些时候单单定义一个一维数组的状态是不够的,二通过状态的定义推出递推公式。具体看场景使用。(下面是leetcode70,72,120,121,122,123,152,300动态规划的题目)*** + + +​ + + +```php + /** + * @param Integer $n + * @return Integer + */ + function climbStairs($n) { + if($n<=1){ + return 1; + } + $res[0]=1; + $res[1]=1; + for($i=2;$i<=$n;$i++){ + $res[$i]=$res[$i-1]+$res[$i-2]; + } + return $res[$n]; + + } +``` +****** + + + +​ + + +```php + /** + * @param String $word1 + * @param String $word2 + * @return Integer + */ + function minDistance($word1, $word2) { + + for($i=0;$i<=strlen($word1);$i++) $dp[$i][0]=$i; + for($i=0;$i<=strlen($word2);$i++) $dp[0][$i]=$i; + + for($i=1;$i<=strlen($word1);$i++){ + for($j=1;$j<=strlen($word2);$j++){ + if(substr($word1,$i-1,1)==substr($word2,$j-1,1)){ + $dp[$i][$j]=$dp[$i-1][$j-1]; + }else{ + $dp[$i][$j]=min($dp[$i-1][$j],$dp[$i][$j-1],$dp[$i-1][$j-1])+1; + } + } + } + return $dp[strlen($word1)][strlen($word2)]; + } +``` +****** + + + +​ + + +```php + /** + * @param Integer[][] $triangle + * @return Integer + */ + function minimumTotal($triangle) { + if(empty(count($triangle))){ + return 0; + } + for($i=count($triangle)-1;$i>=0;$i--){ + for($j=0;$j +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + + //二维数组的0,1,2表示的状态分别是没买股票,买了股票, 卖了股票 + $res=0; + $pro[0][0]=$pro[0][2]=0; + $pro[0][1]=-$prices[0]; + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + $dp[0][0]=0; + $dp[0][1]= -$prices[0]; + $res=0; + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $prices + * @return Integer + */ + function maxProfit($prices) { + $res=0; + $dp[0][0][0]=0; + $dp[0][0][1]= -$prices[0]; + $dp[0][1][0]= -$prices[0]; + $dp[0][1][1]= -$prices[0]; + $dp[0][2][0]=0; + + for($i=1;$i +​ + + +```php + /** + * @param Integer[] $nums + * @return Integer + */ + function maxProduct($nums) { + $max=$min=$res=$nums[0]; + for($i=1;$i +​ + + +```php + +/** + * @param Integer[] $nums + * @return Integer + */ + function lengthOfLIS($nums) { + if(empty($nums)){ + return 0; + } + $res=1; + for($i=0;$i Date: Fri, 5 Jul 2019 17:44:34 +0800 Subject: [PATCH 20/35] Leetcode-php --- ...221\351\242\230\347\233\256\351\233\206\345\220\210.md" | 7 ++++++- ...221\345\237\272\346\234\254\346\223\215\344\275\234.md" | 7 ++++++- .../\345\240\206\346\240\210.md" | 2 +- .../\346\225\243\345\210\227\350\241\250.md" | 7 +++++-- .../\344\272\214\345\210\206\346\237\245\346\211\276.md" | 2 +- .../\345\212\250\346\200\201\350\247\204\345\210\222.md" | 6 +++++- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" index 1cb0b19..e31bb76 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/Leetcode\347\273\217\345\205\270\344\272\214\345\217\211\346\240\221\351\242\230\347\233\256\351\233\206\345\220\210.md" @@ -1,6 +1,6 @@ ## :pencil2:Leetcode经典二叉树题目集合 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) **** ### :pencil2:1.二叉树的前序遍历(leetcode144) @@ -613,6 +613,11 @@ class Solution { ``` **** +### 联系 + + +​ + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" index 0a052e5..766ea8e 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\344\272\214\345\217\211\346\240\221\345\237\272\346\234\254\346\223\215\344\275\234.md" @@ -1,5 +1,5 @@ ## :数据结构线性表之二叉树1 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) @@ -218,6 +218,11 @@ $sortTree->insertTree(4); //var_dump($sortTree->getTree()); ``` +### 联系 + + +​ + diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" index a298935..42ed110 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\345\240\206\346\240\210.md" @@ -1,6 +1,6 @@ ## :数据结构线性表之堆栈 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" index a5c2117..5174d2b 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204/\346\225\243\345\210\227\350\241\250.md" @@ -1,5 +1,5 @@ ## 数据结构之散列表 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) @@ -102,8 +102,11 @@ h(key)=key MOD p, p<=m **假设散列函数的值域为[0,m-1],则设置向量HashTable[0,m-1]为基本表,每一个分量存储一个记录,另外设置向量OverTable[0,v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表中。** **** + ### 联系 -### 联系 + + ​ + diff --git "a/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" index 697f3c1..6c5aa57 100644 --- "a/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" +++ "b/\347\256\227\346\263\225/\344\272\214\345\210\206\346\237\245\346\211\276.md" @@ -1,5 +1,5 @@ ## :算法之排序二分查找 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) ### :pencil2:二分查找 diff --git "a/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" index 30c3dd4..4443d82 100644 --- "a/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" +++ "b/\347\256\227\346\263\225/\345\212\250\346\200\201\350\247\204\345\210\222.md" @@ -1,6 +1,6 @@ ## :算法算法之动态规划 -####php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) +#### php-leetcode之路 [Leetcode-php](https://github.com/wuqinqiang/leetcode-php) ***动态规划需要花很长时间去练习巩固的。其实在理解动态规划之前正确的顺序应该是先写递归,因为很多动态规划的转移方程都是通过递归加记忆法从而推导出公式,但是我随性,所以就不按套路出牌了。接下来就是简单介绍一下动态规划以及应用场景,最后把之前刷过的动态规划的题目全放出来,方便一次性查看。*** **** @@ -251,7 +251,11 @@ f[n]=f[n-1]+f[n+2]; } ``` ****** +### 联系 + +​ + From eee17a2f122f161deccb6a98ccbb7455670d2492 Mon Sep 17 00:00:00 2001 From: xianyunyh Date: Sun, 22 Sep 2019 17:22:46 +0800 Subject: [PATCH 21/35] edit redis --- Cache/Redis.md | 7 ++ .../Readme.md" | 73 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 "\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" diff --git a/Cache/Redis.md b/Cache/Redis.md index b43dba6..54cfc97 100644 --- a/Cache/Redis.md +++ b/Cache/Redis.md @@ -39,3 +39,10 @@ redis是一个开源的支持多种数据类型的key=>value的存储数据库 > hset、hget、hmget、hmset、hkeys、hlen、hsetnx、hvals +### redis 各种类型的场景使用 + +- string 就是存储简单的key=>value的字符串 +- list 使用场景。做先进先出的消费队列 +- set 进行集合过滤重复元素 +- zset 有序集合,排行榜 TOP N +- hash 适合存储一组数据,比如用户的信息 以用户id为键,里面记录用户的昵称等信息。 \ No newline at end of file diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" "b/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" new file mode 100644 index 0000000..60290d5 --- /dev/null +++ "b/\346\223\215\344\275\234\347\263\273\347\273\237/Readme.md" @@ -0,0 +1,73 @@ + +# 操作系统概论 + +操作系统是一种计算机软件。在硬件之上,应用程序之下。主要功能就是管理底下的计算机硬件,并为上层的应用程序提供统一的接口。 + +![img](https://camo.githubusercontent.com/9f8cbe895e7729fc61e18424f2edb7b23ef9d6f3/687474703a2f2f6974666973682e6e65742f486f6d652f4d6f64756c65732f496d616765732f6974666973685f34343836395f302e6a7067) + +**系统调用**:应用程序直接调用操作系统提供的接口 如write 函数 + +**库函数调用**:应用程序通过一些库函数直接调用 如 fwrite + +## 内核态和用户态 + +操作系统为了管理内存。将内存分为**内核空间**(内核态)和**用户空间**。内存空间和用户空间之间有隔离。 + +**用户空间即上层应用程序的活动空间**,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用 + + + + + +![img](https://images2015.cnblogs.com/blog/431521/201605/431521-20160523163606881-813374140.png) + +内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。 + +shell就是外壳。类似一种胶水的功能。可以通过shell访问内核。 + +内核态与用户态是指CPU的运行状态(即特权级别),每个进程的每种CPU状态都有其运行上下文,运行上下文就包括了当前状态所使用的空间,CPU访问的逻辑地址(即空间)通过地址映射表映射到相应的物理地址(即物理内存)。在Linux系统中,进程的用户空间是独立的,而内核空间是公用的,进程切换时,用户空间切换,内核空间不变。 + +对于多数CPU而言,处于内核态时,可以访问所有地址空间,而处于用户态时,就只能访问用户空间了。 + + + +## 用户态和内核态切换 + +操作系统的资源是有限的,如果访问资源的操作过多,必然会消耗过多的资源,而且如果不对这些操作加以区分,很可能造成资源访问的冲突。 + +为了减少有限资源的访问和使用冲突,Unix/Linux的设计哲学之一就是:对不同的操作赋予不同的执行等级,就是所谓特权的概念 + +Linux操作系统中主要采用了0和3两个特权级,分别对应的就是内核态和用户态。运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。很多程序开始时运行于用户态,但在执行的过程中,一些操作需要在内核权限下才能执行,这就涉及到一个从用户态切换到内核态的过程 + +![img](https://images2015.cnblogs.com/blog/431521/201605/431521-20160523210140147-1668637440.gif) + + + +## 库函数调用和系统调用的区别 + +![这里写图片描述](http://img.blog.csdn.net/20170117211419709?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTEZfMjAxNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) + +系统调用(英语:system call),指运行在用户空间的应用程序向操作系统内核请求某些服务的调用过程。 系统调用提供了用户程序与操作系统之间的接口。一般来说,系统调用都在内核态执行。由于系统调用不考虑平台差异性,由内核直接提供,因而移植性较差(几乎无移植性)。 + +库函数(library function),是由用户或组织自己开发的,具有一定功能的函数集合,一般具有较好平台移植性,通过库文件(静态库或动态库)向程序员提供功能性调用。程序员无需关心平台差异,由库来屏蔽平台差异性。 + +| 函数库调用 | 系统调用 | +| ----------------------------- | ----------------------- | +| 平台移植性好 | 依赖于内核,不保证移植性 | +| 调用函数库中的一段程序(或函数) | 调用系统内核的服务 | +| 一个普通功能函数的调用 | 是操作系统的一个入口点 | +| 在**用户空间**执行 | 在**内核空间**执行 | +| 它的运行时间属于“用户时间” | 它的运行时间属于“系统”时间 | +| 属于过程调用,调用开销较小 | 在用户空间和内核上下文环境间切换,开销较大 | +| 库函数数量较多 | UNIX中大约有90个系统调用,较少 | +| 典型的C函数库调用:printf scanf malloc | 典型的系统调用:fork open write | + +![clip_image002](http://blog.chinaunix.net/attachment/201207/11/27105712_13419741441gpp.gif) + +读写IO通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),使用库函数调用可以大大减少系统调用的次数。这是因为缓冲区技术。在用户空间和内核空间,对文件操作都使用了缓冲区,当内核缓冲区写满之后或写结束之后才将内核缓冲区内容写到文件对应的硬件媒介中。 + +**不带缓冲指的是每个read和write这些文件I/O操作都调用的是系统调用,属于内核态的操作** + +诸如fread和fwrite这些标准I/O操作属于用户态操作,具体是库函数的实现,需要借助用户缓冲区来实现 + +更多内容可以参考 [操作系统](https://github.com/xianyunyh/studynotes/tree/master/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F) \ No newline at end of file From 350d6aebe0037bd117923133c94eeb0c7ec19a12 Mon Sep 17 00:00:00 2001 From: xianyunyh Date: Thu, 3 Oct 2019 20:45:08 +0800 Subject: [PATCH 22/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9websocket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../README.md" | 20 --- .../__pycache__/lagou.cpython-36.pyc" | Bin 3648 -> 0 bytes .../boss.py" | 60 --------- .../lagou.py" | 125 ------------------ .../lagoutest.py" | 19 --- .../stop.txt" | 47 ------- .../word.py" | 44 ------ ...14\345\210\206\346\237\245\346\211\276.md" | 2 +- .../Webscokt.md" | 95 ++++++++++++- 9 files changed, 90 insertions(+), 322 deletions(-) delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/README.md" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/__pycache__/lagou.cpython-36.pyc" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/boss.py" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/lagou.py" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/lagoutest.py" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/stop.txt" delete mode 100644 "\347\210\254\350\231\253\350\204\232\346\234\254/word.py" diff --git "a/\347\210\254\350\231\253\350\204\232\346\234\254/README.md" "b/\347\210\254\350\231\253\350\204\232\346\234\254/README.md" deleted file mode 100644 index 89b10b1..0000000 --- "a/\347\210\254\350\231\253\350\204\232\346\234\254/README.md" +++ /dev/null @@ -1,20 +0,0 @@ -## 爬取招聘网数据 - -在我们找工作的时候,有些时候,需要到各个网站去找岗位,程序员当然是使用自己的方式来快速的寻找自己的岗位了。于是就想到使用python来抓取最新的符合自己薪资条件的岗位,以及对应的岗位需要的JD,然后如果匹配的话,那么自己可以一键投递。 - - - -- [x] 模拟登录 - -- [x] 抓取拉钩最新数据 -- [ ] 爬取51job网站数据 -- [ ] 爬取智联数据 -- [ ] 爬取boss数据 -- [ ] 一键投递 -- [ ] 岗位分析【分析出现最多的关键词】 -- [ ] web可视化 - - - - - diff --git "a/\347\210\254\350\231\253\350\204\232\346\234\254/__pycache__/lagou.cpython-36.pyc" "b/\347\210\254\350\231\253\350\204\232\346\234\254/__pycache__/lagou.cpython-36.pyc" deleted file mode 100644 index a44a28e3914045da3b1e552ef2d280e2fdf82265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmb7HZEzGx8J_Q%-E2Y#IpNgO)ha!fGaEp-LO{LL1%xWdA#jo!qg%^%Cp-H!OHVIk zw65Y8(&ewFM`&iBK1GUjGo0f0u z>6qg?YRvUruGPF%&G(qeEN0&{{7j(vS==McVeTc(&oPf>P>-@K8$mtBa%>d!CN{=4 zp&n=BY%}W3Y=Uh;J;C%-T48bn-=obH^u#^U{bGlUZtvNulv4;>kA^gudCgccrCGC>6`M61EoaqSv0_Wi z##YT`_9~uIEe9>cCe{}X_}iUJwEqiU1j{NfGcKauOmWA2X{IHE(&11n@zUwA&RhA< z<`4Y z;ECNV%Stt|Bwv=kGyCG{nPbNidr5?ycER>XhL-oUsXYKlQ-iZ0bxPtG$W&shL(F zr<7x+RFHa`AL}D$i3~OX0(c$pyH##--+PEqf2&AeUs?=Bvvi(|V6m$l>_nZj%|QOo z2&zS4t6+#pEG4oi5<8M*aAgGT4*B-}jl2Edf4l$Vx7L4i>u>K}-MDzMf92hcf8Z)) zmGcE;TuZD<2pTz^HiF6(U&JW-eXAWtGO=ix#BK{HUt;m)Ko&gl0gMZZmOt^Yp!GLD z)~EJ1nu6y6*x?PldX_Nc2@Vl%MUVwrs6@be5>2*x_!C$z+J-^{c07H;Fmy*Zbk7)v zmBTGQg0TVBX(C}u;fWq2dXt1A?nVvdh7BSk^2S_m-+#ynOQPJ8r$~Y-{B%&Mb2+WL zlqMD&jz32J;*f=1GY4Q1P^J)8@hNMv`n#Wu4+BL}yl)6?3DGqloA z{_9E(9G;m2YzeUbGwA@D}vHB^As8Nm3sN znCUbKhlJDA_nAY>72cLmJn><4CL_d!P6S^vo|usof))@TLQ!xNvm+Xhz92F2!bIo3 zO%RMyMyZ%Q3XHwTA({vActnoZfWAqckT-5*Nzn`sSlpKO=wb(E3=%qN!9dx52qQ%z zL!x08LZ!ntVx8I7^cAFa=F~Id`^(F*HmiN5y<$S`;OZ5Nd1?5N@)01GA=k$S0+&Tl z4W)1+bwj*{@Hx_vE>f0N6-V6}LKqEKpwVdJo$!KtcUZgr2cUUtyj zpJc)RfTkT~#0ND+=~hoE0|xeYNZ0yr|N8#j>;20&`ro)EI18eC@BFfmO>EWn$6gpL zcLJ;V_DPx|K8Xi?D=IUVsA!+8bK+xwUPRE#Qk#;>GC@4iXa-S2^(ZmTT-{I7Z!=k;aA@g|$ zHQ2~`5A3Y6M+SCI*`ot{Oxc?T_PDY)59|qLZyDH=uw4-Qskz=ZQc@hQn?tAST6i{^ z?%Ane4l!SR99!??Pz+Q4a4%P+^bp7f&sTRRwrcgsLRX3_o$y*y=;-QPNht!^D~1c=CTG~lvqhj;Y)g>w79}14xtr4H*6JL1+GW3 zBPb~;7%6#4jP^OG(r)S;=$oHO;r9|Ghd|_8^q)ndpG$Q11faw;#+PtMc@%`EaY#R% zmmHwVQa1IaKQ+6sMSK)fm9PmqJ^OCEi!3C%g0i1@jtWW}Vw?&kn3VM&eP5dK>Aav4 zs7f?Sz!YZHB150~ND;k*&5(5({Bv~dj3|^IJ~B -​ +​ diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" index 08b8f20..37e0ab4 100644 --- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/Webscokt.md" @@ -2,7 +2,7 @@ > Websocket是html5提出的一个协议规范,参考rfc6455。 > -> websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便c-s之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。 +> websocket约定了一个通信的规范,通过一个握手的机制,客户端(浏览器)和服务器(webserver)之间能建立一个类似tcp的连接,从而方便C-S之间的通信。在websocket出现之前,web交互一般是基于http协议的短连接或者长连接。 > > WebSocket是为解决客户端与服务端实时通信而产生的技术。websocket协议本质上是一个基于tcp的协议,是先通过HTTP/HTTPS协议发起一条特殊的http请求进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。 @@ -10,7 +10,7 @@ ### Websocket和HTTP协议的关系 -同样作为应用层的协议,WebSocket在现代的软件开发中被越来越多的实践,和HTTP有很多相似的地方,这里将它们简单的做一个纯个人、非权威的比较: +同样作为应用层的协议,WebSocket在现代的软件开发中被越来越多的实践,和HTTP有很多相似的地方,这里将它们简单比较: #### 相同点 @@ -58,12 +58,10 @@ Sec-WebSocket-Version: 13 ```http HTTP/1.1 101 Switching Protocols -Content-Length: 0 Upgrade: websocket Sec-Websocket-Accept: ZEs+c+VBk8Aj01+wJGN7Y15796g= -Server: TornadoServer/4.5.1 Connection: Upgrade -Date: Wed, 21 Jun 2017 03:29:14 GMT +Sec-WebSocket-Protocol: chat, superchat ``` Sec-Websocket-Accept 是一个校验。用客户端发来的sec_key 服务器通过sha1计算拼接商GUID【258EAFA5-E914-47DA-95CA-C5AB0DC85B11 】 。然后再base64encode @@ -96,10 +94,95 @@ Sec-Websocket-Accept 是一个校验。用客户端发来的sec_key 服务器通 - Payload data:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。 +### 服务端简单实现 +```php +// 封装ws 协议的数据包 +function build($msg) { + $frame = []; + $frame[0] = '81'; // 81 就是 10000001 第一位1表示最后一个数据段,最后一位1表示这是文本数据 + $len = strlen($msg); + if ($len < 126) { + //7位长度 第一个是掩码 默认是0 + //小于126的时候 也是 01111110 数据包第二个字节表示长度 + $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len); + } else if ($len < 65025) { + //7位 + 16位 01111110 00000000 00000000 + $s = dechex($len); + $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s; + } else { + //7位 + 64位 01111111 00000000 00000000 + $s = dechex($len); + $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s; + } + $data = ''; + $l = strlen($msg); + for ($i = 0; $i < $l; $i++) { + $data .= dechex(ord($msg{$i})); + } + //最后是数据内容 + $frame[2] = $data; + $data = implode('', $frame); + return pack("H*", $data); +} +//拆包 +function parse($buffer) { + $decoded = ''; + $len = ord($buffer[1]) & 127; + if ($len === 126) { + $masks = substr($buffer, 4, 4); + $data = substr($buffer, 8); + } else if ($len === 127) { + $masks = substr($buffer, 10, 4); + $data = substr($buffer, 14); + } else { + $masks = substr($buffer, 2, 4); + $data = substr($buffer, 6); + } + for ($index = 0; $index < strlen($data); $index++) { + $decoded .= $data[$index] ^ $masks[$index % 4]; + } + return $decoded; + } +$socket = stream_socket_server("tcp://0.0.0.0:8888", $errno, $errstr); +if (!$socket) { + echo "$errstr ($errno)
\n"; + die; +} +while (1) { + $conn = stream_socket_accept($socket); + $data = stream_get_contents($conn,500); + $data = explode("\r\n",$data); + $secKey = ""; + foreach ($data as $key=>$val) { + if (strpos($val,"WebSocket-Key:") >= 1 ) { + $key = explode(":",$val ); + $secKey = $key[1]; + } + } + //固定key 算法 base64(sha1(key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11)) + $hashkey = base64_encode(sha1(trim($secKey)."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true)); + $ws = "HTTP/1.1 101 Switching Protocols\r\n"; + $ws .= "Upgrade: websocket\r\n"; + $ws .= "Connection: Upgrade\r\n"; + $ws .= "Sec-WebSocket-Version:13\r\n"; + $ws .= "Sec-WebSocket-Accept: $hashkey\r\n"; + $ws .= "Sec-WebSocket-Protocol: chat\r\n"; + $ws .= "\r\n"; + if (!$conn) { + continue; + } + fwrite($conn, $ws); + fwrite($conn,build("hello")); + fclose($conn); + break; +} +fclose($socket); +``` + ### 客户端 ```js -var websocket = new WebSocket("ws://127.0.0.1") +var websocket = new WebSocket("ws://127.0.0.1:8888","chat") websocket.onopen = function(){ } From 304cb61982acf488aa0d45581d58e44632b9f9e2 Mon Sep 17 00:00:00 2001 From: xianyunyh Date: Thu, 3 Oct 2019 21:29:45 +0800 Subject: [PATCH 23/35] https --- .../HTTP2.md" | 22 ++++++++++++++++-- .../images/http2.png" | Bin 0 -> 46892 bytes .../images/https.png" | Bin 0 -> 100501 bytes 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" create mode 100644 "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" index e8ae82a..2f329c8 100644 --- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/HTTP2.md" @@ -39,7 +39,7 @@ HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较 3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 4. HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。 -![img](http://tenny.qiniudn.com/HTTPQUBIE2.png) +![img](images/https.png) ## 使用SPDY加快你的网站速度 @@ -64,5 +64,23 @@ SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTT - **header压缩,**如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。 - **服务端推送**(server push),同SPDY一样,HTTP2.0也具有server push功能。目前,有大多数网站已经启用HTTP2.0,例如[YouTuBe](https://www.youtube.com/),[淘宝网](http://www.taobao.com/)等网站,利用chrome控制台可以查看是否启用H2 -![img](http://tenny.qiniudn.com/diff332.png) +![img](images/http2.png) +```conf +server { + listen 443 ssl http2; + server_name example.com; + root /var/www/example.com/public; + + # SSL + ssl_certificate /etc/live/example.com/fullchain.pem; + ssl_certificate_key /etc/live/example.com/privkey.pem; + ssl_trusted_certificate /etc/live/example.com/chain.pem; + + # index.html fallback + location / { + try_files $uri $uri/ /index.html; + } +} + +``` \ No newline at end of file diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/http2.png" new file mode 100644 index 0000000000000000000000000000000000000000..9cf1a93689e88adfdb3b24b38baa8d0754fbb909 GIT binary patch literal 46892 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 literal 0 HcmV?d00001 diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/https.png" new file mode 100644 index 0000000000000000000000000000000000000000..c998d8534939b0b25da12df321e15df3eb049eb2 GIT binary patch literal 100501 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