FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

# 数据结构和算法 ## 数据结构 ## 数组 - 表现形式: ![](../image/数据结构_数组.png) ```java int[] array = {15, 85, 55, 99, 26} ``` ### 链表 - 表现形式: ![](../image/数据结构_链表.png) ```java // 双向链表 private static class Node { // 数据 E item; // 后继节点 Node next; // 前驱节点 Node prev; Node(Node prev, E element, Node next) { this.item = element; this.next = next; this.prev = prev; } } // 单向链表 private static class Node { // 数据 E item; // 后继节点 Node next; Node(E element, Node next) { this.item = element; this.next = next; } } ``` > 双向环形链表:最后一个节点的 next 指向 head 节点,head 节点的 prev 指向最后一个节点。 > > 在实现上,又有带哨兵的双向环形链表和不带哨兵的双向环形链表。区别在于,带哨兵的双向环形链表,额外用一个哨兵节点表示 head,即链表头。 ### 队列 queue 是以顺序的方式维护一组数据集合,在一端添加数据,从另一端移除数据。一般地,添加的一端称为**尾**,移除的一端称为**头**。(体现在先进先出) - 表现形式: ![](../image/数据结构_队列.png) 对于链表、数组的实现,额外引入两个指针,头指针、尾指针,当添加元素时,尾指针移动,当移除元素时,头指针移动。 ### 栈 只能在其一端添加和移除数据,一般地,添加数据的一端称为栈顶,另一端称为栈底。(体现在先进后出) 其实现方式有基于数组、链表。 ### 堆 堆是一种基于树的数据结构,通常用**完全二叉树**实现。其特性如下: - 最顶层的节点称为 root 根节点。 - 在大顶堆中,任意节点 C 与其父节点 P 符合 P.value >= C.value。 - 而小顶堆中,任意节点 C 与其父节点 P 符合 P.value 动态规划是一种数学规划的建模思想, 但本身却只蕴含了一个和暴力枚举差不多的基本算法. #### 斐波那契 ```java /** *
 * 前两项的和等于第三项(利用额外的参数缓存前两项的和,避免了像递归重复计算)
 *    F0    F1    F2    F3    F4    F5    F6    F7    F8    F9    F10    F11    F12    F13
 *     0     1     1     2     3     5     8    13    21    34     55     89    144    233
 * @param n 项
 * @return
 */
public int fibonacci(int n) {
    if (n == 0) {
        return n;
    }

    if (n == 1) {
        return n;
    }

    int a = 0;
    int b = 1;
    for (int i = 2; i = item0.weight) {
            dp[j] = item0.value;
        }
    }

    for (int i = 1; i < items.length; i++) {
        for (int j = capacity; j > 0; j--) {
            // 装得下
            if (j >= items[i].weight) {
                dp[j] = Integer.max(dp[j], items[i].value + dp[j - items[i].weight]);
            }
        }
    }
    return dp[capacity];
}

/**物品*/
static class Item {
    /**编号*/
    private int index;
    /**物品名称*/
    private String name;
    /**重量(g)*/
    private int weight;
    /**价值*/
    private int value;

    public Item(int index, String name, int weight, int value) {
        this.index = index;
        this.name = name;
        this.weight = weight;
        this.value = value;
    }
}
```

#### 完全背包问题

`完全背包问题` 和 `01背包问题` 两者大同小异,唯一的区别就是:

- `01背包问题` 每件物品只有一个,即只能取一次。

- 而 `完全背包问题` 每件物品可以重复取。

```java
/**
 * 
 1. 假定背包容量为10g,要求取走不超过背包容量的物品
 2. 每次可以不拿或全拿,每件物品可以重复取,问最高价值是多少?

    编号   重量(g)   价值(元)
    1      2        3     青铜(c)
    2      3        4     白银(s)
    3      4        5     黄金(a)

    0   1   2   3   4    5    6
 1  0   0   c   c   cc   cc   ccc      青铜
 2  0   0   c   s   s    sc   ss       白银
 3  0   0   c   s   a    a    ac       黄金

 其递推公式为:
 if (装不下) {
    dp[i][j] = dp[i-1][j]
 } else { 装得下
    // 当前背包最大价值 = max(上一次最大价值, 当前物品价值 + 放入当前物品之前的背包容量最大价值)
    dp[i][j] = max(dp[i-1][j], item.value + dp[i-1][j-item.weight])
 }
 *
 * @param items    物品数组
 * @param capacity 容量
 * @return 最大价值
 */
public int select(Item[] items, int capacity) {
    int [] dp = new int[capacity + 1];
    Item item0 = items[0];
    // 特殊处理第0行数据
    for (int j = 0; j < capacity + 1; j++) {
        // 背包容量装得下第0行物品
        if (j >= item0.weight) {
            dp[j] = dp[j - item0.weight] + item0.value;
        }
    }

    for (int i = 1; i < items.length; i++) {
        for (int j = capacity; j > 0; j--) {
            // 装得下
            if (j >= items[i].weight) {
                dp[j] = Integer.max(dp[j], items[i].value + dp[j - items[i].weight]);
            }
        }
    }
    return dp[capacity];
}

static class Item {
    /**编号*/
    private int index;
    /**物品名称*/
    private String name;
    /**重量(g)*/
    private int weight;
    /**价值*/
    private int value;

    public Item(int index, String name, int weight, int value) {
        this.index = index;
        this.name = name;
        this.weight = weight;
        this.value = value;
    }
}
```

#### 零钱兑换问题--最少组成

```java
/**
  面值   0   1   2   3     4     5
    1   0   1   11  111   1111  11111
    2   0   1   2   21    22    221
    5   0   1   2   21    22    1

 总金额    -   类比为背包容量
 硬币面值  -   类比为物品重量
 硬币个数  -   类比为物品价值,固定为1,(因为是求最少组成总金额的硬币数量)

 递推公式如下:
 if (装得下) {
     上次硬币组成个数,   剩余容量能装下的最小个数+1  (个数固定为1)
    dp[j] = Integer.min(dp[j], dp[j - coins[i]] + 1);
    // dp[i][j] = max(dp[i-1][j], dp[i-1][j-item.weight] + 1)
 } else {
    保留上次个数不变
    dp[i][j] = dp[i-1][j]
 }

 * @param coins  面值种类
 * @param amount 总金额
 * @return 最少组成总金额的硬币数量
 */
public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    // 特殊处理第0行数据,可以用Arrays.fill进行优化,赋初始值
    Arrays.fill(dp, amount + 1);
    dp[0] = 0;

    for (int coin : coins) {
        for (int j = coin; j < amount + 1; j++) {
            dp[j] = Integer.min(dp[j], dp[j - coin] + 1);
        }
    }
    return dp[amount] < amount ? dp[amount] : -1;
}
```

#### 零钱兑换问题--多少种组合

```java
/**
 
 面值     0       1       2       3       4       5   (总金额 - 类比为背包容量)
   1     1       1       11      111     1111    11111

   2     1       1       11      111     1111    11111
                         2       21      211     2111
                                         22      221

   5     1       1       11      111     1111    11111
                         2       21      211     2111
                                         22      221
                                                 5
    i: 币种,j: 列(总金额)
                                                   上一次组合种类:1 + 剩余容量的组合种类
      eg: 总金额为3,面值有1、2,共有多少种组合: dp[i][j] = dp[i-1][j] + dp[i][j-coin]

 if (放得下) {
    dp[i][j] = dp[i-1][j] + dp[i][j-coin]
 } else { 放不下
    dp[i][j] = dp[i-1][j]
 }

 * @param coins     面值种类
 * @param amount    总金额
 * @return 多少种组合
 */
public int change(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    dp[0] = 1;
    for (int j = 1; j < amount + 1; j++) {
        if (j >= coins[0]) {
            dp[j] = dp[j - coins[0]];
        }
    }
    for (int i = 1; i < coins.length; i++) {
        for (int j = 1; j < amount + 1; j++) {
            // 容量放得下
            if (j >= coins[i]) {
                dp[j] = dp[j] + dp[j - coins[i]];
            }
        }
    }
    return dp[amount];
}
```

#### 钢条切割问题

```java
/**
 
 钢条切割问题:怎么个切法能够构成最大价值.

 钢条长度: 0    1   2   3   4   5   6   7   8   9   10
 钢条价值: 0    1   5   8   9   10  17  17  20  24  30

 假定钢条长3m:
    0   1   2   3   4
 1      1   11  111 1111
 价值:  1   2   3   4

 2      1   11  111 1111
            2   21  211
                    22
 价值:  1   5   6   10

 3      1   11  111 1111
            2   21  211
                3   22
                    31
 ... ...

 if (放得下) {
                 上一次最大价值 ,   当前物品价值 + 剩余容量能装下的最大价值
    dp[i][j] = max(dp[i-1][j], 当前物品价值 + dp[i][j-物品重量])
 } else { 放不下
    dp[i][j] = dp[i-1][j]
 }

 * @param values 价值数组 - 钢条长度(物品重量)
 *               钢条长度: 0    1   2   3   4   5   6   7   8   9   10   (索引)
 *               钢条价值: 0    1   5   8   9   10  17  17  20  24  30
 * @param n 钢条长度
 * @return 最大价值
 */
public int cut(int[] values, int n) {
    int[] dp = new int[n + 1];
    for (int i = 1; i < values.length; i++) {
        for (int j = 1; j < n + 1; j++) {
            if (j >= i) {
                dp[j] = Integer.max(dp[j], values[i] + dp[j - i]);
            }
        }
    }
    return dp[n];
}
```

#### 最长公共子串

```java
/**

 最长公共字串:abcdeoma  opcdeima  则两个字符串的最长公共字串为3,(连续的子串)

    b   c   d   e   i   m   a
 c  0   1   0   0   0   0   0
 d  0   0   2   0   0   0   0
 e  0   0   0   3   0   0   0
 o  0   0   0   0   0   0   0
 m  0   0   0   0   0   1   0
 a  0   0   0   0   0   0   2

 if (字符相同) {
    dp[i][j] = dp[i-1][j-1] + 1
 } else {
    dp[i][j] = 0
 }

 * @param a 串A
 * @param b 串B
 * @return 最长公共字串
 */
public int lcs(String a, String b) {
    int[][] dp = new int[b.length()][a.length()];
    int max = 0;
    for (int i = 0; i < b.length(); i++) {
        for (int j = 0; j < a.length(); j++) {
            if (a.charAt(j) == b.charAt(i)) {
                // 特殊处理0行或0列,如果为0行或0列,则dp[i - 1][j - 1] 索引为负数
                if (i == 0 || j == 0) {
                    dp[i][j] = 1;
                } else {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                max = Integer.max(max, dp[i][j]);
            } else {
                dp[i][j] = 0;
            }
        }
    }
    return max;
}
```

#### 最长公共子序列

    

Back | FazBrowse Home | New Git URL