ARTS第一周

Algorithm

leetcode301(https://leetcode.com/problems/remove-invalid-parentheses/),刪除最少數(shù)量字符使字符串有效,返回所有可能結(jié)果。有效判定左括號'('和右括號')'匹配。

- Input:"()())()"      Output:["()()()", "(())()"]
- Input:"(a)())()"    Output:["(a)()()", "(a())()"]
- Input:")("            Output: [""]

??看到這個問題第一眼想到的是棧,類似判定字符串是否回文,但是返回的結(jié)果只有一個顯然是不符合結(jié)果的。后面經(jīng)過考慮之后決定通過棧都方式返回1個結(jié)果,再做進一步計算。
??最終大致思路就是對所有對可能情況通過遞歸查找,最后排除錯誤結(jié)果。同時,為了減少遞歸分支,通過增加對最終結(jié)果無效的情況提前判斷終止遞歸。

code
public List<String> removeInvalidParentheses(String s) {
        // 求出1個結(jié)果
        String result = remove(s);
        // 計算最小刪減無效字符數(shù)量
        int strLeft = 0, strRight = 0, resultLeft = 0, resultRight = 0;
        char c;
        for (int i = 0; i < s.length(); i++) {
            c = s.charAt(i);
            if (c == '(') {
                strLeft++;
            } else if (c == ')') {
                strRight++;
            }
        }
        for (int i = 0; i < result.length(); i++) {
            c = result.charAt(i);
            if (c == '(') {
                resultLeft++;
            } else if (c == ')') {
                resultRight++;
            }
        }

        // 找出可能結(jié)果
        List<String> list = new ArrayList<>();
        recursion(list, s, "", 0, 0, 0, strLeft - resultLeft, strRight - resultRight);
        // 排除結(jié)果中無效的字符串
        Iterator<String> it = list.iterator();
        String item;
        while (it.hasNext()) {
            item = it.next();
            if (remove(item).length() != item.length()) {
                it.remove();
            }
        }
        return list;
    }

/**
 * 刪除無效的字符串(返回1個結(jié)果,明確最少刪除字符數(shù))
```java)
 */
private String remove(String s) {
        if (s.length() <= 0) {
            return "";
        }
        Stack<Character> stack = new Stack<>();
        StringBuilder ret = new StringBuilder();
        Character c;
        for (int i = 0; i < s.length(); i++) {
            c = s.charAt(i);
            if (stack.size() == 0) {
                if (c == '(') {
                    stack.push(c);
                } else if (c == ')') {
                } else {
                    ret.append(c);
                }
            } else {
                if (c == '(') {
                    stack.push(c);
                } else if (c == ')') {
                    Character temp;
                    StringBuilder tempS = new StringBuilder();
                    while (stack.size() > 0) {
                        temp = stack.pop();
                        if (temp == '(') {
                            tempS.insert(0, temp).append(c);
                            break;
                        } else {
                            tempS.insert(0, temp);
                        }
                    }
                    ret.append(tempS);
                } else {
                    Character temp = stack.peek();
                    if (temp == ')') {
                        ret.append(c);
                    } else {
                        stack.push(c);
                    }
                }
            }
        }
        Character temp;
        StringBuilder tempS = new StringBuilder();
        while (stack.size() > 0) {
            temp = stack.pop();
            if (temp != '(' && temp != ')') {
                tempS.insert(0, temp);
            }
        }
        ret.append(tempS);
        return ret.toString();
    }

/**
 * 可能結(jié)果
 * 
 * @param list 結(jié)果
 * @param s 原字符串
 * @param result 遞歸中結(jié)果
 * @param index index
 * @param deleteLeft 左括號刪除數(shù)
 * @param deleteRight  右括號刪除數(shù)
 * @param minDeleteLeft 左括號最小刪除數(shù)
 * @param minDeleteRight 右括號最小刪除數(shù)
 */
private void recursion(List<String> list, String s, String result, int index, int deleteLeft, int deleteRight, int minDeleteLeft, int minDeleteRight) {
        if (deleteLeft > minDeleteLeft) return;
        if (deleteRight > minDeleteRight) return;
        // 剩余字符串小于刪除數(shù)
        if (minDeleteLeft + minDeleteRight - deleteLeft - deleteRight > s.length() - index) return;
        if (index >= s.length()) {
            if (deleteLeft == minDeleteLeft && deleteRight == minDeleteRight) {
                if (!list.contains(result)) {
                    list.add(result);
                }
            }
        } else {
            Character c;
            int i;
            for (i = index; i < s.length(); i++) {
                c = s.charAt(i);
                if (c == '(') {
                    recursion(list, s, result + c, i + 1, deleteLeft, deleteRight, minDeleteLeft, minDeleteRight);
                    deleteLeft++;
                    //recursion(list, s, result, i + 1, deleteLeft + 1, deleteRight, minDeleteLeft, minDeleteRight);
                    //break;
                } else if (c == ')') {
                    recursion(list, s, result + c, i + 1, deleteLeft, deleteRight, minDeleteLeft, minDeleteRight);
                    deleteRight++;
                    //recursion(list, s, result, i + 1, deleteLeft, deleteRight + 1, minDeleteLeft, minDeleteRight);
                    //break;
                } else {
                    result = result + c;
                }
            }
            if (i == s.length() && deleteLeft == minDeleteLeft && deleteRight == minDeleteRight) {
                if (!list.contains(result)) {
                    list.add(result);
                }
            }
        }
    }

代碼效率不是特別高,還能繼續(xù)優(yōu)化,優(yōu)化方向一致。
??最后個人總結(jié),只能深度搜索所有可能才能找到結(jié)果的情況,可以通過各種方式,提前結(jié)束子搜索,類似樹分叉提前砍斷子樹,來提高效率。

Review

??作為一個java程序員,一定是用過spring,在springboot配置配置數(shù)據(jù)庫連接池默認(rèn)使用 HikariCP,HikariCP有關(guān)于數(shù)據(jù)庫連接池說明About-Pool-Sizing。
??按照我之前的想法連接池連接數(shù)越多,肯定程序越快,文中指出連接池數(shù)量在不考慮磁盤開銷、網(wǎng)絡(luò)開銷等情況,連接池數(shù)和cpu核數(shù)相同性能最好,如果連接數(shù)遠(yuǎn)大于cpu核數(shù),cpu上下文切換會帶來額外開銷。
??在考慮磁盤開銷和網(wǎng)絡(luò)開銷情況,ssd是會減少磁盤開銷,同時帶寬更高會減少網(wǎng)絡(luò)開銷,一個系統(tǒng)數(shù)據(jù)庫連接數(shù)應(yīng)當(dāng)考慮磁盤、帶寬和cpu核數(shù)來配置適當(dāng)?shù)倪B接池連接數(shù),不然不計后果增加連接數(shù)會適得其反。
(文中的數(shù)據(jù)庫連接池連接數(shù)提供來一個公式,沒細(xì)看,覺得只能作為參考)

再分享一篇文章java in flames,講java中結(jié)合-XX:+PreserveFramePointer參數(shù),對java程序包括底層以及系統(tǒng)進行可視化分析。

Tip

自學(xué)docker遇到需要寫shell腳本。
linux命令 ps -ef|grep xxxxx | grep -v grep|awk '{print $2}'
ps -ef|grep用來找pid。grep -v 反向grep即排除有指定字符的結(jié)果,$2第二列就是pid。寫shell腳本找到pid,然后kill -9 pid。

java包啟動-classpath參數(shù)后jar包,linux使用:分開jar,windows使用;分開jar

分享個git常用命令忽略文件修改
git update-index --assume-unchanged FILENAME
git update-index --no-assume-unchanged FILENAME

Share

結(jié)合前段時間剛跟學(xué)的mysql對自己影響做個思考總結(jié),對比自己一個java程序員思考
1.mysql中server層和innodb引擎之間同步,redolog先寫入處于prepare狀態(tài),binlog再寫完之后提交講redolog改成commit,即使出現(xiàn)異常(如:binlog未寫完mysql異常重啟,binlog恢復(fù)數(shù)據(jù)沒未保存的數(shù)據(jù);binlog寫完mysql異常重啟,binlog恢復(fù)數(shù)據(jù)后發(fā)現(xiàn)redolog處于prepare狀態(tài)后會講redolog改成commit,事物完成,兩邊一致)。在我的理解中和分布式事物很相似,保證事物完整性;
2.事物的隔離中有可重復(fù)讀,事物啟動時建立單獨視圖其他事物不可見,java中有個ThreadLocalMap中就是線程對對象對拷貝,每個線程有自己單獨對對象;
3.mysql鎖可以表鎖和全局鎖,對應(yīng)不同粒度。在java開發(fā)中,同樣可以鎖一個整個方法也可以通過不同對對象java更加細(xì)粒度鎖;
4.在mysql中有change_buffer可以對修改暫時在內(nèi)存中進行,后面再寫到磁盤中,同時寫入順序?qū)懣梢詼p少開銷。同樣,在開發(fā)中,對數(shù)據(jù)庫頻繁跟新可以加一層緩存,暫時在緩存中更新,然后通過定時任務(wù)定時將緩存寫入磁盤,為兩保證緩存不會因為程序重啟不丟失,可以使用redis來代替程序緩存;
5.mysql內(nèi)存數(shù)據(jù)頁,將數(shù)據(jù)加載在優(yōu)先隊列中,同時隊列分兩部分,前面一部分young屬于優(yōu)先隊列原則,后面一部分old在數(shù)據(jù)頁讀如內(nèi)存時放在old頭,同時在讀內(nèi)存數(shù)據(jù)時,如果old數(shù)據(jù)2次訪問間隔時間不超過1s,就將數(shù)據(jù)頁移到y(tǒng)oung頭,這樣對于訪問不頻繁對數(shù)據(jù)很容易被淘汰。這樣數(shù)據(jù)結(jié)構(gòu)在開發(fā)中目前沒有遇到過,但是很有借鑒意義。
6.mysql通過一些緩存將隨機訪問變成順序訪問就如數(shù)組這樣對數(shù)據(jù)結(jié)構(gòu)訪問比鏈表更快一樣。
目前就想到這樣,后面有還會繼續(xù)學(xué)習(xí)mysql有更深對理解會繼續(xù)思考。
最后想說,各種語言在不斷演變過程都有相互借鑒,雖然語言不同目的不同,但是思想是永遠(yuǎn)不變的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容