7.6 头条算法习题

16 minute read

7.6.1 字符串

  • 无重复字符的最长子串 (给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。, 示例 2: 输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。), 示例 3: 输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
class Solution {
    public int lengthOfLongestSubstring(String s) {
        Set<Character> set = new HashSet<>();
        int lengthOfLongest = 0;
        int i = 0, j = 0;
        int n = s.length();
        while(i < n && j < n) {
            if(!set.contains(s.charAt(j))) {
                set.add(s.charAt(j++));
                lengthOfLongest = Math.max(lengthOfLongest, j - i);
            } else {
                set.remove(s.charAt(i++));
            }
        }
        return lengthOfLongest;
    }

}

  • 最长公共前缀 (编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ““。示例 1: 输入: ["flower","flow","flight"] 输出: "fl", 示例 2: 输入: ["dog","racecar","car"] 输出: "" 解释: 输入不存在公共前缀。)
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        String prefix = "";
        int length = strs[0].length();
        int prefixIndex = 1;
        String currentPrefix = null;
        while (prefixIndex <= length) {
            currentPrefix = strs[0].substring(0, prefixIndex);
            for (int i = 1; i < strs.length; i++) {
                if (!strs[i].startsWith(currentPrefix)) {
                    return prefix;
                }
            }
            prefix = currentPrefix;
            prefixIndex++;
        }
        return prefix;
    }
}
  • 字符串的排列 (给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。换句话说,第一个字符串的排列之一是第二个字符串的子串。示例1: 输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba")., 示例2: 输入: s1= "ab" s2 = "eidboaoo" 输出: False)
class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if (s1 == null || s2 == null) {
            return false;
        }
        int left = 0;
        int k = s1.length();
        int count = k;
        boolean isInclusion = false;
        int stringMap[] = new int[26];
        for (int i = 0; i < s1.length(); i++) {
            stringMap[s1.charAt(i) - 'a']++;
        }
        for (int right = 0; right < s2.length(); right++) {
            if (--stringMap[s2.charAt(right) - 'a'] >= 0) {
                count--;
            }
            if (count == 0) {
                isInclusion = true;
                break;
            }
            if (right - left + 1 == k) {
                if (++stringMap[s2.charAt(left) - 'a'] > 0){
                    count++;
                }
                left++;
            }
        }

        return isInclusion;
    }
}
  • 字符串相乘 (给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。示例 1: 输入: num1 = "2", num2 = "3" 输出: "6", 示例 2: 输入: num1 = "123", num2 = "456" 输出: "56088")
class Solution {
    public String multiply(String num1, String num2) {
        if (num1 == null || num2 == null) {
            return null;
        }
        int m = num1.length();
        int n = num2.length();    
        int sum[] = new int [m + n];
        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
                int p1 = i + j;
                int p2 = i + j + 1;
                mul += sum[p2];

                sum[p1] += mul / 10;
                sum[p2] = mul % 10;
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int p : sum) {
            if (!(sb.length() == 0 && p == 0)) {
                sb.append(p);
            }
        }
        return sb.length() == 0 ? "0" : sb.toString();

    }
}
  • 翻转字符串里的单词 (给定一个字符串,逐个翻转字符串中的每个单词。示例 1:输入: "the sky is blue" 输出: "blue is sky the", 示例 2:输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。, 示例 3:输入: "a good example" 输出: "example good a" 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。)
class Solution {
    public String reverseWords(String s) {
        List<String> resultList = new ArrayList();
        for (String item : s.split(" ")) {
            if (item.length() > 0) {
                resultList.add(0, item);    
            }
        }
        return String.join(" ", resultList);
    }
}
  • 简化路径 (以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径 请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。示例1: 输入:"/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。, 示例5: 输入:"/a/../../b/../c//.//" 输出:"/c")
class Solution {
    public String simplifyPath(String path) {
        List<String> res = new ArrayList<>();
		res.add("/");
		int start = 1, end = 1, len = path.length();
		while (end < len) {
			while (end < len && path.charAt(end) != '/')
				end++;
			String subPath = path.substring(start, end);
			if ("..".equals(subPath)) {
				if (res.size() != 1) {
					res.remove(res.size() - 1);
				}
			} else if (!(".".equals(subPath)) && !("/".equals(subPath)) && !("".equals(subPath))) {
                System.out.println(subPath);
				res.add(subPath);
			}

			while (end < len && path.charAt(end) == '/')
				end++;
			start = end;
		}
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < res.size(); i++) {
			if (i == 0 || i == res.size() - 1) {
				sb.append(res.get(i));
			} else {
				sb.append(res.get(i)).append("/");
			}
		}
		return sb.toString();
    }

}
  • 复原IP地址 (给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。示例: 输入: "25525511135" 输出: ["255.255.11.135", "255.255.111.35"])
class Solution {
    public List<String> restoreIpAddresses(String s) {
        int gap = s.length() / 4;
        List<String> restoreIps = new ArrayList<>();
        _restoreIpAddresses(s, 0, "", restoreIps);

        return restoreIps;
    }

    public void _restoreIpAddresses(String s, int n, String out, List<String> resultList) {
        if (n == 4) {
            if (s.isEmpty()) {
                resultList.add(out);
            }
            return ;

        }
        System.out.println(out);
        for (int i = 1; i < 4; i++) {
                if (s.length() < i) {
                    break;
                }
                int value = Integer.parseInt(s.substring(0, i));
                if (value > 255 || i != String.valueOf(value).length()) {
                    continue;
                }

                _restoreIpAddresses(s.substring(i), n+1, out + s.substring(0, i) + (n==3 ? "" : "."), resultList);
            }

    }
}

7.6.2 数组与排序

  • 三数之和 (给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。示例: 例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为: [[-1, 0, 1],[-1, -1, 2]])
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> resultList = new ArrayList<>();
        if (nums.length == 0) return resultList;
        Arrays.sort(nums);
        boolean hasZero = false;
        for (int i = 0; i < nums.length - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1;
            int right = nums.length - 1;
            int sum = 0 - nums[i];
            while (left < right) {
                if (nums[left] + nums[right] == sum) {
                    resultList.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    while (left < right && nums[right] == nums[right - 1]) right--;
                    left++;
                    right--;
                } else if (nums[left] + nums[right] < sum) {
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    left++;
                } else {
                    while (left < right && nums[right] == nums[right - 1]) right--;
                    right--;
                  }
            }

        }
        return resultList;
    }
}
  • 搜索旋转排序数组(假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。示例: 输入: nums = [4,5,6,7,0,1,2], target = 0 输出: 4; 输入: nums = [4,5,6,7,0,1,2], target = 3 输出: -1)
class Solution {
    public int search(int[] nums, int target) {
        return search(nums, 0, nums.length - 1, target);
    }

    private int search(int[] nums, int low, int high, int target) {
        if (low > high) {
            return -1;
        }
        int mid = (low + high) / 2;
        if (nums[mid] == target) {
            return mid;
        }
        if (nums[mid] < nums[high]) {
            if (nums[mid] < target && target <= nums[high]) {
                return search(nums, mid + 1, high, target);
            } else {
                return search(nums, low, mid - 1, target);
            }
        } else {
            if (nums[low] <= target  && target <nums[mid]) {
                return search(nums, low, mid - 1, target);
            } else {
                return search(nums, mid + 1, high, target);
            }
        }
    }
}
  • 最长连续递增序列(给定一个未经排序的整数数组,找到最长且连续的的递增序列。示例: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1,3,5], 长度为3。尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。输入: [2,2,2,2,2] 输出: 1 解释: 最长连续递增序列是 [2], 长度为1。)
class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int previousValue = -1;
        int previousLengthOfLCIS = 0;
        int lengthOfLCIS = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > previousValue) {
                lengthOfLCIS++;
            } else {
                if (lengthOfLCIS > previousLengthOfLCIS) {
                    previousLengthOfLCIS = lengthOfLCIS;    
                }
                lengthOfLCIS = 1;
            }
            previousValue = nums[i];
        }
        if (previousLengthOfLCIS > lengthOfLCIS) {
            lengthOfLCIS = previousLengthOfLCIS;
        }
        return lengthOfLCIS;
    }
}
  • 数组中的第K个最大元素(在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。示例: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5; 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 输出: 4)
class Solution {
    public int findKthLargest(int[] nums, int k) {
        heapSort(nums);
        return nums[nums.length - k];
    }

    public void heapSort(int[] nums) {
        int len = nums.length;
        for (int i = len / 2 - 1; i >= 0; i--) {
            maxHeap(nums, i, len);
        }

        for (int i = len - 1; i > 0; i--) {
            swap(nums, 0, i);
            maxHeap(nums, 0, i);
        }
    }

    public void maxHeap(int[] nums, int index, int length) {
        int temp = nums[index];
        for (int i = index * 2 + 1; i < length; i = index * 2 + 1) {
            if (i+1 < length && nums[i] < nums[i+1]) {
                i++;
            }
            if (nums[i] > temp) {
                nums[index] = nums[i];
                index = i;
            } else {
                break;
            }
        }
        nums[index] = temp;
    }

    public void swap(int[] nums, int left, int right) {
        int tmp = nums[left];
        nums[left] = nums[right];
        nums[right] = tmp;
    }

}
  • 最长连续序列(给定一个未排序的整数数组,找出最长连续序列的长度。要求算法的时间复杂度为 O(n)。示例: 输入: [100, 4, 200, 1, 3, 2] 输出: 4 解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。)
class Solution {
    public int longestConsecutive(int[] nums) {
        HashSet<Integer> numsSet = new HashSet<>();
        int longestCount = 0;
        for (int i = 0; i < nums.length; i++) {
            numsSet.add(nums[i]);
        }
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            int tmpLongestCount = 0;
            while (numsSet.contains(num++) && tmpLongestCount < nums.length) {
                tmpLongestCount++;
            }
            if (tmpLongestCount > longestCount) {
                longestCount = tmpLongestCount;
            }
        }
        return longestCount;
    }
}
  • 第k个排列(给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。示例: 输入: n = 3, k = 3 输出: "213"; 输入: n = 4, k = 9 输出: "2314")
class Solution {
    public String getPermutation(int n, int k) {
        List<Integer> nums = new ArrayList<>();
        int factorial[] = new int[n + 1];
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            nums.add(i + 1);
        }

        int sum = 1;
        factorial[0] = 1;
        for (int i = 1; i <= n; i++) {
            sum *= i;
            factorial[i] = sum;
        }
        k--;

        for (int i = 1; i <= n; i++) {
            int index = k / factorial[n - i];
            sb.append(nums.get(index));
            nums.remove(index);
            k -= index * factorial[n - i];
        }
        return sb.toString();
    }


}
  • 朋友圈(班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。示例: 输入: [[1,1,0],[1,1,0],[0,0,1]] 输出: 2 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。第2个学生自己在一个朋友圈。所以返回2。输入: [[1,1,0],[1,1,1],[0,1,1]] 输出: 1 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。)
class Solution {
    public int findCircleNum(int[][] M) {
        int count = 0;
        int len = M[0].length;
        int visited[] = new int[len];
        for (int i = 0; i < M[0].length; i++) {
            if (visited[i] == 0) {
                dfs(M, visited, i);
                count++;
            }
        }

        return count;
    }

    public void dfs(int[][]M, int visited[], int i) {
        for (int j = 0; j < M[0].length; j++) {
            if (M[i][j] == 1 && visited[j] == 0) {
                visited[j] = 1;
                dfs(M, visited, j);
            }
        }
    }


}
  • 合并区间(给出一个区间的集合,请合并所有重叠的区间。示例: 输入: [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18]] 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 输入: [[1,4],[4,5]] 输出: [[1,5]] 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。)
class Solution {
    private class IntervalComparator implements Comparator<Interval> {
        public int compare(Interval a, Interval b) {
            return a.start < b.start ? -1 : a.start == b.start ? 0 : 1;
        }
    }
    public int[][] merge(int[][] intervals) {
        List<Interval> intervalList = new ArrayList<>();
        for (int i = 0; i < intervals.length; i++) {
            Interval interval = new Interval();
            interval.start = intervals[i][0];
            interval.end = intervals[i][1];
            intervalList.add(interval);
        }
        Collections.sort(intervalList, new IntervalComparator());
        LinkedList<Interval> merged = new LinkedList<Interval>();
        for (Interval interval : intervalList) {
            if (merged.isEmpty() || merged.getLast().end < interval.start) {
                merged.add(interval);
            } else {
                merged.getLast().end = Math.max(merged.getLast().end, interval.end);
            }
        }
        int [][] mergeArray = new int[merged.size()][2];
        int i = 0;
        for (Interval interval : merged) {
            mergeArray[i][0] = interval.start;
            mergeArray[i][1] = interval.end;
            i++;
        }
        return mergeArray;
    }

    class Interval {
        int start;
        int end;
    }
}
  • 接雨水(给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。示例: 输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6)
class Solution {
    public int trap(int[] height) {
        int left = 0, right = height.length - 1;
        int ans = 0;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            if (height[left] < height[right]) {
                if (height[left] >= leftMax) {
                    leftMax = height[left];
                } else {
                    ans += (leftMax - height[left]);
                }
                ++left;
            } else {
                if (height[right] >= rightMax) {
                    rightMax = height[right];
                } else {
                    ans += (rightMax - height[right]);
                }
                --right;
            }
        }
        return ans;
    }
}

7.6.3 链表和树

  • 合并两个有序链表(将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null && l2 == null) {
            return null;
        }
        ListNode top = new ListNode(0);
        ListNode currsor = top;
        while (l1 != null || l2 != null) {
            if(l1 == null) {
                currsor.next = new ListNode(l2.val);
                l2 = l2.next;
            } else if (l2 == null) {
                currsor.next = new ListNode(l1.val);
                l1 = l1.next;
            } else {
                int left = l1.val;
                int right = l2.val;
                if(left > right) {
                    currsor.next = new ListNode(right);
                    l2 = l2.next;
                } else {
                    currsor.next = new ListNode(left);
                    l1 = l1.next;
                }    
            }
            currsor = currsor.next;

            System.out.println(currsor.val);
        }
        return top.next;
    }
}
  • 反转链表(反转一个单链表。示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode tail = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = tail;
            tail = head;
            head = next;
        }

        return tail;
    }
}
  • 两数相加(给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。您可以假设除了数字 0 之外,这两个数都不会以 0 开头。示例: 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode sumListNode = null;
        ListNode nextNode = null;
        int tenNum = 0;
        while(l1 != null || l2 != null) {
            int l1Val = 0;
            if(l1 != null) {
                l1Val = l1.val;
            }
            int l2Val = 0;
            if(l2 != null) {
                l2Val = l2.val;
            }
            int sum = l1Val + l2Val + tenNum;
            int unitNum = sum % 10;
            tenNum = sum / 10;
            if(nextNode == null) {
                nextNode = new ListNode(unitNum);
                sumListNode = nextNode;
            } else {
                nextNode.next = new ListNode(unitNum);
                nextNode = nextNode.next;
            }
            if(l1 != null) {
                l1 = l1.next;    
            }
            if(l2 != null) {
                l2 = l2.next;    
            }

        }
        if(tenNum != 0) {
            nextNode.next = new ListNode(tenNum);
            nextNode = nextNode.next;
        }
        return sumListNode;
    }
}
  • 排序链表(在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。示例: 输入: 4->2->1->3 输出: 1->2->3->4; 输入: -1->5->3->4->0 输出: -1->0->3->4->5)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode prev = null;
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            prev = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        prev.next = null;
        ListNode left = sortList(head);
        ListNode right = sortList(slow);
        return merge(left, right);
    }

    private ListNode merge(ListNode left, ListNode right) {
        ListNode p = new ListNode(0);
        ListNode result = p;
        while (left != null && right != null) {
            if (left.val < right.val) {
                result.next = left;
                left = left.next;
            } else {
                result.next = right;
                right = right.next;
            }

            result = result.next;
        }
        if (left != null) {
            result.next = left;
        }

        if (right != null) {
            result.next = right;
        }

        return p.next;
    }
}
  • 环形链表 II(给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。示例: 输入:head = [3,2,0,-4], pos = 1 输出:tail connects to node index 1 解释:链表中有一个环,其尾部连接到第二个节点。输入:head = [1,2], pos = 0 输出:tail connects to node index 0 解释:链表中有一个环,其尾部连接到第一个节点。)
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode cursor = head;
        ListNode prev = null;
        Set<ListNode> visited = new HashSet<ListNode>();
        while (cursor != null) {
            if (visited.contains(cursor)) {
                prev = cursor;
                break;
            }
            visited.add(cursor);
            cursor = cursor.next;
        }

        return prev;
    }
}
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;

        ListNode a = headA;
        ListNode b = headB;

        while (a != b) {
            a = a == null ? headB : a.next;
            b = b == null ? headA : b.next;
        }

        return a;
    }
}
  • 合并K个排序链表(合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。示例: 输入: [1->4->5,1->3->4,2->6]输出: 1->1->2->3->4->4->5->6)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) {
            return null;
        } else if (lists.length == 1) {
            return lists[0];
        } else if (lists.length == 2) {
            return merge2Lists(lists[0], lists[1]);
        }
        int mid = lists.length/2;
        ListNode[] l1 = new ListNode[mid];
        for(int i = 0; i < mid; i++){
            l1[i] = lists[i];
        }

        ListNode[] l2 = new ListNode[lists.length-mid];
        for(int i = mid,j=0; i < lists.length; i++,j++){
            l2[j] = lists[i];
        }

        return merge2Lists(mergeKLists(l1),mergeKLists(l2));
    }

    public ListNode merge2Lists(ListNode leftList, ListNode rightList) {
        ListNode p = new ListNode(0);
        ListNode cursor = p;
        while (leftList != null && rightList != null) {
            if (leftList.val < rightList.val) {
                cursor.next = leftList;
                leftList = leftList.next;
            } else {
                cursor.next = rightList;
                rightList = rightList.next;
            }
            cursor = cursor.next;
        }
        if (leftList != null) {
            cursor.next = leftList;
        }
        if (rightList != null) {
            cursor.next = rightList;
        }
        return p.next;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    TreeNode node = null;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) {
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left == null) {
            return right;
        } else if (right == null) {
            return left;
        } else {
            return root;
        }
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    List<List<Integer>> orderList = new ArrayList<>();
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        if (root == null) return orderList;
        zigzagLevelOrder(root, 0);
        return orderList;
    }

    public void zigzagLevelOrder(TreeNode root, int level) {
        if (orderList.size() == level) {
            List<Integer> orders = new ArrayList<>();
            orderList.add(orders);
        }
        if (level % 2 == 0) {
            orderList.get(level).add(root.val);
        } else {
            orderList.get(level).add(0, root.val);
        }

        if (root.left != null) {
            zigzagLevelOrder(root.left, level + 1);
        }
        if (root.right != null) {
            zigzagLevelOrder(root.right, level + 1);
        }
    }
}

7.6.4 动态或贪心

class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length <= 1) return 0;
        int length = prices.length;
        int diff[] = new int[length - 1];
        for (int i = 0; i < length - 1; i++) {
            diff[i] = prices[i + 1] - prices[i];
        }
        int dp[] = new int [length];
        dp[0] = 0;
        int maxProfit = 0;
        for (int i = 1; i < length; i++) {
            if (dp[i - 1] + diff[i - 1] < diff[i - 1]) {
                dp[i] = diff[i - 1];
            } else {
                dp[i] = dp[i - 1] + diff[i - 1];
            }
            maxProfit = Math.max(maxProfit, dp[i]);
        }
        return maxProfit;
    }
}
  • 最大子序和(给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。)
class Solution {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) return -1;
        int length = nums.length;
        int[] dp = new int[length];
        dp[0] = nums[0];
        int maxSum = dp[0];
        for (int i = 1; i < length; i++) {
             if (dp[i - 1] + nums[i] < nums[i]) {
                 dp[i] = nums[i];
             } else {
                 dp[i] = nums[i] + dp[i - 1];
             }
            maxSum = Math.max(maxSum, dp[i]);
        }
        return maxSum;
    }
}
class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int dp[] = new int[triangle.size() + 1];
        for (int i = triangle.size() - 1;i >= 0;i--) {
            for (int j = 0; j < triangle.get(i).size(); j++) {
                dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
            }
        }
        return dp[0];
    }
}

7.6.5 数据结构

7.6.6 扩展联系