Algorithms - Thinking Training

Design a timeout function

Ideas:

  1. Open a watchdog thread to monitor the timeout, and notify the outside if the timeout
  2. In order to improve the performance of the program, the watchdog thread should be blocked during the waiting timeout period
  3. 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:

  1. A carry variable is required to save the carry;
  2. 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:

  1. Two pointers are required, indicating the left and right boundaries of the substring
  2. 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:

  1. Step 1: Transpose the Matrix
  2. 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:

  1. If you search violently, you need three layers of loops
  2. 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:

  1. Easiest to think of: three-layer loop, violent search
  2. 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;
}

Guess you like

Origin blog.csdn.net/H_Zhang/article/details/106113607