数据结构与算法

目录

  • <a href="#base">时间复杂度和空间复杂度分析</a>
  • <a href="#ArrayLinkedList">数组、链表、跳表的基本实现和特性</a>
  • <a href="#StackQueue">栈、队列、优先队列、双端队列</a>
  • <a href="#Hash">哈希表、映射、集合</a>
  • <a href="#tree">树</a>
    • <a href="#binaryTreeIteration">二叉树的遍历</a>
    • <a href="#binarySearchTree">二叉搜索树</a>
    • <a href="#trie">字典树Trie</a>
  • <a href="#divide">分治、回溯</a>
  • <a href="#dfs">DFS(深度优先遍历)</a>
  • <a href="#bfs">BFS(广度优先遍历)</a>
  • <a href="#double">二分查找</a>
  • <a href="#tow-bfs">双向BFS</a>
  • <a href="#bit">位运算</a>
  • <a href="#bloom">布隆过滤器</a>
  • <a href="#sort">排序算法</a>

<a id="base" name="base"/>

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

参考链接:

<a id="ArrayLinkedList" name="ArrayLinkedList"/>

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

参考链接:

<a id="StackQueue" name="StackQueue"/>

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

参考链接:

<a id="Hash" name="Hash"/>

哈希表、映射、集合

参考链接:

<a id="tree" name="tree" />

<a id="binaryTreeIteration" name="binaryTreeIteration"/>

二叉树的遍历

前序遍历

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

代码模板:

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

// 方式二:使用栈存放节点
public void preOrderTraverse(TreeNode root) {
    Stack<TreeNode> 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<TreeNode> 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<TreeNode> stack = new Stack<>();
    List<TreeNode> 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<TreeNode> 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

<a id="trie" name="trie" />

字典树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;
        }
    }
}

<a id="divide" name="divide"/>

分治、回溯

<a id="dfs" name="dfs"/>

DFS

代码模板

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

<a id="bfs" name="bfs"/>

BFS

代码模板

// BFS一般使用队列来存储数据
public void bfs(String begin, String end, List<String> wordList) {
    Deque<String> deque = new LinkedList<>();
    Set<String> 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--;
        }
    }
}

<a id="double" name="double"/>

二分查找

代码模板

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;
        }
    }
}

<a id="tow-bfs" name="two-bfs"/>

双向BFS

代码模板

public int doubleBfs(String start, String end, List<String> wordList) {
    Set<String> startSet = new HashSet<>();
    Set<String> endSet = new HashSet<>();
    Set<String> 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<String> tmpSet = startSet;
            startSet = endSet;
            endSet = tmpSet;
        }
        Set<String> 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++;
    }
}

<a id="bit" name="bit"/>

位运算

基础

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

<a id="questions" name="questions"/>

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

<a id="bloom" name="bloom"/>

布隆过滤器

原理和实现

参考地址: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

<a id="sort" name="sort"/>

排序算法

参考链接

  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;
                }
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容