数据结构与算法

目录

时间复杂度和空间复杂度分析

参考链接:

  • 如何理解算法时间复杂度的表示法
  • Master theorem
  • 主定理

数组、链表、跳表的基本实现和特性

参考链接:

  • Java ArrayList源码分析
  • Linked List的标准实现代码
  • Linked List示例代码
  • Java LinkedList源码分析
  • LRU Cache - Linked list: LRU缓存机制
  • Redis - Skip List: 跳跃表、为啥Redis使用跳表(Skip List)而不是使用Red-Black?

<栈、队列、优先队列、双端队列>

参考链接:

  • Java的PriorityQueue文档
  • Java的Stack源码
  • Java的Query源码
  • Python的heapq
  • Python高性能的container库

哈希表、映射、集合

参考链接:

  • Java Set文档
  • Java Map文档

二叉树的遍历

前序遍历

根节点----->左子树----->右子树

代码模板:

// 方式一:使用递归
public void preOrderTraverse(TreeNode node) {
    if (node != null) {
        // 对节点进行处理
        // to do something
        // 迭代左右子节点
        preOrderTraverse(node.left);
        preOrderTraverse(node.right);
    }
}

// 方式二:使用栈存放节点
public void preOrderTraverse(TreeNode root) {
    Stack stack = new Stack();
    TreeNode tmp = root;
    while (tmp != null || !stack.isEmpty()) {
        if (tmp != null) {
            // 处理当前节点
            // to do something
            stack.push(tmp);
            tmp = tmp.left;
        } else {
            TreeNode node = stack.pop();
            tmp = node.right;
        }
    }
}

中序遍历

左子树----->根节点----->右子树

代码模板:

// 方式一:递归
public void inOrderTraverse(TreeNode root) {
    if (root != null) {
        // 先递归左子树
        inOrderTraverse(root.left);
        // 处理当前节点
        // to do something
        // 递归右子树
        inOrderTraverse(root.right);
    }
}

// 方式二:栈
public void inOrderTraverse(TreeNode root) {
    Stack stack = new Stack<>();
    TreeNode tmp = root;
    while (tmp != null || !stack.isEmpty()) {
        if (tmp != null) {
            stack.push(tmp);
            tmp = tmp.left;
        } else {
            TreeNode node = stack.pop();
            // 处理节点
            // to do something
            tmp = node.right;
        }
    }
}

后序遍历

左子树----->右子树----->根节点

代码模板:

// 方式一:递归
public void postOrderTraverse(TreeNode root) {
    if (root != null) {
        // 递归左右子树
        postOrderTraverse(root.left);
        postOrderTraverse(root.right);
        // 处理当前节点
        // to do something
    }
}

// 方式二:使用栈
// 直接编写后序比较困难,这里先以根节点----->右节点----->左节点的顺序压栈,然后对其进行倒序
public void postOrderTraverse(TreeNode root) {
    Stack stack = new Stack<>();
    List list = new ArrayList<>(); // 进行辅助
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        if (node == null) continue;
        if (node.left != null) stack.push(node.left);
        if (root.right != null) stack.push(node.right);
        list.add(node);
    }
    Collections.reverse(list);
}

层次遍历

代码模板:

public void levelTraverse(TreeNode root) {
    // 层次遍历需要使用队列进行实现
    Deque deque = new LinkedList<>();
    // 初始化
    deque.addLast(root);
    // 进行遍历
    while (!deque.isEmpty()) {
        int size = deque.size();
        while (size-- > 0) {
            TreeNode node = deque.removeFirst();
            if (node == null) continue;
            // 处理当前节点
            // to do something
            deque.addLast(node.left);
            deque.addLast(node.right);
        }
    }
}

二叉树例子

[图片上传失败...(image-4face3-1578882824710)]

前序遍历:12457836
中序遍历:42758136
后序遍历:47852631
层次遍历:12345678

字典树Trie

概念

    字典树,即Trie树,又称为单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
    它的优点是:最大限度地减少无畏的字符串比较,查询效率比哈希表高。

代码模板

public class Trie {
    private TrieNode root; //根节点
    
    public Trie() {
        root = new TrieNode('');
    }
    
    public void insert(String word) {
        char[] array = word.toCharArray();
        TrieNode tmp = root;
        for (int i = 0; i < array.length; i++) {
            int index = array[i] - 'a';
            if (tmp.children[index] == null) tmp.children[index] = new TrieNode(array[i]);
            tmp = tmp.children[index];
        }
        tmp.isWord = true;
    }
    
    public boolean search(String word) {
        char[] array = word.toCharArray();
        TrieNode tmp = root;
        for (char c : array) {
            if (tmp.children[c - 'a'] == null) return false;
            tmp = tmp.children[c - 'a'];
        }
        return tmp.isWord;
    }
    
    public boolean startsWith(String prefix) {
        char[] array = prefix.toCharArray();
        TrieNode tmp = root;
        for (char c : array) {
            if (tmp.children[c - 'a'] == null) return false;
            tmp = tmp.children[c - 'a'];
        }
        return true;
    }
    
    /**
     * 使用数组实现
     */
    private class TrieNode {
        char val;
        boolean isWord;
        TrieNode[] children;
        
        TrieNode() {
            isWord = false;
            children = new TrieNode[26];
        }
        
        TrieNode(char c) {
            isWord = false;
            children = new TrieNode[26];
            val = c;
        }
    }
}

分治、回溯

DFS

代码模板

public void dfs(TreeNode node, Set visited) {
    if (visited.contains(node)) return;
    // 将节点添加到visited中
    visited.add(node)
    // do something
    dfs(node.child, visited);
}

BFS

代码模板

// BFS一般使用队列来存储数据
public void bfs(String begin, String end, List wordList) {
    Deque deque = new LinkedList<>();
    Set visited = new HashSet<>();
    // init
    deque.add(begin);
    while (!deque.isEmpty()) {
        int size = deque.size();
        while (size > 0) {
            String str = deque.removeFirst();
            if (!visited.contains(str)) {
                //do something
                visited.add(str);
                deque.addLast(str);
            }
            size--;
        }
    }
}

二分查找

代码模板

public int binarySearch(int[] array, int left, int right, int target) {
    while (left <= right) {
        int mid = (left + right) / 2;
        if (array[mid] == target) {
            // todo
            return mid;
        } else if (array[mid] < tartget) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
}

双向BFS

代码模板

public int doubleBfs(String start, String end, List wordList) {
    Set startSet = new HashSet<>();
    Set endSet = new HashSet<>();
    Set visited = new HashSet<>();
    int step = 0;
    //init
    startSet.add(start);
    endSet.add(end);
    //bfs
    while (!startSet.isEmpty() && !endSet.isEmpty()) {
        //优先扩散元素少的
        if (startSet.size() > endSet.size()) {
            Set tmpSet = startSet;
            startSet = endSet;
            endSet = tmpSet;
        }
        Set tmpSet = new HashSet<>();
        for (String s : startSet) {
            if (endSet.contains(s)) return step + 1;//相遇说明找到最短路径
            if (!visited.contains(s)) {
                //do something
                visited.add(s);
                tmpSet.add(s);
            }
        }
        startSet = tmpSet;
        step++;
    }
}

位运算

基础

含义 运算符 示例
左移 << 0011 => 0110
右移 >> 0011 => 0001
按位或 | 0011
--------=>1011
1011
按位与 & 0011
--------=>0011
1011
按位取反 ~ 0011 => 1100
按位异或(相同为零不同为一) ^ 0011
--------=>1000
1011

XOR-异或操作特点

  • x ^ 0 = x
  • x ^ 1s = ~x //注意 1s = ~0
  • x ^ (~x) = 1s
  • x ^ x = 0
  • c = a ^ b => a ^ c = b, b ^ c = a //交换两个数
  • a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c)

指定位置的位运算

  1. 将x最右边的n位清零:x & (~0 << n)
  2. 获取x的第n位值(0或者1):(x >> n) & 1
  3. 获取x的第n位的幂值:x & (1 << (n - 1))
  4. 仅将第n位置为1:x | (1 << n)
  5. 仅将第n位置为0:x & (~ (1 << n))
  6. 将x最高位至第n位(含)清零:x & ((1 << n) - 1)
  7. 将第n位至第0位(含)清零:x & (~ ((1 << (n + 1)) - 1))

位运算实战要点

  • 判断奇偶性:
    x % 2 == 1 --> (x & 1) == 1
    x % 2 == 0 --> (x & 1) == 0
  • x >> 1 --> x / 2
    即:x = x / 2; --> x = x >> 1;
    mid = (left + right) / 2; --> mid = (left + right) >> 1;
  • x = x & (x - 1) 清零最低位的1
  • x & -x => 得到最低位的1
  • x & ~x => 0

布隆过滤器

原理和实现

参考地址:https://www.cnblogs.com/cpselvis/p/6265825.html

主要运用

参考地址:https://blog.csdn.net/tianyaleixiaowu/article/details/74721877

  • 缓存击穿
  • 垃圾邮件识别
  • 集合判重

代码模板

参考地址:https://github.com/lovasoa/bloomfilter/blob/master/src/main/java/BloomFilter.java
参考地址:https://github.com/Baqend/Orestes-Bloomfilter

排序算法

参考链接

  1. 十大经典排序算法
  2. 快速排序代码示例
  3. 归并排序代码示例
  4. 堆排序代码示例

初级排序

  1. 选择排序(Selection Sort)

代码模板:

/**
 * 每次找最小值,然后放到待排序数组的起始位置
 * 时间复杂度:O(n^2)
 * @author 潘磊明
 * @date 2019/12/5
 */
public class SelectionSort {
    public void sort(int[] array) {
        for(int i = 0; i < array.length - 1; i++) {
            int min = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[min] > array[j]) min = j;
            }
            int tmp = array[i];
            array[i] = array[min];
            array[min] = tmp;
        }
    }
}
  1. 插入排序(Insertion Sort)

代码模板:

/**
 * 从前到后逐步构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
 * 时间复杂度:O(n^2)
 * @author 潘磊明
 * @date 2019/12/5
 */
public class InsertionSort {
    public void sort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int j = i - 1;
            while (j >= 0 && array[j] > array[j + 1]) {
                int tmp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = tmp;
                j--;
            }
        }
    }
}
  1. 冒泡排序(Bubble Sort)

代码模板:

/**
 * 嵌套循环,每次查看相邻的元素,如果逆序则交换
 * 时间复杂度:O(n^2)
 * @author 潘磊明
 * @date 2019/12/5
 */
public class BubbleSort {
    public void sort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                }
            }
        }
    }
}

你可能感兴趣的:(数据结构与算法)