二分查詢變種題
Example:
[1 , 2 , 4 , 7 ]
8 返回-1
2 返回下標1
4返回下標2
0 返回-1
3返回比他小的最近的那個的下標 1
//指標移動的方法來縮小範圍
function binarySearch($arr, $n, $target)
{
$l = 0;
$r = $n - 1; //變數實際意義 [l....r]裡找target , 要明確變數的定義
//二分查詢的遞迴終止條件應該是沒有範圍可以查詢 , 這裡我看到的一點是遞迴和遞迴終止條件的資料的型別一般不一樣
//迴圈不變數是什麼 , l r一直在變, 但是迴圈不變數不會變 , //變數實際意義 [l....r]裡找target , 這個就是迴圈不變數
while ($l <= $r) //區間[l..r] 依然是有效的 , 是個只有一個元素的陣列
{
$mid = (int)(($l + $r) / 2);
if ($arr[$mid] == $target) {
return $mid;
}
//返回比他小的最近的那個的下標
if ($target > $arr[$mid] && $target < $arr[$mid+1]) {
return $mid;
}
if ($target > $arr[$mid]) {
$l = $mid + 1;
} else {
$r = $mid - 1;
}
}
//指標偏移出界,跳出迴圈,找不到
return -1;
}
多維陣列轉字串拼接
另一道類似題:一維陣列轉樹狀結構(巢狀陣列)
- a=1&b=2
- key小寫升序
- 如二維的
$[a][b]
則a.b=5 - 分號隔開 a=1&b=2;a.b=5……………
//廣度優先搜尋 , 使用佇列 , 類似樹的層序遍歷
function convertToStr($arr)
{
$str = '';
$queue[] =$arr ;
while (!empty($queue)) {
$a = array_shift($queue);
foreach ($a as $k=>$v) {
if (is_array($v)) {
array_push($queue, $v);
$str .= $k;
} else {
$str.=$k.$v;
}
}
var_dump($queue);
}
return $str;
}
計算使用者交易費用
Example:
梯度表
1-5筆 15元
6-10筆 5元
假設使用者交易筆數 6筆 , 則15 + 5 = 20元
除了遍歷字典外 , 還可以用雜湊表空間換時間 O(1)時間 O(n)空間
function getTradeFee(int $tradeCount):int
{
if ($tradeCount < 1) {
throw new InvalidArgumentException('trade count must large than 0');
}
$fee = 0;
$map = [
['min'=>1,'max'=>5, 'trade_fee' => 15],
['min'=>6,'max'=>10, 'trade_fee' => 5],
];
foreach ($map as $item) {
if ($tradeCount > $item['max']) {
$fee += $item['trade_fee'];
$tradeCount -= $item['max'];
}
if ($tradeCount >= $item['min'] && $tradeCount <= $item['max']) {
$fee += $item['trade_fee'];
return $fee;
}
}
}
實現trim函式(移除空格)
先寫暴力破解 , 犧牲空間的方法
LeetCode MoveZeros的類似題目 , 使用雙指標技術
//別打我,PHP的寫法就是簡單粗暴... 有空了再寫個O(1)空間複雜度的
function trim($arr)
{
$a = '';
$p1 = 0;
$len = strlen($arr);
for ($i = 0; $i <= $len-1; $i++) {
if ($arr[$i] != ' ') {
$a.= $arr[$i];
$p1++;
}
}
return $a;
}
字串翻轉
LeetCode Reverse String
使用技巧: Two Pointers
function reverseStr($str)
{
$len=strlen($str);
$p1=0;
$p2=$len-1;
while ($p1 <= $p2) {
$tmp = $str[$p1];
$str[$p1] = $str[$p2];
$str[$p2] = $tmp;
$p1++;
$p2--;
}
return $str;
}
Sort Colors
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note: You are not suppose to use the library's sort function for this problem.
Example:
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
1計數排序
function sortColors($arr)
{
$zero = 0;$one = 0;$two = 0;
$sorted = [];
foreach ($arr as $v) {
if ($v == 0) {
$zero++;
}
if ($v == 1) {
$one++;
}
if ($v == 2) {
$two++;
}
}
for ($i = 0; $i < $zero; $i++) {
$sorted[] = 0;
}
for ($i = 0; $i < $one; $i++) {
$sorted[] = 1;
}
for ($i = 0; $i < $one; $i++) {
$sorted[] = 2;
}
return $sorted;
}
2三路快排(對撞指標的一種) 這題能體現對指標的定義,操作,初始化
![image-20190321125552476](/Users/lin/Library/Application Support/typora-user-images/image-20190321125552476.png)
![image-20190321130629849](/Users/lin/Library/Application Support/typora-user-images/image-20190321130629849.png)
動態規劃 ?
Tips: 先找到子問題,再找重疊子問題
Advanced: 找狀態和狀態轉移函式
在陣列中找湊成的這個數的最小個數
如11 [1,2,5] 11=5+5+1 3個
1如果資料量極大 , 先排序, 二分查詢到<=目標值最近的那個值,往下遍歷
2動態規劃/遞迴+記憶化搜尋,找重複子問題
湊硬幣
1,2,5分硬幣,湊100分錢
倒水
3L 4L 杯子 量5L水
7L 17L 9L水
窮舉下去 通常從 小->大開始
斐波那契數列
Climbing Stairs
House Robber
Best Time to Buy and Sell Stock
這題有點難,有點類似Climbing Stairs
另一到類似題:如果你已經知道了未來100天一個股票股市的每一天的價格,然後請設計一套演算法求出什麼時候購入什麼時候賣出能達到最大收益。
如果只是一次買入一次賣出則簡單許多,同樣動態規劃
思考過程: 先暴力破解再考慮優化 , 其實每次遍歷只要減去前面數裡最小的一個就行了 , 所以可以把子問題的解存起來 , min(1) 前1個數最小的數 , min(n-1) 前n個數最小的數 , 然後判斷哪個比最大的大 , 就是答案了
function maxProfit($prices)
{
$maxProfit=0;
$min = $prices[0]; // 儲存子問題的解 min(.....)
foreach ($prices as $k=>$price) {
$min = min($min, $price);
if ($price - $min > $maxProfit) {
$maxProfit = $price-$min;
}
}
return $maxProfit;
}
手寫二叉搜尋樹
class Node
{
public $data;
public $left;
public $right;
public function __construct($data)
{
$this->data = $data;
}
}
class BST {
public $root;
public function __construct(Node $root)
{
$this->root = $root;
}
public function add($data)
{
if ($this->root == null) {
$this->root = new Node($data);
return true;
}
$node = $this->root;
while ($node) {
/*這裡遇到了很大的問題
if else語句 儘量寫成兩個分支的 , 減少在同一個if判斷多個條件
小資料量(1個也行)模擬一遍迴圈 , 防止寫錯無限迴圈
*/
if ($data < $node->data ) {
if ($node->left == null) {
$node->left = new Node($data);
break;
} else {
$node = $node->left;
}
}
if ($data > $node->data ) {
if ($node->right == null) {
$node->right = new Node($data);
break;
} else {
$node = $node->right;
}
}
}
return true;
}
}
反轉二叉樹
層序遍歷法
function reverse(Node $root)
{
$queue = [];
if ($root == null) {
return null;
}
array_push($queue, $root);
while (!empty($queue)) {
$node = array_shift($queue);
swap($node->left, $node->right);
if ($node->left) {
array_push($queue, $node->left);
}
if ($node->right) {
array_push($queue, $node->right);
}
}
return $root;
}
遞迴法
function reverse(Node $root)
{
if ($root == null) {
return null;
}
reverse($root->left);
reverse($root->right);
swap($root->left, $root->right);
return $root;
}
快速排序
操作原陣列寫法
![image-20190321125229483](/Users/lin/Library/Application Support/typora-user-images/image-20190321125229483.png)
//明確各變數 引數定義
//不變式
//終止條件
//對陣列[l,r] 進行快速排序
function quickSort($arr, $l,$r)
{
//當l大於等於R , 陣列只有一個元素或沒有元素了 , 不再需要排序
if ($l >= $r) {
return ;
}
//否則對陣列進行劃分操作,並返回p
$p = partition($arr,$l,$r);
quickSort($arr, $l, $p - 1); //[l,p-1] 繼續排
quickSort($arr, $p+1, $r); //[p+1,r] 繼續排
}
//返回p arr[l...p-1) < arr[p] arr[p] > arr[p+1...r]
function partition($arr,$l,$r):int
{
$pivot = $arr[$l]; //選取第一個元素作為pivot
$j = $l;
$i = $l+1;
for (; $i <= $r; $i++) {
if ($arr[$i] < $pivot) {
swap($arr[$i], $arr[$j]);
}
$j++;
}
swap($arr[$l], $arr[$j]);
}
犧牲空間複雜度但思路清晰的寫法 (未寫待續)
合併排序
function mergeSort($arr)
{
$count = count($arr);
$midIndex = (int)(($count-1 / 2) ) ;
if ($count==1) {
return $arr;
}
$l = mergeSort(array_slice($arr,0,$midIndex));
$r = mergeSort(array_slice($arr, $midIndex));
return merge2($l,$r);
}
function merge($l,$r)
{
$arr = [];
$lp = 0;
$rp = 0;
$lpMax = count($l)-1;
$rpMax = count($r)-1;
while ($lp<=$lpMax && $rp<=$rpMax) {
if ($l[$lp] < $r[$rp]) {
$arr[] = $l[$lp];
$lp++;
}
if ($r[$rp] < $l[$lp]) {
$arr[] = $r[$rp];
$rp++;
}
}
if ($lp <= $lpMax) {
$arr = array_merge($arr,array_slice($l,$lp));
}
if ($rp <= $rpMax) {
$arr = array_merge($arr,array_slice($r,$rp));
}
return $arr;
}
Move Zeros
Given an array nums
, write a function to move all 0
's to the end of it while maintaining the relative order of the non-zero elements.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
1可以用類似trim的解法,後面填充
function moveZeros($arr)
{
$a = [];
$p1 = 0;
for ($i = 0; $i <= count($arr)-1; $i++) {
if ($arr[$i] != 0) {
$a[] = $arr[$i];
$p1++;
}
}
for (; $p1 <= count($arr) - 1; $p1++) {
$a[$p1]=0;
}
return $a;
}
2雙指標(未寫待續)
[0,k) 是非0 [k,n-1]是0
一個指標用於遍歷陣列 , 遇到非0元素放到k處 遍歷完畢停止
另一個用於準備下一個非0元素的存放位置 , 然後從k遍歷到陣列末尾填充0
![image-20190321201343415](/Users/lin/Library/Application Support/typora-user-images/image-20190321201343415.png)
給定一個資料量極大的亂序整數陣列,找出裡面的最大值
請想出一個高效演算法
最少都要掃描一遍,暴力破解感覺就是最優了
除非可以容忍一定的錯誤率 , 按資料分佈取部分樣本(比如每隔1個取1個)
function max($arr)
{
$gap = 2;
$max = $arr[0];
for ($i = 0; $i < count($arr); $i += $gap) {
if ($arr[$i] > $max) {
$max = $arr[$i];
}
}
return $max;
}
兩個有序陣列 , 判斷其中一個是否為另一個的子陣列
二分查詢變種
Kth Largest Element in an Array ?
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
快排partition + 類似二分
or 遍歷 n*k遍
Longest Palindromic Subsequence ?
Given a string s, find the longest palindromic subsequence's length in s. You may assume that the maximum length of s is 1000.
Example 1:
Input:
"bbbab"
Output:
4
One possible longest palindromic subsequence is "bbbb".
Example 2:
Input:
"cbbd"
Output:
2
One possible longest palindromic subsequence is "bb".
使用滑動視窗
Multiply Strings ?
Given two non-negative integers num1
and num2
represented as strings, return the product of num1
and num2
, also represented as a string.
Example 1:
Input: num1 = "2", num2 = "3"
Output: "6"
Example 2:
Input: num1 = "123", num2 = "456"
Output: "56088"
Note:
- The length of both
num1
andnum2
is < 110. - Both
num1
andnum2
contain only digits0-9
. - Both
num1
andnum2
do not contain any leading zero, except the number 0 itself. - You must not use any built-in BigInteger library or convert the inputs to integer directly.
亂序陣列中找比左邊都小比右邊都大的數 用Go寫的
//小於前面最小的 , 大於後面最大的 t 滿足 arr[0...t-1]>arr[t] arr[t+1...l]<arr[t]
// O(n^2)
//優化,儲存 前n個數字最小的 , 後n個數字最大的 , 則第二次掃描只要一遍n+n=2n
func findPos(arr []int) int {
l := len(arr) - 1
for i := 0; i <= l; i++ {
minLeft := arr[0]
maxRight := arr[l]
for j := 0; j <= l; j++ {
if j < i {
if arr[j] < minLeft {
minLeft = arr[j]
}
}
if j == i {
continue
}
if j > i {
if arr[j] > maxRight {
maxRight = arr[j]
}
}
}
if arr[i] < minLeft && arr[i] > maxRight {
return i
}
}
return -1
}
func main() {
arr := []int{3, 4, 2, 0, 1}
fmt.Println(findPos(arr))
}
陣列實現迴圈佇列
演算法題總結
1.總結各種演算法用到的解題工具
1.1對撞指標/多指標/滑動視窗
1.2分解為子問題/樹狀結構/動態規劃/遞迴+記憶化搜尋
1.3利用partition每一步的性質獲得第k大的數
2.需要始終貫徹的
2.1明確變數的定義
2.2明確函式的定義
2.3迴圈不變數/不變式 -> 確認邊界條件
2.4小資料量除錯
3.其他
3.1當實在沒有好的思路的時候,先解決問題, 之後再考慮優化 一定要嘗試暴力解法 雖然沒有使用到特殊條件 , 但是解決了比沒解決強 , 不斷問自己是否能更優
3.2很多問題都是經典問題和經典問題的延伸
3.3字串的問題可以看做就是陣列問題
3.4一些實際問題可以轉化成演算法模型來解決 , 比如那道老鼠毒藥題 , 轉化為狀態0和1
SQL題
使用者表 帖子表 前10發帖量使用者名稱和發帖量,是最優解嗎,如何加索引,為何這樣加
select a.username b.thread_count from users as a
join
(select uid,count(*) as thread_count from thread groupby thread_count limit 10 order by thread desc ) as b
on a.uid=b.uid
可能的優化:
仔細看看JOIN,GROUPBY,ORDERBY,LIMIT 這幾個的內部執行過程 , 內部對索引的使用
ORDERBY 內部過程 索引使用 https://www.cnblogs.com/gulingjingguai/p/9484275.html
ORDERBY
MySQL
問了隔離級別 mysql預設級別 , 可重複讀是啥 , 還說了MVCC
ASCII , UTF8編碼 , 位元組序
概率論題
Tips: 排列組合問題解不出來就做笛卡爾積 , 爆破
1.30個人每個人生日都不同的概率
簡單排列題…不能再簡單
2.從100只重量不同羊裡面先選出十隻,然後從這十隻中選出一隻最重的記為A,然後再從剩餘的90只選出十隻,再從十隻裡面選重量最重的為B 問 P(A>B)=?
貝葉斯公式 , P(A|B) , 再P(A>B)= P(A|B)*0.5
3.n個人有多大概率至少兩個人是同一天生日
假設n=30
至少兩個人同一天生日= 1-沒有一個人是同一天生日,同第一題
4.一個村有一條河一邊70W人另一邊30W,每天有100W,跨河的有多少(題意模糊,最好先問清題意)
邏輯題
1.1000 個瓶子中有一瓶毒藥,一隻老鼠吃到毒藥一週之內會死,如果要在一週之內檢測出有毒藥的一瓶,問至少需要幾隻老鼠?
只有一瓶毒藥,所以有1000種組合 , 一隻老鼠有 死/活 兩種狀態 , 10只老鼠有2^10種死活組合
狀態對映 2^10=1024 > 1000個狀態
類似的二進位制對映: ASCII 二進位制對映到字元
QQ使用的udp是如何保證可靠的
FPM/NGINX/MYSQL 許多都是使用TCP進行通訊的,體現了TCP的重要性
排查連線問題的方法 ?
如:排查php-fpm連線數過多問題
檢視TCP連線命令: lsof -i tcp
排查FPM程式問題
排查NGINX程式問題
排查MYSQL執行緒問題,事務問題,鎖問題
死鎖的本質,如何排查死鎖 ?
mysql官方文件
如何用一條表示式求出ABC三個數中間的那個數
暴力解法:
if(a>=b&&a<=c||a<=b&&a>=c)則a
if(b>=a&&b<=c||b<=a&&b>=c)則c
if(c>=a&&c<=b||c<=a&&c>=b)則b
數學解法:
private static int f(int a, int b, int c) {
if ((b - a) * (a - c) >= 0) {
return a;
} else if ((a - b) * (b - c) >= 0) {
return b;
} else {
return c;
}
}