算法知识


判断链表是否有环

使用快慢指针,当他们相遇时,则有环

bool IsExitsLoop(slist *head)
{
    slist *slow = head, *fast = head;

    while ( fast && fast->next ) 
    {
        slow = slow->next;
        fast = fast->next->next;
        if ( slow == fast ) break;
    }

    return !(fast == NULL || fast->next == NULL);
}

找到环的入口

参考博客地址:http://www.cppblog.com/humanchao/archive/2012/11/12/47357.html

公式推导:

快指针经过的路径:2s

慢指针经过的路径:s

nr:走了n圈

假设真个链表长度为L,入环口与相遇点距离为x,起点到入环口的距离为a

则有:

2s = s + nr
s = nr


a + x = nr
a + x = (n - 1)r + r = (n-1)r + L - a
a = (n - 1)r + (L - a - x)

由公式推导可知,只需要我们让两个指针分别指向起点和相遇的点,每次分别next走一步,则一定会在入环点相遇。

求环的大小

只需让这两个快慢指针,继续在环里面走,再次相遇时,所走的步数 = 环的长度

背包问题

01背包求最大值

x轴代表bag的容量提升,y轴代表不同物体的序号

物品定义:

int w[5] = { 0 , 2 , 3 , 4 , 5 };            //商品的体积2、3、4、5
int v[5] = { 0 , 3 , 4 , 5 , 6 };            //商品的价值3、4、5、6
int bagV = 8;                            //背包大小

状态转移方程:

for (int i = 1; i <= 4; i++) {
        for (int j = 1; j <= bagV; j++) {
            if (j < w[i])
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
        }
    }

完全背包问题求最大值

而完全背包问题只需在01背包上做延伸就可以了,即原来的状态转移方程式改为:

if (j < w[i])
    dp[i][j] = dp[i - 1][j];
else
    dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);

i的原因

因为完全背包,即物品可以重复叠加,即我们应该从本行中提取,不断更新本行的数据

背包问题之装满背包的方案总数

(详情可以查看以下博主的讲解 https://blog.csdn.net/wumuzi520/article/details/7021210)

对于01背包,设置初始值

F[0][0] = 1
F[1~n][0] = 1
F[0][1~m] = 0

状态转移方程式

然后就可以计算填满当前(每一列 j 都是对应不同的背包容量时的数量)背包容量时的总数

而完全背包的状态转移方程是则应该为

原理和原来求最大值是一样的

在leetcode上总结的背包问题分类:

组合问题:

dp[i] += dp[i-num]

True、False问题

dp[i] = dp[i] or dp[i-num]

最大最小问题

dp[i] = min(dp[i], dp[i-num]+1)或者dp[i] = max(dp[i], dp[i-num]+1)

然后再考虑是01 or 完全背包,是否要考虑顺序

假设target为范围、容量大小,数组nums为物品,则有

  • 如果求组合数就是nums放在外循环,target在内循环

  • 如果求排列数就是外层for遍历背包,内层for循环遍历物品。

异或

异或的性质

  • 两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是
    如果同一位的数字相同则为 0,不同则为 1

异或的规律

  • 任何数和本身异或则为 0
  • 任何数和 0 异或是 本身
  • 异或满足交换律。 即 a ^ b ^ c ,等价于 a ^ c ^ b

所以它可用于直接解决:一个数字出现一次,其他都出现了两次,让我们找到出现一次的数 的问题

  • 时间复杂度:O(N),其中N为数组长度。
  • 空间复杂度:O(1)

LRU算法

LRU是Least Recently Used的缩写,也被称为最近最少使用算法,淘汰掉最近很少被使用的数据

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
    this.limit = capacity
    this.arr = []
    this.map = new Map()
};

/** 
 * @param {number} key
 * @return {number}
 */
LRUCache.prototype.get = function(key) {
    if(!this.map.has(key))return -1
    const index = this.arr.indexOf(key)
    for(let i = index; i > 0; i--) {
        this.arr[i] = this.arr[i-1]
    }
    this.arr[0] = key
    return this.map.get(key)
};

/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LRUCache.prototype.put = function(key, value) {
    this.map.set(key, value)
    const index = this.arr.indexOf(key)
    if(index !== -1){
        this.arr.splice(index, 1)
    }
    this.arr.unshift(key)
    if(this.arr.length > this.limit){
        const pop = this.arr.pop()
        this.map.delete(pop)
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * var obj = new LRUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */

如果是利用迭代器的特性,还可以这样用

LRUCache.prototype.get = function (key) {
    const cache = this.cache
    if (cache.has(key)) {
        const value = cache.get(key)
        cache.delete(key)
        cache.set(key, value)
        return value
    } else {
        return -1
    }
};
LRUCache.prototype.put = function (key, value) {
    const cache = this.cache
    if (cache.has(key)) cache.delete(key)
    if (cache.size === this.max) cache.delete(cache.keys().next().value)
    cache.set(key, value)
};

LFU算法

Least Frequently Used

也被称为最近最少访问频率,他有两个维度,一个是访问频率,一个是时间最近

实质上就是添加多个一哈希表用于记录访问频率

var LFUCache = function(capacity) {
  this.size = capacity
  this.valueMap = new Map()     // 记录值
  this.useMap = new Map()       // 记录使用次数
};

LFUCache.prototype.get = function(key) {
  if (this.valueMap.has(key)){       // 当存在时 删掉原来的重新添加  使用值加1
    let use = this.useMap.get(key)
    let value = this.valueMap.get(key)
    this.valueMap.delete(key)
    this.useMap.set(key, use + 1)
    this.valueMap.set(key, value)
    return value
  } else {
    return -1
  }
};

LFUCache.prototype.put = function(key, value) {
  if(this.size === 0) return
  let min = Math.min(...this.useMap.values())  // 缓存下 最小使用值
  if (this.valueMap.has(key)) {                // 如果存在 值重新赋, use加一
    this.valueMap.set(key, value)
    let use = this.useMap.get(key)
    this.useMap.set(key, use + 1)
  } else {                                     // 不存在就直接加
    this.valueMap.set(key, value)
    this.useMap.set(key, 1)
  }
  // 当超出, 删掉不常用的 当碰到用的次数相同的删掉 较前未使用的
  if(this.valueMap.size > this.size){
    let it = this.valueMap.keys()              // 缓存 key 遍历器
    let delKey = it.next().value
    while(this.useMap.get(delKey) !== min){    // 获取使用值为 min 的key
      delKey = it.next().value
    }
    this.useMap.delete(delKey)                 // 删掉该项
    this.valueMap.delete(delKey)
  }
};

文章作者: Hello
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hello !
 上一篇
MongoDB MongoDB
1.MongoDB概述MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。 mongoDB不需要学习sql语句 NoSQL,指的是非关系型的数据库。NoSQL有时也
2022-02-26
下一篇 
红宝书啃读note(下) 红宝书啃读note(下)
第12章BOMBOM核心是window对象,window对象在浏览器有双重身份,一个是ECMAScript的Global对象,一个就是浏览器窗口的JavaScript接口。 窗口关系 top对象始终指向最上层窗口,即浏览器窗口本身 pare
2021-12-20
  目录