本篇内容是我为了准备算法面试,看了一遍 labuladong 写的算法小抄,做的笔记。

虽然市面上讲算法的书不少,主流的算法4,数据结构与算法分析,还有针对面试的剑指offer,啊哈算法,图解算法,左程云算法指南,还有网上的一些数据结构和算法视频,比如浙大数据结构课程等。

但是根据当时我准备时间极其有限,一到两周。以及我翻过这几本书后,我觉得 labuladong 的书最适合我,原因有二。

第一,labuladong 战略上藐视算法,认为面向面试的算法很简单,有套路,这给了我一种信心,因为我在此之前除了学了一点 C语言数据结构之外,对算法没什么系统的概念,只觉得好像有点难。根据我此后一两周的学习,也确实很快掌握了一些算法题的套路,从感觉上没那么害怕算法了,甚至有点享受算法。

第二,labuladong 这本书虽然运用的语言比较多样,cpp,Python,JAVA 都有,但是都比较容易转为 JAVA,这就对我来说比较友好,我刚转 JAVA,最好还是 JAVA语言比较熟悉。剑指offer 也挺好,但是全都是 cpp,看起来也有点复杂。

1. 数据结构的存储方式

2. 数据结构基本操作——遍历+访问

线性

​ for/while

void traverse(int[] arr) {           for (int i = 0; i < arr.length; i++) { // 迭代访问 arr[i] }
}

非线性

​ 递归

链表遍历框架

兼具迭代和递归结构:

class ListNode{int val;ListNode next;
}void traverse(ListNode head){for(ListNode p=head; p!=null; p=p.next;){//迭代访问 p.val}
}void traverse(ListNode head){//递归访问traverse(head.next);
}

二叉树遍历框架

典型非线性递归遍历结构:

class TreeNode{int val;TreeNode left, right;
}void traverse(TreeNode root){// 前序遍历traverse(root.left);//中序遍历traverse(root.right);//后序遍历
}

N 叉树遍历框架

class TreeNode{int val;TreeNode[] children;
}void traverse(TreeNode root){for(TreeNode child : root.children){traverse(child)}
}

N叉树遍历可以拓展为图的遍历。
用 boolean 数组visited 标记访问过的顶点。

3. 算法刷题指南

数据结构是工具,算法是通过合适的工具解决特定问题的方法。
要了解常用数据结构的特性和缺陷。

先刷二叉树。
因为二叉树最容易培养框架思维,大部分算法技巧,本质都是树的遍历问题。

所有二叉树问题都用这个框架

void traverse(TreeNode root) { // 前序遍历 traverse(root.left) // 中序遍历 traverse(root.right) // 后序遍历
}

LeetCode 124 题,难度 Hard,让你求二叉树中最大路径和,主要代码如下:

  • 本质就是后序遍历
class Solution {int ans = Integer.MIN_VALUE;public int maxPathSum(TreeNode root) {oneSideMax(root);return ans;}private int oneSideMax(TreeNode root) {if (root == null) {return 0;}int left = Math.max(oneSideMax(root.left), 0);int right = Math.max(oneSideMax(root.right), 0);ans = Math.max(root.val + left + right, ans); //在这里获得路径和return root.val + Math.max(left, right); //返回最大边即可}
}

LeetCode 105 题,重建二叉树

  • 不要看这个函数的参数很多,只是为了控制数组索引而已,本质上该算法也就是一个前序遍历。
TreeNode buildTree(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd, Map<Integer, Integer> inMap) {if(preStart > preEnd || inStart > inEnd) {return null;}TreeNode root = new TreeNode(preorder[preStart]); int inRoot = inMap.get(root.val);int numsLeft = inRoot - inStart; root.left = buildTree(preorder, preStart + 1, preStart + numsLeft ,inorder, inStart, inRoot + 1, inMap)root.right = buildTree(preorder, preStart + numsLeft + 1, preEnd, inorder, inRoot + 1, inEnd, inMap);return root;
}

[没看懂]
LeetCode 99 题,难度 Hard,恢复一棵 BST(二叉搜索树),主要代码如下:
https://leetcode-cn.com/problems/recover-binary-search-tree/

void traverse(TreeNode* node) { if (!node) return; traverse(node->left);if (node->val < prev->val) { s = (s == NULL) ? prev : s;t = node; } prev = node; traverse(node->right);
}

–就是中序遍历,对于一个 BST 中序遍历意味着什么?【从小到大输出?】
注:对于 BST 树,左子树所有结点值 < 根节点 < 右子树所有值

先刷树

刷10道有点难受,结合框架做20道就有点自己的理解,刷完整个专题,再去做回溯、动态规划、分治专题,会发现只要涉及递归,都是树的问题。

动态规划详解说过凑零钱问题,暴力解法就是遍历一棵 N 叉树:

def coinChange(coins: List[int], amount: int):def dp(n):if n == 0: return 0 if n < 0: return -1 res = float('INF')for coin in coins:subproblem = dp(n - coin) # 子问题无解,跳过if subproblem == -1: continue res = min(res, 1 + subproblem) return res if res != float('INF') else -1 return dp(amount)

提取框架,看出核心思路

不过是一个 N 叉树的遍历问题而已

def dp(n): for coin in coins: dp(n - coin)

很多动态规划问题就是在遍历一棵树,你如果对树的遍历操作烂熟于心,起码知道怎么把思路转化成代码,也知道如何提取别人解法的核心思路。

回溯算法就是个 N 叉树的前后序遍历问题,没有例外
比如 N 皇后问题吧,主要代码如下:

void backtrack(int[] nums, LinkedList<Integer> track) { if (track.size() == nums.length) { res.add(new LinkedList(track)); return; }
for (int i = 0; i < nums.length; i++) { if (track.contains(nums[i])) continue; track.add(nums[i]);    // 进入下一层决策树 backtrack(nums, track); track.removeLast();
}

/* 提取出 N 叉树遍历框架 */

void backtrack(int[] nums, LinkedList<Integer> track) { for (int i = 0; i < nums.length; i++) { backtrack(nums, track);
}

算法4——扫了一眼,好书,java 描述的,留着对照看,以后要过一遍
二分图——代替反向索引
间隔度数计算——BFS 广度优先搜索寻找最短路径问题
套汇问题——bellman-ford 算法,寻找负权重环

4. 动态规划框架

​ 动规问题一般形式就是求最值
​ 比如求最长递增子序列,最小编辑距离
​ 求解动态规划核心问题是穷举
​ 动规穷举特点——存在重叠子问题
​ 暴力穷举效率低下,需要备忘录或 DP table 优化穷举过程。【剪枝?】
​ 动规问题一定具备最优子结构,才能通过子问题的最值得到原问题最值。
​ 列出正确的「状态转移方程」才能正确地穷举

​ 【差解法】双 for 暴力循环时间 On^2
​ 【好解法】备忘录或 dp,时间 On

​ 动规三要素
​ 重叠子问题
​ 最优子结构
​ 状态转移方程

​ 思考框架:
​ 1.明确「状态」
​ 2.定义dp 数组/函数的含义
​ 3.明确「选择」
​ 4.明确 base case

​ 题目列表
​ 1.斐波那契数列——重叠子问题
​ 2.凑零钱——最优子结构
​ 3.求二叉树最大值
​ 4.编辑距离
​ 5.最长递增子列

斐波那契数列问题和凑零钱问题来详解动态规划的基本原理。
前者主要是让你明白什么是重叠子问题(斐波那契数列严格来说不是动态规划问题),
后者主要举集中于如何列出状态转移方程

但凡遇到需要递归的问题,最好都画出递归树,这对你分析算法的复 杂度,寻找算法低效的原因都有巨大帮助 。

1. 斐波那契数列暴力递归

int fib(int N) {if (N == 1 || N == 2) return 1; return fib(N - 1) + fib(N - 2);
}

递归算法时间复杂度 = 子问题个数*解决一个子问题需要的时间
子问题个数=递归树中结点总数
二叉树结点总数是指数级别,子问题个数 O2^n
解决一个子问题时间,没有循环,只有 f(n-1)+f(n-2)一个加法操作,时间 O1
所以这个算法时间负责度 O2^n

2. 带备忘录的递归解法

​ 先去备忘录里查一下,有就直接返回,没有再递归解决

int fib(int N) {if (N < 1) return 0; //备忘录全初始化为 0 vector<int> memo(N + 1, 0); //初始化最简情况return helper(memo, N);
}
int helper(vector<int>& memo, int n) {//base caseif (n == 1 || n == 2) return 1; //已经计算过if(memo[n] != 0) return memo[n]; memo[n] = helper(memo, n - 1) + helper(memo, n - 2); return memo[n];
}

3. dp 数组的迭代解法

int fib(int N) {vector<int> dp(N + 1, 0);// base casedp[1] = dp[2] = 1;for (int i = 3; i <= N; i++)dp[i] = dp[i - 1] + dp[i - 2]; return dp[N];
}

根据斐波那契数列 的状态转移方程,当前状态只和之前的两个状态有关,其实并不需要那么⻓ 的一个 DP table 来存储所有的状态,只要想办法存储之前的两个状态就行 了。所以,可以进一步优化,把空间复杂度降为 O(1):

int fib(int n) {if (n == 2 || n == 1)return 1;int prev = 1, curr = 1;for (int i = 3; i <= n; i++) {int sum = prev + curr;prev = curr;curr = sum;} return curr;
}

斐波那契数列——重叠子问题
优化后,空间复杂度降到 O1
当前状态=前两个状态相加
华为机试第5题 《跳台阶1/3》 就是这样做的

322.凑零钱——如何列出动态转移方程——最优子结构
base case
状态转移方程
上面这两个应该是最优子结构了
https://leetcode-cn.com/problems/coin-change/solution/322-ling-qian-dui-huan-by-leetcode-solution/

public class Solution {public int coinChange(int[] coins, int amount) {int max = amount + 1;int[] dp = new int[amount + 1];Arrays.fill(dp, max);dp[0] = 0;for (int i = 1; i <= amount; i++) {for (int j = 0; j < coins.length; j++) {if (coins[j] <= i) {dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);}}}return dp[amount] > amount ? -1 : dp[amount];}
}

转移方程应为:F(S)=F(S−C)+1

4. 自己总结

备忘录 = 记忆化搜索
自顶向下的递归,备忘录存储之前查找过的值,需要 On 的临时存储数组。
dp table = 动态规划
自低向上,也需要一个 dp 数组,On 空间
这两个方法复杂度开支相同,本质一样。

求一棵二叉树的最大值,不难吧(简单起⻅,假设节点中的值都是非负数)

int maxVal(TreeNode root) {if (root == null)return -1;int left = maxVal(root.left);int right = maxVal(root.right); return max(root.val, left, right);
}

最优子结构并不是动态规划独有的一种性质,能求最值的问题大部分都具有这个性质;但反过来,最优子结构性质作为动态规划问题的必要条件,一定是让你求最值的

72.编辑距离
https://leetcode-cn.com/problems/edit-distance/

遍历方向
主要就是看 base case 和最终结果的存储 位置
1、遍历的过程中,所需的状态必须是已经计算出来的。
2、遍历的终点必须是存储结果的那个位置。

5. 回溯算法

主要是做遍历,列出所有答案,或者只要一个(获得就提前返回)
不算是求最值。

题目列表
1.全排列
2.N 皇后

回溯算法核心框架

for 选择 in 选择列表: #做选择
​   将该选择从选择列表移除
​   路径.add(选择)
​   backtrack(路径, 选择列表) #撤销选择
​   路径.remove(选择)
​   将该选择再加入选择列表 回溯算法框架
result = []
def backtrack(路径, 选择列表): if 满足结束条件: result.add(路径) return for 选择 in 选择列表: 做选择 backtrack(路径, 选择列表) 撤销选择

回溯算法代表问题
46.全排列问题https://leetcode-cn.com/problems/permutations/solution/quan-pai-lie-by-leetcode-solution-2/

为何要撤销选择?

树形结构的深度优先遍历,就是回溯算法
状态,每个结点表示了求解问题的不同阶段
深度优先遍历在回到上一层结点时需状态重置
状态重置,就是回溯算法中回溯的意思
状态变量
1.递归到了第几层depth,depth==nums.size 是递归终止条件
2.已经选择了那些数,放进列表 path 中(栈)
3.布尔数组 used,是否访问过

我的理解

回溯算法,就是树的深度优先遍历
从根节点,逐渐深入到一个完整的排列
排列数=总数字数,则排列完成,加入 res 列表

向左子树一路压栈,压到底层,访问一次。
然后开始弹栈,弹出后访问右子树,再一路压到底层,访问一次。
再弹栈。
每次压栈时将这个点记录到 used 或者 visited 列表,
下次往下压时就不压这个了。

(used数组,dong 用的是 stack.contains() 更方便,dong 结构更清晰,采用东的。
力扣对照着看)

N 皇后问题
https://leetcode-cn.com/problems/n-queens/
也可以用框架,就是在选择方法上要改一改
更进一步还有八皇后题目https://leetcode-cn.com/problems/eight-queens-lcci/

回溯算法和动态规划的暴力求解阶段类似,
只是动态规划有重叠子问题,回溯算法没有。

6. 二分查找搜索

704经典二分查找https://leetcode-cn.com/problems/binary-search/
34.二分查找左右边界https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/

左边界二分搜索

int left_bound(int[] nums, int target) { if (nums.length == 0) return -1;int left = 0;int right = nums.length; // 注意 while (left < right) { //注意int mid = (left + right) / 2; if (nums[mid] == target) { right = mid;} else if (nums[mid] < target) {left = mid + 1;} else if (nums[mid] > target) {right = mid; //注意 } } return left;
}

右侧边界

int right_bound(int[] nums, int target) { if (nums.length == 0) return -1;int left = 0, right = nums.length; while (left < right) {int mid = (left + right) / 2;if (nums[mid] == target) {left = mid + 1; // 注意} else if (nums[mid] < target) { left = mid + 1;} else if (nums[mid] > target) {right = mid; } } return left - 1; // 注意
}

关键点在于,nums[mid]==target 时,不是返回,而是调整搜索区间
如果搜左边界,就写成 high=mid-1;
如果搜右边界,就写成 low=mid+1

【这个方法太复杂,容易出错,我更喜欢二分法找到中心,然后向两遍扩散】

7. 滑动窗口算法

双指针解决子串、子数组问题

int left = 0, right = 0;
while (right < s.size()) {// 增大窗口 window.add(s[right]); right++; while (window needs shrink) { // 缩小窗口 window.remove(s[left]); left++; }
}

这个滑动窗口也可以视情况使用 set 来做
比如不重复最小子串

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) { unordered_map<char, int> need, window;for (char c : t) need[c]++;int left = 0, right = 0; int valid = 0;while (right < s.size()) { // c 是将移入窗口的字符 char c = s[right]; // 右移窗口        right++; // 进行窗口内数据的一系列更新 ... /*** debug 输出的位置 *** /      printf("window: [%d, %d)\n", left, right); /********************/ //判断左侧窗口是否要收缩   while (window needs shrink) { // d 是将移出窗口的字符 char d = s[left];         //左移窗口      left++; // 进行窗口内数据的一系列更新 ... } }
}

现在开始套模板,只需要思考以下四个问题:
1、当移动 right 扩大窗口,即加入字符时,应该更新哪些数据?
2、什么条件下,窗口应该暂停扩大,开始移动 left 缩小窗口?
3、当移动 left 缩小窗口,即移出字符时,应该更新哪些数据?
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?

76最小覆盖子串
https://leetcode-cn.com/problems/minimum-window-substring/

可以使用方括号访问键对应的值 map[key] 。需要注意的是,如果该 key 不存在,C++ 会自动创建这个 key,并把 map[key] 赋值为 0。

map[key]++ 相当于 Java 的 map.put(key, map.getOrDefault(key, 0) + 1) 。

疑问
// 判断左侧窗口是否要收缩

while (valid == need.size()) { ————如果 valid 是3,但是里面2个 c,一个 b并不等于 need{a,b,c} 怎么整?

if (window[c] == need[c])
valid++; ————明白了,判断 window 中 a 个数==need 中 a 个数才valid++

567字符串排列
https://leetcode-cn.com/problems/permutation-in-string/

438字母异位词
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/

3最长无重复子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

8. 双指针

快慢指针解决链表中的问题,比如典型的判定链表中是否包含环;
左右指针解决数组(或者字符串)中的问题,比如二分查找。

快慢指针
一般都初始化指向链表的头结点 head,前进时快指针 fast 在前, 慢指针 slow 在后
1.是否有环
2.已知有环,判断环的起点(脑筋急转弯 k-m)
3.链表中点(快慢指针,一个一步,一个两步)
链表归并
4.链表倒数第 k 个元素

ListNode slow, fast;
slow = fast = head;
while (k-- > 0) fast = fast.next;
while (fast != null) { slow = slow.next; fast = fast.next;
}
return slow;

左右指针
在数组中实际是指两个索引值,一般初始化为 left = 0, right = nums.length - 1
1.二分查找
2.两数之和
167两数之和:输入有序数组
https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
3.盛水最多的容器
4.反转数组,只需要O1空间复杂度
5.滑动窗口是左右指针最高级玩法

9. BFS 算法

DFS 算法就是回溯算法

BFS 的核心思想应该不难理解的,就是把一些问题抽象成图,从一个点开始,向四周开始扩散。
一般来说,我们写 BFS 算法都是用「队列」这种数 据结构,每次将一个节点周围的所有节点加入队列。
BFS 找到的路径一定是最短的,但代价 就是空间复杂度比 DFS 大很多。

DFS 不能找最短路径吗?
其实也是可以的,但是时间复杂度相对高很多。
你想啊,DFS 实际上是靠递归的堆栈记录走过的路径,你要找到最短路径,肯定得把二叉树中所有树杈都探索完才能对比出最短的路径有多⻓对不对?
而 BFS 借助队列做到一次一步「⻬头并进」,是可以在不遍历完整棵树的条件下找到最短距离的。

BFS 可以找到最短距离,但是空间复杂度高,而 DFS 的空间复杂度较低
BFS 还是有代价的,一般来说在找最短路径的时候使用 BFS, 其他时候还是 DFS 使用得多一些(主要是递归代码好写)

BFS 常见场景:问题的本质就 是让你在一幅「图」中找到从起点 start 到终点 target 的最近距离
1.走迷宫,有的格子是围墙不能走,从起点到终点的最短距离是多少?如果这个迷宫带「传送门」可以瞬间传送呢?
2.两个单词,要求你通过某些替换,把其中一个变成另一个,每次只能替换一个字符,最少要替换几次?
3.连连看游戏,两个方块消除的条件不仅仅是图案相同,还得保证两个方块之间的最短连线不能多于两个拐点。你玩连连看,点击两个坐标,游戏是如何判断它俩的最短连线有几个拐点的?

// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {Queue<Node> q; // 核心数据结构
Set<Node> visited; // 避免走回头路q.offer(start); // 将起点加入队列
visited.add(start);
int step = 0; // 记录扩散的步数while (q not empty) {int size = q.size();/* 将当前队列中的所有节点向四周扩散 */ for (int i = 0; i < size; i++) {Node cur = q.poll();/* 划重点:这里判断是否到达终点 */if (cur is target)return step;/* 将 cur 的相邻节点加入队列 */ for (Node x : cur.adj()) if (x not in visited) { q.offer(x); visited.add(x); } }/* 划重点:更新步数在这里 */ step++; }
}

队列 q 就不说了,BFS 的核心数据结构;
cur.adj() 泛指 cur 相邻的节点,比如说二维数组中, cur 上下左右四面的位置就是相邻节点;
visited 的主要作用是防止走回头路,大部分时候都是必须的,但是像一般的二叉树结构,没有子节点到父节点的指针,不会走回头路就不需要visited

111.二叉树最小深度
https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/

752.解开密码锁最少次数
https://leetcode-cn.com/problems/open-the-lock/

10. 专题

(较难排后,对比剑指offer 看)

​ 动态规划,找状态转移方程几步流程——0308看了一些

53.最大子序和E https://leetcode-cn.com/problems/maximum-subarray/submissions/
突破惯性思考方式 -> 以子序列结束点为基准,遍历以某个节点为结束的所有子序列。
可解决如背包问题, 最大公共子串问题,包括本题
详解:https://leetcode-cn.com/problems/maximum-subarray/solution/xiang-xi-jie-du-dong-tai-gui-hua-de-shi-xian-yi-li/

动态规划思想是倒推思想,归纳法

二分专题
简单二分用 while(left<=right) 在循环里面就能找到答案
复杂二分用 while(left<right) 因为退出时left==right,减少很多麻烦,缺点是初次用起来容易在里面死循环
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/da-jia-bu-yao-kan-labuladong-de-jie-fa-fei-chang-2/
二分查找多个变种总结
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/yi-wen-dai-ni-gao-ding-er-fen-cha-zhao-j-ymwl/
这个人写的二分法统一总结很棒,避免很多麻烦
https://www.cnblogs.com/buaaliang/p/11372738.html

labuladong算法小结相关推荐

  1. 数据挖掘中分类算法小结

    数据挖掘中分类算法小结   数据仓库,数据库或者其它信息库中隐藏着许多可以为商业.科研等活动的决策提供所需要的知识.分类与预测是两种数据分析形式,它们可以用来抽取能够描述重要数据集合或预测未来数据趋势 ...

  2. [转载]SIFT(尺度不变特征变换)算法小结

    原文地址:SIFT(尺度不变特征变换)算法小结[转]作者:慕容天峰 最近一直在看SIFT算法.Sift是David Lowe于1999年提出的局部特征描述子,并于2004年进行了更深入的发展和完善.S ...

  3. linemod算法小结

    Linemod算法小结   LineMod方法是由Hinterstoisser[1][2][3]在2011年提出,主要解决的问题是复杂背景下3D物体的实时检测与定位,用到了RGBD的信息,可以应对无纹 ...

  4. 分治算法小结(附例题详解)

    分治算法小结(附例题详解) 我的理解: 分治算法我的理解就是看人下菜碟,我们要解决的问题就好像一群人构成的集体,要我们解决这个问题,那我们就要满足这群人里面每个人不同的需求,也就是写出解决的代码,把每 ...

  5. Fast Gradient Sign Attack(FGSM)算法小结

    Fast Gradient Sign Attack(FGSM)算法小结 对抗攻击引发了机器学习一系列的思考,训练出来的model是否拥有强大的泛化能力?模型的准确率是否真实? 在对抗攻击中添加一些肉眼 ...

  6. 距离(distance)算法小结

    18种和"距离(distance)"."相似度(similarity)"相关的量的小结 在计算机人工智能领域,距离(distance).相似度(similari ...

  7. Tarjan算法小结1——SCC

    引入 许多最短单源路径算法,如Dijkstra,SPFA, floyd, Bellman-Ford等,在运用时只能给出指定点到任意点的最短距离,抑或是给出图中是否有环的信息,并不能准确确定环的个数.包 ...

  8. 基于暗通道先验的单幅图像去雾算法小结

    在开始之前,先看一组去雾的效果图. 1. 关于去雾的意义及算法 去雾在视频监控.航拍.遥感.自动/辅助驾驶等需要在室外复杂.恶劣天气下运行的视觉系统,都可能需要运用到这一技术. 目前去雾算法主要有两个 ...

  9. mysql loop嵌套_MySQL中Nested-Loop Join算法小结

    数据库中JOIN操作的实现主要有三种:嵌套循环连接(Nested Loop Join),归并连接(Merge Join)和散列连接或者哈稀连接(Hash Join).其中嵌套循环连接又视情况又有两种变 ...

  10. SLAM之特征匹配(二)————RANSAC--------翻译以及经典RANSAC以及其相关的改进的算法小结

    本文翻译自维基百科,英文原文地址是:http://en.wikipedia.org/wiki/ransac     RANSAC是"RANdom SAmple Consensus(随机抽样一 ...

最新文章

  1. C 内存free()出错
  2. Vuex 的异步数据更新(小记)
  3. Nature盘点的这些代码,个个都改变了科学:Fortran、AlexNet还有arXiv等
  4. leetCode第五题-求字符串最长回文字符串
  5. 解决Extjs中Combobox显示值和真实值赋值问题
  6. mac上java文件如何编译_如何在Mac上用Java编译和运行程序?
  7. Ajax中的JSON
  8. java面试题学生表成绩表老师表_SQL经典学生老师成绩表面试题
  9. java day51【综合案例day03】
  10. ArcGIS三维制作步骤
  11. 谷歌正式推出中国版谷歌软件精选
  12. 小白重装电脑教程—WePE or 老毛桃
  13. usb声卡驱动_艾肯iCON ProDrive第三代USB声卡驱动全新发布!
  14. 微信小程序实现旋转动画效果
  15. 1、字符串与数字相互转换
  16. C++中 =defaule 和 =delete 使用
  17. 解决HTML、PHP乱码问题
  18. 浏览器兼容css hack,CSS Hack技术解决多浏览器兼容问题
  19. 如何免费学习lynda.com上的课程
  20. 第一节:人像精修第一步-合理转档

热门文章

  1. 易语言手游辅助开发教程
  2. XUI 一个简洁而优雅的Android原生UI框架,解放你的双手!
  3. 电机与拖动基础--第一章
  4. iOS 在线下载字体
  5. 时间选择器(timepicker)用法
  6. android timepicker 设置颜色,android – 更改TimePicker文本颜色
  7. matlab sae模型,发动机平均值模型的三篇SAE论文
  8. 一种便携式导弹飞控系统外场实时仿真测试系统设计
  9. iRedMail在Debian或Ubuntu Linux上的安装
  10. UTM大地坐标系与经纬度转换算法C++