Leetcode算法解析101+

<center>#104 Maximum Depth of Binary Tree</center>

  • link
  • Description:
    • Given a binary tree, find its maximum depth.
    • The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
  • Solution:
    • 递归,以当前节点为根的树最大深度是左右子树中深度更高的那一棵加一
  • Code:
    # code block
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        return Math.max(left, right) + 1;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#118 Pascal's Triangle</center>

  • link
  • Description:
    • Given numRows, generate the first numRows of Pascal's triangle.
  • Input: 5
  • Output:
    [
         [1],
        [1,1],
       [1,2,1],
      [1,3,3,1],
     [1,4,6,4,1]
    ]
    
  • Solution:
    • 模拟题,根据图上的规律来进行循环
    • 注意防止数组越界
  • Code:
    # code block
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> result = new ArrayList<>();
        if (numRows <= 0) {
            return result;
        }
        List<Integer> firstRow = new ArrayList<Integer>();
        firstRow.add(1);
        result.add(firstRow);
        for (int i = 2; i <= numRows; i++) {
            List<Integer> ithRow = new ArrayList<>();
            List<Integer> lastRow = result.get(result.size() - 1);
            for (int j = 0; j < i; j++) {
                if (j == 0 || j == i - 1) {
                    ithRow.add(1);
                } else {
                    ithRow.add(lastRow.get(j - 1) + lastRow.get(j));
                }
            }
            result.add(ithRow);
        }
        return result;
    }
    

<center>#119 Pascal's Triangle II</center>

  • link
  • Description:
    • Given an index k, return the kth row of the Pascal's triangle.
  • Input: 3
  • Output: [1,3,3,1]
  • Assumptions:
    • Could you optimize your algorithm to use only O(k) extra space?
  • Solution:
    • Pascal's Triangle 的follow up,重点在于O(k)的空间复杂度
    • 通过滚动数组的方式可以达到O(k)的空间复杂度
  • Code:
    # code block
    public List<Integer> getRow(int rowIndex) {
        Integer[][] rows = new Integer[2][rowIndex + 1];
        rows[0][0] = 1;
        rows[1][0] = 1;
        for (int i = 0; i <= rowIndex; i++) {
            for (int j = 1; j < i; j++) {
                rows[i % 2][j] = rows[(i + 1) % 2][j - 1] + rows[(i + 1) % 2][j];
            }
            rows[i % 2][i] = 1;
        }
        List<Integer> result = Arrays.asList(rows[rowIndex % 2]);
        return result;
    }
    
  • Time Complexity: O(k ^ 2)
  • Space Complexity: O(k)

<center>#119 Pascal's Triangle II</center>

  • link
  • Description:
    • Given an index k, return the kth row of the Pascal's triangle.
  • Input: 3
  • Output: [1,3,3,1]
  • Assumptions:
    • Could you optimize your algorithm to use only O(k) extra space?
  • Solution:
    • Pascal's Triangle 的follow up,重点在于O(k)的空间复杂度
    • 通过滚动数组的方式可以达到O(k)的空间复杂度
  • Code:
    # code block
    public List<Integer> getRow(int rowIndex) {
        Integer[][] rows = new Integer[2][rowIndex + 1];
        rows[0][0] = 1;
        rows[1][0] = 1;
        for (int i = 0; i <= rowIndex; i++) {
            for (int j = 1; j < i; j++) {
                rows[i % 2][j] = rows[(i + 1) % 2][j - 1] + rows[(i + 1) % 2][j];
            }
            rows[i % 2][i] = 1;
        }
        List<Integer> result = Arrays.asList(rows[rowIndex % 2]);
        return result;
    }
    
  • Time Complexity: O(k ^ 2)
  • Space Complexity: O(k)

<center>#121 Best Time to Buy and Sell Stock</center>

  • link
  • Description:
    • Say you have an array for which the ith element is the price of a given stock on day i.
    • If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
  • Input: [7, 1, 5, 3, 6, 4]
  • Output: 5
  • Solution:
    • 记录当前遇到的最小值,每次将当前卖出与最小值买入所得利润与最大利润进行比较
  • Code:
    # code block
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) {
            return 0;
        }
        int profit = 0;
        int min = prices[0];
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > min && prices[i] - min > profit) {
                profit = prices[i] - min;
            }
            min = Math.min(min, prices[i]);
        }
        return profit;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#122 Best Time to Buy and Sell Stock II</center>

  • link
  • Description:
    • Say you have an array for which the ith element is the price of a given stock on day i.
    • Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
  • Input: [1,2,3,1,6]
  • Output: 7
  • Solution:
    • 这题可以简化为找到所有的递增区间的总增长
  • Code:
    # code block
    public int maxProfit(int[] prices) {
        int profit = 0;
        if (prices == null || prices.length <= 1) {
            return profit;
        }
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > prices[i - 1]) {
                profit = profit + prices[i] - prices[i - 1];
            }
        }
        return profit;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#125 Valid Palindrome</center>

  • link
  • Description:
    • Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
  • Input: "A man, a plan, a canal: Panama"
  • Output: true
  • Solution:
    • 相向型的两根指针题
    • 熟悉Java的Character和String的API会很容易做
  • Code:
    # code block
    public boolean isPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        int start = 0, end = s.length() - 1;
        char[] sc = s.toCharArray();
        while (start < end) {
            while (start < end && !Character.isLetterOrDigit(sc[start])) {
                start++;
            }
            while (start < end && !Character.isLetterOrDigit(sc[end])) {
                end--;
            }
            if (start < end) {
                char startL = Character.toLowerCase(sc[start]);
                char endL = Character.toLowerCase(sc[end]);
                if (startL != endL) {
                    return false;
                }
                start++;
                end--;
            }
        }
        return true;
    }
    
  • Time Complexity: O(n)

<center>#141 Linked List Cycle</center>

  • link
  • Description:
    • Given a linked list, determine if it has a cycle in it.
    • Follow up:
      • Can you solve it without using extra space?
  • Solution:
    • 最简单的想法就是用HashSet存访问过的点,再遇到就返回true
    • 不用额外空间的话,就最好使用快慢指针,如果快指针走到底了,就说明无环,如果快慢指针相遇了,就是有环
  • Code:
    # code block
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head.next;
        ListNode fast = head.next.next;
        while (fast != null && fast.next != null) {
            if (fast == slow) {
                return true;
            }
            fast = fast.next.next;
            slow = slow.next;
        }
        return false;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#Find Minimum in Rotated Sorted Array</center>

  • link

  • Description:

    • Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
    • Find the minimum element.
  • Input: [4 5 6 7 0 1 2]

  • Output: 4

  • Assumptions:

    • You may assume no duplicate exists in the array.
  • Solution:

    • rotated排序数组, 无重复, 满足使用二分法的条件。 取中点的值, 如果小于最后一个值, 说明从中点到最后一个值为递增,中间不会有最小值, 反之则在。
    • 注意点:
      • Corner case: 数组是一个未翻转的排序数组。
  • Code:

    # code block
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int start = 0, end = nums.length - 1;
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] < nums[end]) {
                end = mid;
            } else {
                start = mid;
            }
        }
        return nums[start] < nums[end] ? nums[start] : nums[end];
    }
    
    
  • Time Complexity: O(lg n)

  • Space Complexity: O(1)

<center>#154 Find Minimum in Rotated Sorted Array II</center>

  • link

  • Description:

    • Follow up for "Find Minimum in Rotated Sorted Array":
      • What if duplicates are allowed?
      • Would this affect the run-time complexity? How and why?
    • Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
      (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).
    • Find the minimum element.
  • Input: [4 5 6 7 0 0 1 2]

  • Output: 4

  • Assumptions:

    • The array may contain duplicates.
  • Solution:

    • 复杂度上升到O(n), 不能用二分法。这题重点是不能使用二分法的原因。

    • 二分法的本质是通过每一次判断舍弃一半的不可能为答案的值, 或者保留有可能是答案的那一半的值。

    • 假设一个数组全是0, 其中有一个-1. 当我们尝试做二分,不可能在O(1)的时间内发现答案会在哪一边。

    • Code:

    # code block
    public int findMin(int[] nums) {
        int result = Integer.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) {
            result = Math.min(nums[i], result);
        }
        return result;
    }
    
    
  • Time Complexity: O(n)

  • Space Complexity: O(1)

<center>#160 Intersection of Two Linked Lists</center>

  • link
  • Description:
    • Write a program to find the node at which the intersection of two singly linked lists begins.
  • Input:
    A:          a1 → a2
                       ↘
                         c1 → c2 → c3
                       ↗
    B:     b1 → b2 → b3
    
  • Output: c1
  • Solution:
    • 这道题的基础解法是使用hashtable遍历其中一条链表,存储经过的节点,在与另一个链表一一比较
    • 最优的解法请参考如下solution,从算法的角度来讲记下来就好,如果有兴趣做数学证明的也可以尝试一下
  • Code:
    # code block
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode pt = headA;
        while (pt.next != null) {
            pt = pt.next;
        }
        pt.next = headB;
        ListNode slow = headA.next;
        ListNode fast = headA.next.next;
        while (fast != null && fast.next != null && slow != fast) {
            fast = fast.next.next;
            slow = slow.next;
        }
        if (fast == null || fast.next == null) {
            pt.next = null;
            return null;
        }
        while (slow != headA) {
            slow = slow.next;
            headA = headA.next;
        }
        pt.next = null;
        return slow;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#162 Find Peak Element</center>

  • link
  • Description:
    • A peak element is an element that is greater than its neighbors.
    • Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.
    • The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
  • Input: [1, 2, 3, 1]
  • Output: 2
  • Assumptions:
    • You may imagine that num[-1] = num[n] = -∞.
  • Solution:
    • 二分法, 如果中间值大于左右邻居则为peak, 如果中间值小于右邻居,则保证右边肯定有peak, 如果中间值小于左邻居,则保证左边肯定有peak,保留有peak的一半。
  • Code:
    # code block
    public int findPeakElement(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0, end = nums.length - 1;
        while (start < end - 1) {
            int mid = start + (end - start) / 2;
            if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) {
                return mid;
            } else if (nums[mid] > nums[mid - 1]) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return nums[start] > nums[end] ? start : end;
    }
    
    
  • Time Complexity: O(lg n)
  • Space Complexity: O(1)

<center>#167 Two Sum II - Input array is sorted </center>

  • link

  • Description:

    • Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.
    • The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
    • You may assume that each input would have exactly one solution and you may not use the same element twice.
  • Input: numbers={2, 7, 11, 15}, target=9

  • Output: index1=1, index2=2

  • Assumptions:

    • each input would have exactly one solution
    • you may not use the same element twice
  • Solution:

    • Two Sum 的题一般有两种解法, 一种是用hash, 一种是用两根指针。
    • 对于本题来说,在排序数组中两根指针时间复杂度为O(n)与hash相同, 空间复杂度为O(1)优于hash的O(n)。
    • 所以Two Sum加排序数组就意味着最优解是使用两根指针.初始化左指针left指向数组起始,初始化右指针right指向数组结尾。根据已排序这个特性:
      *如果numbers[left]与numbers[right]的和value小于target,说明应该增加value,因此left右移指向一个较大的值。
      *如果value大于target,说明应该减小value,因此right左移指向一个较小的值。
      *value等于target,则找到,返回left+1和right+1。
      *注意点:
      *注意返回值是1-based索引,不是0-based索引
      *两个int相加可能会溢出, 但是使用int可以通过leetcode的test case, 用long更优
  • Code:

    # code block
    public int[] twoSum(int[] numbers, int target) {
        if (numbers == null || numbers.length < 2) {
            return new int[0];
        }
        int[] result = new int[0];
        // 初始化两根指针
        int left = 0, right = numbers.length - 1;
        while (left < right) {
            int val = numbers[left] + numbers[right];
            if (val == target) {
                // find the answer and return it
                result = new int[2];
                // the result should be 1-based index not the 0-based
                result[0] = left + 1;
                result[1] = right + 1;
                return result;
            } else if (val > target) {
                // drop the right
                right--;
            } else {
                // drop the left
                left++;
            }
        }
        return result;
    }
    
    
  • Time Complexity: O(n)

  • Space Complexity: O(1)

<center>#175 Combine Two Tables</center>

  • link
  • Description:
    • Write a SQL query for a report that provides the following information for each person in the Person table, regardless if there is an address for each of those people:
    • FirstName, LastName, City, State
  • Input:
    Table: Person
    +-------------+---------+
    | Column Name | Type    |
    +-------------+---------+
    | PersonId    | int     |
    | FirstName   | varchar |
    | LastName    | varchar |
    +-------------+---------+
    PersonId is the primary key column for this table.
    Table: Address
    +-------------+---------+
    | Column Name | Type    |
    +-------------+---------+
    | AddressId   | int     |
    | PersonId    | int     |
    | City        | varchar |
    | State       | varchar |
    +-------------+---------+
    AddressId is the primary key column for this table.
    
  • Solution:
    • 因为每个人不管地址存不存在都要在新表中,所以用person表左外连接address表
  • Code:
    # code block
    SELECT a.FirstName, a.LastName, b.City, b.State
    FROM Person a
    LEFT OUTER JOIN Address b
    on a.PersonId = b.PersonId;
    

<center>#189 Rotate Array</center>

  • link
  • Description:
    • Rotate an array of n elements to the right by k steps.
    • For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
      Could you do it in-place with O(1) extra space?
  • Input: [1,2,3,4,5,6,7] 3
  • Output: [5,6,7,1,2,3,4]
  • Assumptions:
    • 三步翻转法,原理可以通过折纸来模拟。解决O(n)时间O(1)空间数组或者字符串左右移的最优方法。
  • Solution:
  • Code:
    # code block
    public void rotate(int[] nums, int k) {
        if (nums == null || nums.length < 2) {
            return;
        }
        int n = nums.length;
        k = k % n;
        if (k == 0) {
            return;
        }
        reverse(nums, 0, n - k - 1);
        reverse(nums, n - k, n - 1);
        reverse(nums, 0, n - 1);
    }
    
    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int tmp = nums[start];
            nums[start] = nums[end];
            nums[end] = tmp;
            start++;
            end--;
        }
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#206 Reverse Linked List</center>

  • link
  • Description:
    • Reverse a singly linked list.
  • Input: [1,2,3,4]
  • Output: [4,3,2,1]
  • Solution:
    • 链表头的next记得置null
  • Code:
    # code block
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode prev = head;
        ListNode next = head.next;
        while (next != null) {
            ListNode tmp = next.next;
            next.next = prev;
            prev = next;
            next = tmp;
        }
        head.next = null;
        return prev;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(1)

<center>#216 Combination Sum III</center>

  • link
  • Description:
    • Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
  • Input: k = 3, n = 9
  • Output: [[1,2,6], [1,3,5], [2,3,4]]
  • Solution:
    • 典型的隐式图DFS遍历, 与combination sum的区别在于多了combination元素个数的限制, 解决方案是在dfsHelper中加入一个变量记录还需要的元素的数量。
  • Code:
    # code block
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> result = new ArrayList<>();
        if (k == 0 || n <= 0) {
            return result;
        }
        dfsHelper(1, n, k, new ArrayList<Integer>(), result);
        return result;
    }
    
    private void dfsHelper(int start, int n, int k, List<Integer> state, List<List<Integer>> result) {
        if (k == 0 && n == 0) {
            result.add(new ArrayList<Integer>(state));
            return;
        }
        if (k <= 0 || n <= 0) {
            return;
        }
        for (int i = start; i <= 9; i++) {
            if (i <= n) {
                state.add(i);
                dfsHelper(i + 1, n - i, k - 1, state, result);
                state.remove(state.size() - 1);
            }
        }
    }
    

<center>#217 Contains Duplicate</center>

  • link
  • Description:
    • Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.
  • Input: [1,1]
  • Output: true
  • Solution:
    • 判断重复使用哈希表
  • Code:
    # code block
    public boolean containsDuplicate(int[] nums) {
        if (nums == null || nums.length <= 1) {
            return false;
        }
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (set.contains(nums[i])) {
                return true;
            }
            set.add(nums[i]);
        }
        return false;
    }
    
  • Time Complexity: O(n)
  • Space Complexity: O(n)

<center>#283 Move Zeroes</center>

  • link

  • Description:

    • Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.
  • Input: [0, 1, 0, 3, 12]

  • Output: [1, 3, 12, 0, 0]

  • Assumptions:

    • You must do this in-place without making a copy of the array.
    • Minimize the total number of operations.
  • Solution:

    • 最基本的两根指针问题,要求in-place并且最小化操作数量。最能理解的方法是循环一遍,去零, 再将后面意义置零。假设这个数组大部分都是零,那么总操作数就大约是两次循环。
    • 优化的办法是使用快慢指针。 定义左指针左边的数不是零, 遍历数组,一旦遇到不是零的就将左右指针的值交换, 是零就将右指针又移一格。这样就只遍历数组一遍。
    • 该算法保证了如果右指针大于左指针, 那么左指针向右(包含左指针), 右指针向左(不包含右指针)的区域内全部都是0.
  • Code:

    # code block
    public void moveZeroes(int[] nums) {
        if (nums == null || nums.length == 0) {
            return;
        }
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[right] != 0) {
                int tmp = nums[right];
                nums[right] = nums[left];
                nums[left] = tmp;
                left++;
            }
            right++;
        }
    }
    
  • Time Complexity: O(n)

  • Space Complexity: O(1)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容