演算法學習之旅,終點亦是起點

chenlixin發表於2020-09-01

可點選右方檢視目錄

資料結構

腦圖:naotu.baidu.com/file/0a53d3a5343bd...

  • 一維:

    基礎:陣列 array (string), 連結串列 linked list

    高階:棧 stack, 佇列 queue, 雙端佇列 deque, 集合 set, 對映 map (hash or map), etc

  • 二維:

    基礎:樹 tree, 圖 graph

    高階:二叉搜尋樹 binary search tree (red-black tree, AVL), 堆 heap, 並查集 disjoint set, 字典樹 Trie, etc

  • 特殊:

    位運算 Bitwise, 布隆過濾器 BloomFilter

    LRU Cache

時間複雜度

www.bigocheatsheet.com/

演算法學習小結

演算法

  • If-else, switch —> branch

  • for, while loop —> Iteration

  • 遞迴 Recursion (Divide & Conquer, Backtrace)

  • 搜尋 Search: 深度優先搜尋 Depth first search, 廣度優先搜尋 Breadth first search, A*, etc

  • 動態規劃 Dynamic Programming

  • 二分查詢 Binary Search

  • 貪心 Greedy

  • 數學 Math , 幾何 Geometry

刷題路線分享

基礎

深度優先搜尋

回溯

分治

動態規劃

部分程式碼模板 (僅供參考)

並不是題目答案,只是說明大部分題目有套路可循;主要是java、python的版本,或者虛擬碼,應該能看懂。

遞迴

// java
public  void  recur(int level, int param) {

    // terminator

    if (level > MAX_LEVEL) {

        // process result

        return;

    }

    // process current logic

    process(level, param);

    // drill down

    recur( level: level + 1, newParam);

    // restore current status

}

分治、回溯

// java
private  static  int  divide_conquer(Problem problem, ) {

    if (problem == NULL) {

        int  res = process_last_result();

        return res;

    }

    subProblems = split_problem(problem)

    res0 = divide_conquer(subProblems[0])

    res1 = divide_conquer(subProblems[1])

    result = process_result(res0, res1);

    return result;

}

DFS

遞迴寫法

// java
public  List<List<Integer>> levelOrder(TreeNode root) {

    List<List<Integer>> allResults = new  ArrayList<>();

    if(root==null){

        return allResults;

    }

    travel(root,0,allResults);

    return allResults;

}

private  void  travel(TreeNode root,int level,List<List<Integer>> results){

    if(results.size()==level){

        results.add(new  ArrayList<>());

    }

    results.get(level).add(root.val);

    if(root.left!=null){

        travel(root.left,level+1,results);

    }

    if(root.right!=null){

        travel(root.right,level+1,results);

    }

}

非遞迴寫法

# Python
def  DFS(self, tree):

    if tree.root is  None:

        return []

    visited, stack = [], [tree.root]

    while stack:

        node = stack.pop()

        visited.add(node)

        process (node)

        nodes = generate_related_nodes(node)

        stack.push(nodes)

    # other processing work

    ...

BFS

# Python
def  BFS(graph, start, end):

    visited = set()

    queue = []

    queue.append([start])

    while queue:

        node = queue.pop()

        visited.add(node)

        process(node)

        nodes = generate_related_nodes(node)

        queue.push(nodes)

    # other processing work

    ...

貪心演算法

貪心演算法是在當前情況下做出的最優決定,它只考慮眼前,獲得的是區域性的最優解,並且,希望透過每次獲得區域性最優解最後找到全域性的最優解。

二分查詢

// Java
public  int  binarySearch(int[] array, int target) {

    int  left = 0, right = array.length - 1, mid;

    while (left <= right) {

        mid = (right - left) / 2 + left;

        if (array[mid] == target) {

            return mid;

        } else  if (array[mid] > target) {

            right = mid - 1;

        } else {

            left = mid + 1;

        }

    }

    return -1;

}

動態規劃

  • 動態規劃 和 遞迴或者分治 沒有根本上的區別(關鍵看有無最優的子結構)

  • 共性:找到重複子問題

  • 差異性:最優子結構、中途可以淘汰次優解

DP順推模板

# 虛擬碼
function DP():

    dp = [][] # 二維情況

    for i = 0 .. M {

        for j = 0 .. N {

            dp[i][j] = _Function(dp[i’][j’]...)

        }

    }

    return dp[M][N];

Trie 樹(字典樹)

// java
class  Trie {

    private  boolean  isEnd;

    private  Trie[] next;

    /** Initialize your data structure here. */

    public  Trie() {

        isEnd = false;

        next = new  Trie[26];

    }

    /** Inserts a word into the trie. */

    public  void  insert(String  word) {

        if (word == null || word.length() == 0) return;

        Trie  curr = this;

        char[] words = word.toCharArray();

        for (int  i = 0;i < words.length;i++) {

            int  n = words[i] - 'a';

            if (curr.next[n] == null) curr.next[n] = new  Trie();

            curr = curr.next[n];

        }

        curr.isEnd = true;

    }

    /** Returns if the word is in the trie. */

    public  boolean  search(String  word) {

        Trie  node = searchPrefix(word);

        return node != null && node.isEnd;

    }

    /** Returns if there is any word in the trie that starts with the given prefix. */

    public  boolean  startsWith(String  prefix) {

        Trie  node = searchPrefix(prefix);

        return node != null;

    }

    private  Trie  searchPrefix(String  word) {

        Trie  node = this;

        char[] words = word.toCharArray();

        for (int  i = 0;i < words.length;i++) {

            node = node.next[words[i] - 'a'];

            if (node == null) return  null;

        }

        return node;

    }

}

並查集

// java
class  UnionFind {

    private  int  count = 0;

    private  int[] parent;

    public  UnionFind(int  n) {

        count = n;

        parent = new  int[n];

        for (int  i = 0; i < n; i++) {

            parent[i] = i;

        }

    }

    public  int  find(int  p) {

        while (p != parent[p]) {

            parent[p] = parent[parent[p]];

            p = parent[p];

        }

        return p;

    }

    public  void  union(int  p, int  q) {

        int  rootP = find(p);

        int  rootQ = find(q);

        if (rootP == rootQ) return;

        parent[rootP] = rootQ;

        count--;

    }

}

布隆過濾器

// java
public  class  BloomFilter {

    private  static  final  int  DEFAULT_SIZE = 2 << 24;

    private  static  final  int[] seeds = new  int[] { 5, 7, 11, 13, 31, 37, 61 };

    private  BitSet  bits = new  BitSet(DEFAULT_SIZE);

    private  SimpleHash[] func = new  SimpleHash[seeds.length];

    public  BloomFilter() {

        for (int  i = 0; i < seeds.length; i++) {

            func[i] = new  SimpleHash(DEFAULT_SIZE, seeds[i]);

        }

    }

    public  void  add(String  value) {

        for (SimpleHash  f  : func) {

            bits.set(f.hash(value), true);

        }

    }

    public  boolean  contains(String  value) {

        if (value == null) {

            return  false;

        }

        boolean  ret = true;

        for (SimpleHash  f  : func) {

            ret = ret && bits.get(f.hash(value));

        }

        return ret;

    }

    // 內部類,simpleHash

    public  static  class  SimpleHash {

        private  int  cap;

        private  int  seed;

        public  SimpleHash(int  cap, int  seed) {

            this.cap = cap;

            this.seed = seed;

        }

        public  int  hash(String  value) {

            int  result = 0;

            int  len = value.length();

            for (int  i = 0; i < len; i++) {

                result = seed * result + value.charAt(i);

            }

            return (cap - 1) & result;

        }

    }

}

LRU Cache

// java 雜湊表 + 雙連結串列
class  LRUCache {

    /**

    * 快取對映表

    */

    private  Map<Integer, DLinkNode> cache = new  HashMap<>();

    /**

    * 快取大小

    */

    private  int  size;

    /**

    * 快取容量

    */

    private  int  capacity;

    /**

    * 連結串列頭部和尾部

    */

    private  DLinkNode  head, tail;

    public  LRUCache(int  capacity) {

        //初始化快取大小,容量和頭尾節點

        this.size = 0;

        this.capacity = capacity;

        head = new  DLinkNode();

        tail = new  DLinkNode();

        head.next = tail;

        tail.prev = head;

    }

    /**

    * 獲取節點

    * @param  key 節點的鍵

    * @return 返回節點的值

    */

    public  int  get(int  key) {

        DLinkNode  node = cache.get(key);

        if (node == null) {

            return -1;

        }

        //移動到連結串列頭部

        (node);

        return  node.value;

    }

    /**

    * 新增節點

    * @param  key 節點的鍵

    * @param  value 節點的值

    */

    public  void  put(int  key, int  value) {

        DLinkNode  node = cache.get(key);

        if (node == null) {

            DLinkNode  newNode = new  DLinkNode(key, value);

            cache.put(key, newNode);

            //新增到連結串列頭部

            addToHead(newNode);

            ++size;

            //如果快取已滿,需要清理尾部節點

            if (size > capacity) {

                DLinkNode  tail = removeTail();

                cache.remove(tail.key);

                --size;

            }

        } else {

            node.value = value;

            //移動到連結串列頭部

            moveToHead(node);

        }

    }

    /**

    * 刪除尾結點

    *

    * @return 返回刪除的節點

    */

    private  DLinkNode  removeTail() {

        DLinkNode  node = tail.prev;

        removeNode(node);

        return node;

    }

    /**

    * 刪除節點

    * @param  node 需要刪除的節點

    */

    private  void  removeNode(DLinkNode  node) {

        node.next.prev = node.prev;

        node.prev.next = node.next;

    }

    /**

    * 把節點新增到連結串列頭部

    *

    * @param  node 要新增的節點

    */

    private  void  addToHead(DLinkNode  node) {

        node.prev = head;

        node.next = head.next;

        head.next.prev = node;

        head.next = node;

    }

    /**

    * 把節點移動到頭部

    * @param  node 需要移動的節點

    */

    private  void  moveToHead(DLinkNode  node) {

        removeNode(node);

        addToHead(node);

    }

    /**

    * 連結串列節點類

    */

    private  static  class  DLinkNode {

        Integer  key;

        Integer  value;

        DLinkNode  prev;

        DLinkNode  next;

        DLinkNode() {

        }

        DLinkNode(Integer  key, Integer  value) {

            this.key = key;

            this.value = value;

        }

    }

}

十大經典排序演算法

閱讀:www.cnblogs.com/onepixel/p/7674659...

圖片來源於上文

演算法學習小結

演算法學習小結

快速排序

// java
public  static  void  quickSort(int[] array, int begin, int end) {

    if (end <= begin) return;

    int  pivot = partition(array, begin, end);

    quickSort(array, begin, pivot - 1);

    quickSort(array, pivot + 1, end);

}

static  int  partition(int[] a, int begin, int end) {

    // pivot: 標杆位置,counter: 小於pivot的元素的個數

    int  pivot = end, counter = begin;

    for (int  i = begin; i < end; i++) {

        if (a[i] < a[pivot]) {

            int  temp = a[counter]; a[counter] = a[i]; a[i] = temp;

            counter++;

        }

    }

    int  temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp;

    return counter;

}

歸併排序

// java
public  static  void  mergeSort(int[] array, int left, int right) {

    if (right <= left) return;

    int  mid = (left + right) >> 1; // (left + right) / 2

    mergeSort(array, left, mid);

    mergeSort(array, mid + 1, right);

    merge(array, left, mid, right);

}

public  static  void  merge(int[] arr, int left, int mid, int right) {

    int[] temp = new  int[right - left + 1]; // 中間陣列

    int  i = left, j = mid + 1, k = 0;

    while (i <= mid && j <= right) {

        temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];

    }

    while (i <= mid) temp[k++] = arr[i++];

    while (j <= right) temp[k++] = arr[j++];

    for (int  p = 0; p < temp.length; p++) {

        arr[left + p] = temp[p];

    }

    // 也可以用 System.arraycopy(a, start1, b, start2, length)

}

堆排序

// java
public  static  void  heapSort(int[] array) {

    if (array.length == 0) return;

    int  length = array.length;

    for (int  i = length / 2-1; i >= 0; i-)

        heapify(array, length, i);

    for (int  i = length - 1; i >= 0; i--) {

        int  temp = array[0]; array[0] = array[i]; array[i] = temp;

        heapify(array, i, 0);

    }

}

static  void  heapify(int[] array, int length, int i) {

    int  left = 2 * i + 1, right = 2 * i + 2int  largest = i;

    if (left < length && array[left] > array[largest]) {

        largest = left;

    }

    if (right < length && array[right] > array[largest]) {

        largest = right;

    }

    if (largest != i) {

        int  temp = array[i]; array[i] = array[largest]; array[largest] = temp;

        heapify(array, length, largest);

    }

}

結語

光說不練假把式,重要的是動手做題。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章