算法刷题[二]

Trie

  • 前缀树, 是一种多叉树数据结构, 与普通的树不同,它的每个节点没有具体的数值. 而是维护一个链表. 比如26字母的前缀树, 就是一个26个元素的字符数组. a-z映射到0-25, 每个元素存放的是下一个节点的地址.
  • 下面构造中, 如果判断Trie中有某个单词, 是以最后一个字母的下一个节点的is_end值判断的.会有一个延迟. 构造时导致的.
struct TrieNode{
    bool is_end;
    TrieNode* links[26];
    TrieNode(){
        is_end = false;
        memset(links, 0, sizeof(links));
    }
};
class Trie {
public:
    TrieNode* root;
    /** Initialize your data structure here. */
    Trie() {
       root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode* node = root;
        for (char c : word) {
            if (node->links[c - 'a'] == NULL)
                node->links[c - 'a'] = new TrieNode();
            node = node->links[c - 'a'];
        }
        node->is_end = true; // 实际上是结尾的下一个节点, 来判断当前节点是否结尾, 有一个节点的延后
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode* node = root;
        for(char c : word) {
            if(node->links[c - 'a'] == NULL)
                return false;
            node = node->links[c - 'a'];
       }
       return node->is_end;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        TrieNode* node = root;
        for(char c : prefix) {
            if (node->links[c - 'a'] == NULL)
                return false;
            node = node->links[c - 'a'];
       }
       return true;
    }
};

上下左右移动

int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};     // 左,右,上,下移动,例如:{0, -1}代表向左移动一步
int x = start.x + dir[i][0];
int y = start.y + dir[i][1];

地下迷宫

/**
 * 小青蛙有一天不小心落入了一个地下迷宫,
 * 小青蛙希望用自己仅剩的体力值P跳出这个地下迷宫。
 * 为了让问题简单,假设这是一个n*m的格子迷宫,
 * 迷宫每个位置为0或者1,0代表这个位置有障碍物,
 * 小青蛙达到不了这个位置;1代表小青蛙可以达到的位置。
 * 小青蛙初始在(0,0)位置,地下迷宫的出口在(0,m-1)(保证这两个位置都是1,并且保证一定有起点到终点可达的路径),
 * 小青蛙在迷宫中水平移动一个单位距离需要消耗1点体力值,
 * 向上爬一个单位距离需要消耗3个单位的体力值,
 * 向下移动不消耗体力值,当小青蛙的体力值等于0的时候还没有到达出口,
 * 小青蛙将无法逃离迷宫。现在需要你帮助小青蛙计算出能否用仅剩的体力值跳出迷宫(即达到(0,m-1)位置)。
 */
#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <algorithm>
#include <limits.h>
using namespace std;

class Point{
public:
    int x;
    int y;
    Point(int x, int y): x(x), y(y) {};
    
    bool operator== (const Point& p) {
        return (this->x == p.x) && (this->y == p.y);
    }
};

class Cmp{
public:
    bool operator()(const Point& p1, const Point& p2) const{
        if (p1.x == p2.x)
            return p1.y < p2.y;
        return p1.x < p2.x;
    }
};

vector<Point> res;
int max_p = INT_MIN;
int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
int cost[4] = {-1, -1, -3, 0};
void backtrack(vector<vector<int>> v, vector<Point>& select, int p, Point start , Point target) {
    if (select[select.size() - 1] == target && p >= 0) {
        if (p > max_p) {
            res = select;
            max_p = p;
        }
        return ;
    }
    if (p < 0)
        return ;
    for (int i = 0; i < 4; i++) {
        int x = start.x + dir[i][0];
        int y = start.y + dir[i][1];
        Point point(x, y);
        if (x < 0 || x >= v.size() || y < 0 || y >= v[0].size() || v[x][y] != 1 || find(select.begin(), select.end(), point) != select.end())
            continue;
        select.push_back(point);
        backtrack(v, select, p + cost[i], point, target);
        select.pop_back();
    }
    
}

void solution(vector<Point> & select) {
    int n, m, p;
    cin>>n>>m>>p;
    vector<vector<int>> map(n, vector<int>(m, 0));
    for(int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int val;
            cin>>val;
            map[i][j] = val;
        }
    }
    select.push_back(Point(0,0));
    backtrack(map, select, p, Point(0,0), Point(0, m-1));
}
int main() {
    vector<Point> s;
    solution(s);
    if (res.size() == 0)
        cout<<"Can not escape!"<<endl;
    else {
        for (int i = 0; i < res.size() - 1; i++) {
            cout<<"["<<res[i].x<<","<<res[i].y<<"],";
        }
        cout<<"["<<res[res.size() - 1].x<<","<<res[res.size() - 1].y<<"]";
    }
    return 0;
}

/*输入一个正整数n,求n!(即阶乘)末尾有多少个0? 比如: n = 10; n! = 3628800,所以答案为2*/
#include <iostream>
using namespace std;

int main(){
    int n;
    cin>>n;
    int count = 0;
    while(n/=5) {
        count += n;
    }
    cout<<count<<endl;
}
/*
解释:比如100/5=20,即有20个数包含因数5:5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100。这20个数中又有20/5=4个包含因数5:25,50,75,100(可以把前面的20个数看成5*1,5*2,5*3,5*4,5*5,5*6,5*7,5*8,5*9,5*10,,5*11,5,12,5*13,5*14,5*15,5*16,5*17,5*18,5*19,5*20),然后这4个数中的因数5又被计算过了,即可。
*/

vector中的最值

#include <vector>
#include <algorithm>
#include <iostream>
 
vector<int>::iterator max_index = max_element(v.begin(), v.end());
vector<int>::iterator min_index = min_element(v.begin(), v.end());
distance(v.begin(), min_index); // 获得两个iterator的距离 (相减)

二分查找

/*
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
*/
int search(vector<int>& nums, int target) {
   int l = 0, r = nums.size() - 1;
   // 如果没有= 一个元素的时候会出错
   while(l <= r) {
       int mid = (l + r) / 2;
       if (target < nums[mid]) {
           r = mid - 1;
       } else if (target > nums[mid]) {
           l = mid + 1;
       } else if (target == nums[mid]) {
           return mid;
       }
   }
   return -1;
}

/*
请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
*/

int upper_bound_(int n, int v, vector<int>& a) {
	 // write code here
	 int l = 0, r = n - 1;
	 int min = n;
	 while(l <= r) {
	     int mid = (r + l) / 2;
	     if (a[mid] < v) {
	         l = mid + 1;
	     } else if (a[mid] >= v) {
	         r = mid - 1;
	         if (mid < min)
	             min = mid;
	     }
	 }
	 return (min + 1);
	}

LRU

class LRUCache {
public:
    unordered_map<int, list<pair<int, int>>::iterator> mp;
    list<pair<int, int>> cache;
    int cap;
    LRUCache(int capacity) {
        cap = capacity;
    }
    
    int get(int key) {
        auto it = mp.find(key);
        if (it == mp.end())
            return -1;
        auto target = it->second;
        int val = target->second;
        put(key, val);
        return val;
    }
    
    void put(int key, int value) {
        auto it = mp.find(key);
        // 无论存不存在, 都需要删除,在头部插入一个
        if (it != mp.end()) {
            cache.erase(it->second);
            mp.erase(key);
        }
        pair<int, int> p = make_pair(key, value);
        cache.push_front(p);
        // map insert函数插入重复不会覆盖. []插入可以覆盖
        mp[key] = cache.begin();
        if (cache.size() > cap) {
            mp.erase(cache.back().first);
            cache.pop_back();
        }
    }
};

set 插入

set容器插入insert函数会返回一个pair, 
第一个元素是插入的值的iter, (如果插入成功指向新元素, 插入失败指向已存在元素)
第二个元素是bool, 表示是否插入成功

set<int> s;
s.insert(1);
auto i = s.insert(1);
if (i.second)
    cout<<"yes"<<endl;
else
    cout<<"no"<<endl;
cout<<*(i.first)<<endl;

no
1
    

双指针

// 判断链表是否有环, 空间复杂的O(1), 双指针
    bool hasCycle(ListNode *head) {
        if(head == NULL)
            return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast) {
            if (fast == NULL || fast->next == NULL)
                return false;
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }


// 可以用set, 但是空间复杂的O(n)
	bool hasCycle(ListNode *head) {
        set<ListNode*> s;
        while(head != NULL) {
            auto res = s.insert(head);
            head = head->next;
            if (!res.second)
                return true;
        }
        return false;
    }

合并链表

/*
1. 使用伪头节点
2. 使用三元表达式, 完美解决了输入为空的情况
3. 尾插法, 需要一个额外的头指针, 用于最后的输出. 
*/

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* root = new ListNode(0);
        ListNode* cur = root;
        while (l1 && l2) {
            if (l1->val < l2->val) {
                cur->next = l1;
                l1 = l1->next;
            }
            else {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        cur->next = l1 != NULL ?l1:l2;
        return root->next;
        
    }
};

数组转容器

// size_t 无符号整数  精度是整形的双倍
int arr[] = {1,3,6,9,2,1,4};
size_t sz = sizeof(arr) / sizeof(int);
vector<int> v(arr, arr + sz);

vector<int> v;
v.assign(begin(arr), end(arr));

原码, 反码, 补码

原码, 反码, 补码 详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值