Design a timeout function
Ideas:
- Open a watchdog thread to monitor the timeout, and notify the outside if the timeout
- In order to improve the performance of the program, the watchdog thread should be blocked during the waiting timeout period
- Timeout linked list management can be implemented in companion objects
class ATimeOut constructor(private val timeoutNanos: Long) {
/** 记录超时时间点 */
private var timeoutAt = 0L
/** 下一个节点 */
private var next: ATimeOut? = null
/**
* 开始计时
*/
fun enter() {
val timeoutNanos = timeoutNanos
scheduleTimeout(this, timeoutNanos)
}
/**
* 通知使用方超时
*/
protected open fun timedOut() {
}
/**
* 剩余时间
*/
private fun remainingNanos(now: Long) = timeoutAt - now
/**
* watchdog子线程
*/
private class Watchdog internal constructor() : Thread("watchdog") {
init {
isDaemon = true
}
override fun run() {
while (true) {
var timeout: ATimeOut? = null
synchronized(ATimeOut::class.java) {
/** 获取超时节点,等待期间线程阻塞 */
timeout = awaitTimeout()
// 链表为空,线程退出
// 下次schedule时,会创建一个新的线程
if (timeout === head) {
head = null
return
}
timeout?.timedOut()
}
}
}
}
/**
* 伴生对象负责:
* 1. 维护将新节点链入链表逻辑
* 2. 从链表中取出超时节点逻辑
*/
companion object {
private val IDLE_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60)
private val IDLE_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(IDLE_TIMEOUT_MILLIS)
/** 链表头 */
private var head: ATimeOut? = null
/**
* 将新节点链入链表当中
* 链表按超时时间排序
*/
private fun scheduleTimeout(node: ATimeOut, timeoutNanos: Long) {
synchronized(ATimeOut::class.java) {
if (head == null) {
head = ATimeOut(-1)
/** 开启watchdog线程 */
Watchdog().start()
}
val now = System.nanoTime()
node.timeoutAt = now + timeoutNanos
val remainingNanos = node.remainingNanos(now)
var prev = head!!
/** 链表按 超时时间排序 */
while (true) {
if (prev.next == null || remainingNanos < prev.next!!.remainingNanos(now)) {
node.next = prev.next
prev.next = node
if (prev === head) {
/** 唤醒watchdog线程 */
(ATimeOut::class.java as Object).notify()
}
break
}
prev = prev.next!!
}
}
}
/**
* 从链表取出超时节点,并从链表中将节点删除
*/
internal fun awaitTimeout(): ATimeOut? {
val node = head!!.next
if (node == null) {
val startNanos = System.nanoTime()
// 如果队列为空,watchdog线程等60s,60s后还为空,线程退出
(ATimeOut::class.java as Object).wait(IDLE_TIMEOUT_MILLIS)
// 线程被唤醒后,需要判断
return if (head!!.next == null && System.nanoTime() - startNanos >= IDLE_TIMEOUT_NANOS) {
head
} else {
null
}
}
var waitNanos = node.remainingNanos(System.nanoTime())
// 线程阻塞
if (waitNanos > 0) {
val waitMillis = waitNanos / 1000000L
waitNanos -= waitMillis * 1000000L
(ATimeOut::class.java as Object).wait(waitMillis, waitNanos.toInt())
return null
}
// 走到这里,代表节点超时,将节点删除
head!!.next = node.next
node.next = null
return node
}
}
}
Two linked lists want to add
Ideas:
- A carry variable is required to save the carry;
- Finally exit the loop and need to judge the carry again
public ListNode addLinkedList(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode curr = dummyHead;
// p,q两个移动指针
ListNode p = l1, q = l2;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
// 计算进位
carry = sum / 10;
// 构建新节点,注意要和10取模
curr.next = new ListNode(sum % 10);
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
if (carry > 0) {
curr.next = new ListNode(carry);
}
return dummyHead.next;
}
Find the longest substring without repetition
Ideas:
- Two pointers are required, indicating the left and right boundaries of the substring
- In the search process, use the data structure Set to store substrings and determine whether there are repetitions
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> set = new HashSet<Character>();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int right = -1, result = 0;
int n = s.length();
for (int left = 0; left < n; left++) {
if (left != 0) {
// 左指针向右移动一格,移除一个字符
set.remove(s.charAt(left - 1));
}
while (right + 1 < n && !set.contains(s.charAt(right + 1))) {
// 不断地移动右指针
set.add(s.charAt(right + 1));
right++;
}
// 第 left 到 right 个字符是一个极长的无重复字符子串
result = Math.max(result, right - left + 1);
}
return result;
}
rotate matrix clockwise
Ideas:
- Step 1: Transpose the Matrix
- Step 2: Invert each row
public void rotate(int[][] matrix) {
int n = matrix.length;
// 转置
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
int tmp = matrix[j][i];
matrix[j][i] = matrix[i][j];
matrix[i][j] = tmp;
}
}
// 反转每行
for (int i = 0; i < n; i++) {
int start = 0;
int end = n;
while (start < end) {
int tmp = matrix[i][start];
matrix[i][start] = matrix[i][end];
matrix[i][end] = tmp;
start++;
end--;
}
}
}
Find three numbers in the array whose sum is closest to target
Ideas:
- If you search violently, you need three layers of loops
- Two layers of loops, using double pointers to reduce one layer of loops, the premise needs to be sorted
public int threeCloseNumber(int[] nums, int target) {
// 排序
Arrays.sort(nums);
int result = nums[0] + nums[1] + nums[2];
for (int i = 0; i < nums.length - 2; i++) {
// 左右两个指针
int left = i + 1;
int right = nums.length - 1;
while (left != right) {
int sum = nums[0] + nums[left] + nums[right];
if (Math.abs(sum - target) < Math.abs(result - target)) {
result = sum;
}
// 如果比target大,右指针左移
if (sum > target) {
right--;
} else {
left++;
}
}
}
return result;
}
Find all triples in the array whose sum is 0
Ideas:
- Easiest to think of: three-layer loop, violent search
- Two layers of circulation. Auxiliary space is needed to temporarily store traversed data
public List<List<Integer>> threeNum(int[] nums) {
// 存储所有三元组
Set<List<Integer>> result = new LinkedHashSet<>();
for (int i = 0; i < nums.length - 2; i++) {
int target = 0 - nums[i];
Map<Integer, Integer> map = new HashMap<>();
for (int j = i + 1; j < nums.length; j++) {
int v = target - nums[j];
Integer exist = map.get(v);
// 找到一个三元组
if (exist != null) {
List<Integer> list = Arrays.asList(nums[i], exist, nums[j]);
result.add(list);
} else {
// 存储数据
map.put(nums[j], nums[j]);
}
}
}
return result;
}