剑指 Offer 38. 字符串的排列:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/
点击查看代码
// 思想:从剩余可选字符中填充指定位置,直到所有位置填充完成
// 注意String和char[]互转;ArrayList转数组均通过toArray(new T[0])完成
public class Solution {
public String[] permutation(String s) {
// 标记该元素是否已使用,剪枝
boolean[] used = new boolean[s.length()];
// 用于结果去重
HashSet<String> resSet = new HashSet<>();
dfs("", s, resSet, used);
ArrayList<String> res = new ArrayList<>(resSet);
return res.toArray(new String[0]);
}
private void dfs(String curStr, String s, HashSet<String> set, boolean[] used) {
if (curStr.length() == s.length()) {
set.add(curStr);
}
// 填充下一位置的元素
for (int i = 0; i < s.length(); i++) {
if (used[i]) {
// 如果该元素已经使用了,则继续查找下一个未使用的元素
continue;
}
used[i] = true;
dfs(curStr + s.charAt(i), s, set, used);
used[i] = false;
}
}
}
剑指 Offer 48. 最长不含重复字符的子字符串:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/
点击查看代码
// 最长不含重复字符的子字符串:双指针 + hashmap
public class Solution {
public static int lengthOfLongestSubstring(String s) {
//k-v = 字符 - 字符所在位置。如果存在重复字符,更新位置到最新
HashMap<Character, Integer> map = new HashMap<>();
//非重复字符串长度 = r - l, l维护的是非重复区间的左边界(l, r]
int l = -1;
int maxLen = 0;
for (int r = 0; r < s.length(); r++) {
if (map.containsKey(s.charAt(r))) {
// 更新左指针, 取max是为了避免abba这种样例场景下l的倒退
l = Math.max(l, map.get(s.charAt(r)));
}
maxLen = Math.max(maxLen, r - l);
map.put(s.charAt(r), r);
}
return maxLen;
}
}
点击查看代码
// 正则表达式实现,忽略题目O(1)的空间复杂度限制
public class Solution {
public String reverseWords(String s) {
// 去掉空格,个数不限 "[\\s]+", 分割的字符串数组中存在""情况
// 去掉逗号,个数不限 "[,]+", 分割的字符串数组中存在""情况
String[] strArr = s.split("[\\s]+");
// 题目已保证字符串种至少存在一个单词,StringBuilder非线程安全&性能更佳
StringBuilder builder = new StringBuilder();
int i = strArr.length - 1;
while (i >= 0) {
if (strArr[i].length() > 0) {
if (builder.length() == 0) {
builder.append(strArr[i]);
} else {
builder.append(" ").append(strArr[i]);
}
}
i--;
}
return builder.toString();
}
}
// 题目限制了空间复杂度为O(1):使用subString,用r和l保存单个单词的取值区间
public class Solution {
public String reverseWords(String s) {
StringBuilder builder = new StringBuilder();
int r = s.length() - 1;
while (r >= 0) {
// r指向单次最右边的字母
while (r >= 0 && s.charAt(r) == ' ') {
r--;
}
if (r < 0) {
break;
}
// l指向单次最左边的字母
int l = r - 1;
while (l >= 0 && s.charAt(l) != ' ') {
l--;
}
if (l < 0 && s.charAt(0) == ' ') {
break;
}
l++;
// 基于l和r截取s的子串,[l, r)
String subStr = s.substring(l, r + 1);
if (builder.length() == 0) {
builder.append(subStr);
} else {
builder.append(" ").append(subStr);
}
// 更新r
r = l - 1;
}
return builder.toString();
}
}
剑指 Offer 62. 圆圈中最后剩下的数字:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/
点击查看代码
// 通用解法:使用队列模拟实现,但m很大时模拟实现会超时
public class Solution {
public int lastRemaining(int n, int m) {
LinkedList<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
queue.add(i);
}
while (queue.size() > 1) {
int i = 1;
// 循环淘汰第m个数字
while (i < m) {
queue.add(queue.poll());
i++;
}
queue.poll();
}
return queue.poll();
}
}
// 数学推导,定义:f(n, m)表示有n个人时最后存活下来的人的编号
// 则f(n, m) = (f(n-1, m) + m) % n, 且f(1,m) = 0
public class Solution {
public int lastRemaining(int n, int m) {
// 最后存活下来的人的编号, n == 1
int pos = 0;
// 基于公式,推导初始化状态下的存活下来的人的编号n > 1
for (int i = 2; i <= n; i++) {
pos = (pos + m) % i;
}
return pos;
}
}
- 用 Rand7() 实现 Rand10():https://leetcode-cn.com/problems/implement-rand10-using-rand7/
点击查看代码
// 利用rand7可产生49个随机数,基于7进制,数据取值范围是[8, 56]
// 要生成[1,10]范围随机数,每4个数对应1~10中的一个数,则需要40个数
// 因此:rand7产生的数中,我们需要的数据范围是[8,47],总共40个数
public class Solution extends SolBase {
public int rand10() {
// 利用rand7生成[8,47]范围中的数
int val = 48;
while (val > 47) {
val = (rand7() * 7 + rand7());
}
// 基于val构造rand10范围的数[1,10]
return (val - 8) / 4 + 1;
}
}
点击查看代码
// x取值范围大,必须使用二分查找,时间复杂度:O(logX)
public class Solution {
public int mySqrt(int x) {
int l = 0;
int r = x;
int ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
if ((long) mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
}
// 写一个函数,求带精度的平方根,函数参数为目标数字和精度。二分法
public class Solution {
public float mySqrt(float n, float e) {
float l = 0;
float r = n;
while (l <= r) {
float mid = (1 + r) / 2;
if (mid * mid < (n - e)) {
l = mid + 1;
} else if (mid * mid > (n + e)) {
r = mid - 1;
} else {
return mid;
}
}
return l;
}
}
点击查看代码
// 贪心:总加油量 >= 总耗油量一定有解;[0, i]如果位置i的剩余油量<0,则[0, i]中的任一位置都不能作为起点,枚举位置直接是[i+1]
public class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curTank = 0;
int sumTank = 0;
int res = 0;
for (int i = 0; i < gas.length; i++) {
curTank = curTank + gas[i] - cost[i];
sumTank = sumTank + gas[i] - cost[i];
if (curTank < 0) {
// 重新以i + 1作为起点环行
curTank = 0;
res = i + 1;
}
}
return sumTank >= 0 ? res : -1;
}
}
点击查看代码
// 合并区间:数组排序 + 双指针
public class Solution {
public int[][] merge(int[][] intervals) {
if (intervals.length <= 1) {
return intervals;
}
// 数组排序,元素1相同时再基于元素2做升序排序
Arrays.sort(intervals, ((o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0]));
ArrayList<int[]> res = new ArrayList<>();
int s = intervals[0][0];
int e = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] > e) {
// 下一区间和当前维护的区间不相交,将当前区间加入res,同时更新s、e
res.add(new int[]{s, e});
s = intervals[i][0];
}
// 更新e, 待合并区间可能完全包含于当前维护的区间
e = Math.max(e, intervals[i][1]);
}
// 判断最后维护的[s, e]是否已加入res
if (res.size() == 0 || res.get(res.size() - 1)[1] < e) {
res.add(new int[]{s, e});
}
return res.toArray(new int[res.size()][]);
}
}
点击查看代码
public int[][] generateMatrix(int n) {
int[][] res = new int[n][n];
int rlow = 0, rhigh = n - 1;
int clow = 0, chigh = n - 1;
int fillNum = 1, upNum = n * n;
while (fillNum <= upNum) {
// 从左到右
for (int i = clow; i <= chigh; i++) {
res[rlow][i] = fillNum++;
}
rlow++;
// 从上到下
for (int i = rlow; i <= rhigh; i++) {
res[i][chigh] = fillNum++;
}
chigh--;
// 从右到左
for (int i = chigh; i >= clow; i--) {
res[rhigh][i] = fillNum++;
}
rhigh--;
// 从下到上
for (int i = rhigh; i >= rlow; i--) {
res[i][clow] = fillNum++;
}
clow++;
}
return res;
}
}
点击查看代码
public int romanToInt(String s) {
// 使用hashmap存储字符到数字的映射,直接累加转换后的数字即可
HashMap<String, Integer> map = new HashMap<>();
String[] keys = {"I", "V", "X", "L", "C", "D", "M", "IV", "IX", "XL", "XC", "CD", "CM"};
int[] values = {1, 5, 10, 50, 100, 500, 1000, 4, 9, 40, 90, 400, 900};
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
int sum = 0;
for (int i = 0; i < s.length(); i++) {
if (i + 1 == s.length() || s.charAt(i) == 'V' || s.charAt(i) == 'L' || s.charAt(i) == 'D' || s.charAt(i) == 'M') {
sum += map.get(String.valueOf(s.charAt(i)));
} else {
// 标记本此取值是否已完成处理
if (s.charAt(i) == 'I' && (s.charAt(i + 1) != 'V' && s.charAt(i + 1) != 'X')) {
sum += 1;
} else if (s.charAt(i) == 'X' && (s.charAt(i + 1) != 'L' && s.charAt(i + 1) != 'C')) {
sum += 10;
} else if (s.charAt(i) == 'C' && (s.charAt(i + 1) != 'D' && s.charAt(i + 1) != 'M')) {
sum += 100;
} else {
sum += map.get(String.valueOf(s.substring(i, i + 2)));
i++;
}
}
}
return sum;
}
}
点击查看代码
// 按序打印:CountDownLatch
// CountDownLatch-倒计时计算器: 同步工具类,用来协调多个线程之间的同步。核心方法:countDown/await。
// 缺点是其值只能被初始化一次,使用完毕后不能再次被使用
public class Foo {
CountDownLatch cdLatch1, cdLatch2;
public Foo() {
// 用于线程1和线程2之间的同步
cdLatch1 = new CountDownLatch(1);
// 用于线程2和线程3之间的同步
cdLatch2 = new CountDownLatch(1);
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
cdLatch1.countDown();
}
public void second(Runnable printSecond) throws InterruptedException {
// 等待cdLatch1计算器取值为0时被唤醒,在计数器为0之前会一致等待直到线程被中断或超出了指定的等待时间
cdLatch1.await();
printSecond.run();
cdLatch2.countDown();
}
public void third(Runnable printThird) throws InterruptedException {
// 等待cdLatch2计算器取值为0时被唤醒
cdLatch2.await();
printThird.run();
}
}
// 三个线程循环打印ABC 10次,使用:ReentrantLock-可重入的互斥锁:lock/unlock,通过newCondition可实现线程之间的协调通信
public class Solution {
private static int state = 0;
private static final ReentrantLock lock = new ReentrantLock();
private static final Condition condA = lock.newCondition();
private static final Condition condB = lock.newCondition();
private static final Condition condC = lock.newCondition();
private static void function(String s, int targetId, Condition awaitCond, Condition signalCond) {
while (state < 30) {
// 尝试获取可重入的互斥锁
lock.lock();
try {
if (state % 3 != targetId) {
// 当前线程释放锁,并将自己沉睡,等待唤醒
awaitCond.await();
}
System.out.print(s);
state++;
// 唤醒其它等待中的线程
signalCond.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
function("A", 0, condA, condB);
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
function("B", 1, condB, condC);
}
});
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
function("C", 2, condC, condA);
}
});
threadA.start();
threadB.start();
threadC.start();
}
}
艾子的世界艾子主宰
浙公网安备 33010602011771号