[Leetcode][LinkedList]链表相关题目汇总/分析/总结

目录

  1. Reverse BST
  2. Add Number
  3. Remove
  4. Sort
  5. Merge
  6. Cycle
  7. Others

A LinkedList Definition

 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }

题目详解


#206 Reverse Linked List
  • Sol1: 递归 recursion
    Sol1.1 从前往后
    help函数参数为cur要处理的list,pre为已处理过的list
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        return help(head, null);
}
    public ListNode help(ListNode cur, ListNode pre){
        if(cur.next == null){
            cur.next = pre;
            return cur;
        }
        ListNode n = cur.next;
        cur.next = pre;
        return help(n, cur);
    }

Sol1.2 从后往前
先循环找到最后面的Node,开始处理依次翻转整个链表。处理返回翻转后的子链表

public ListNode help2(ListNode cur){
        if(cur.next == null){
            return cur;
        }
        ListNode nex = cur.next;
        ListNode res = help2(cur.next);
        nex.next = cur;
        cur.next = null;
        return res;
    }
  • Sol2: 非递归
    Sol2.1 通过stack的方式
public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        Stack<ListNode> a = new Stack<>();
        while(head.next != null){
            a.push(head);
            head = head.next;
        }
        ListNode reverse = new ListNode(head.val);
        ListNode res = reverse;
        while(!a.isEmpty()) {
            reverse.next = a.pop();
            reverse = reverse.next;
        }
        reverse.next = null;
        return res;
    }

Sol2.2 通过迭代iteration的方式
三个指针来做循环,前中后,每次改变cur指针next指向,然后移动三个指针到下一个节点

        ListNode prev = null;
        ListNode cur = head;
        while(cur!=null){
            ListNode next = cur.next; 
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        return prev;

#92 Reverse Linked List II

Reverse a linked list from position m to n. Do it in one-pass.

  • sol1: 指针
public ListNode reverseBetween(ListNode head, int m, int n) {
        if(head == null || head.next == null){return head;}
        if(n == 1){return head;}
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode A = dummy;
        ListNode B = dummy.next;
        int copy_m = m;
        while(m!=1){
            A = A.next;
            B = B.next;
            m--;
        }
        //ListNode C = B.next;
        while(copy_m != n){
            if(B.next == null){break;}
            ListNode tmp = B.next.next;
            B.next.next = A.next;
            A.next = B.next;
            B.next = tmp; 
            copy_m++;
        }
        return dummy.next;
    }
  • Sol2: 非递归--stack的方式
public ListNode reverseBetween(ListNode head, int m, int n) {
        if(head == null || head.next == null || m >= n){return head;}
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        head = dummy;
        
        ListNode pre = head;
        for(int i = 1; i < m; i++){
            pre = pre.next;
        }
        
        ListNode tmp = pre.next;
        
        Stack<ListNode> s = new Stack<>();
        int i = 0;
        while(m+i <= n){
            s.push(tmp);
            tmp = tmp.next;
            i++;
        }
        
        ListNode post = tmp;
        
        ListNode rev = s.pop();
        tmp = rev;
        while(!s.isEmpty()){
            tmp.next = s.pop();
            tmp = tmp.next;
        }
        
        pre.next = rev;
        tmp.next = post;
        
        return dummy.next;
    }

#24 Swap Nodes in Pairs

交换链表中相邻的两个元素。

  • sol
    交换A-B-C-D中的B和C需要做如下操作:
    将A指向C
    将B指向D
    将C指向B
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode tmp = dummy; //tmp==>A
        while(tmp.next!=null && tmp.next.next!=null){
            ListNode B = tmp.next;
            ListNode C = tmp.next.next;
            tmp.next = C;
            B.next = C.next;
            C.next = B;
            tmp = tmp.next.next;
        }
        return dummy.next;
    }

#25 Reverse Nodes in k-Group

将一个链表中每k个数进行翻转,末尾不足k个的数不做变化。
A->B->C->D->E,现在我们要翻转BCD三个节点。进行以下几步:
1.C->B
2.D->C
3.B->E
4.A->D
5.返回及节点B

    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null || k < 2) {
            return head;
        }       
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;
        ListNode cur = dummy.next;
        ListNode end = cur;
        int count = 0;
        while(end != null){
            end = end.next;
            count++;
            if(count % k == 0){
                while(cur.next!=end){
                    ListNode tmp = cur.next.next;
                    cur.next.next = pre.next; //c-b
                    pre.next = cur.next;
                    cur.next = tmp;
                }
                pre = cur;
                cur = cur.next;
            }
        }
        return dummy.next;
    }

#61 Rotate List

将一个链表中的元素向右旋转k个位置。

  • sol
    先要用一个count变量求出链表的长度。而k%count就是循环右移的步数。
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null || k == 0) return head;
        ListNode p = head;
        int count = 1;
        while(p.next!=null){
            p = p.next;
            count++;
        }
        k = k % count;
        if(k==0){return head;}
        ListNode fast = head;
        ListNode slow = head;
        while(k!=0){
            fast = fast.next;
            k--;
        }
        while(fast.next!=null){
            slow = slow.next;
            fast = fast.next;
        }
        p.next = head;
        head = slow.next;
        slow.next = null;
        return head;
    }

#86 Partition List

给定一个链表以及一个目标值,把小于该目标值的所有节点都移至链表的前端,大于或等于目标值的节点移至链表的尾端,同时要保持这两部分在原先链表中的相对位置。

  • sol 两个指针
    一个负责收集比目标小的,一个收集大于等于目标的。
    public ListNode partition(ListNode head, int x) {
        if(head == null || head.next == null){return head;}
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode smallDummy = new ListNode(0);;
        ListNode largeDummy = new ListNode(0);;
        ListNode smallCur = smallDummy;
        ListNode largeCur = largeDummy;
        while(dummy.next!=null){
            ListNode p = dummy.next;
            if(p.val >= x){
                largeCur.next = p;
                largeCur = largeCur.next;
            }else{
                smallCur.next = p;
                smallCur = smallCur.next;
            }
            dummy = dummy.next;
        }
        smallCur.next = largeDummy.next;
        largeCur.next = null;
        return smallDummy.next;
    }

#328 Odd Even Linked List

奇数位置组合在一起,偶数位置的组合在一起。O(1)空间复杂度,O(n)时间复杂度

  • sol 链表指针处理
public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null){return head;}
        ListNode odd = head;
        ListNode even = head.next;
        while(even != null && even.next != null){
            ListNode tmp = even.next;
            even.next = tmp.next;
            tmp.next = odd.next;
            odd.next = tmp;
            odd = tmp;
            even = even.next;
        }
        return head;
    }

#725 Split Linked List in Parts
  • Sol Split Input List
    there are N / k items in each part, plus the first N % k parts have an extra item. We can count N with a simple loop.
    public ListNode[] splitListToParts(ListNode root, int k) {
        ListNode p = root;
        int count = 0;
        while(p != null){
            p = p.next;
            count++;
        }
        int team = count / k; //3
        int left = count % k; //1
        ListNode[] res = new ListNode[k];
        for(int i = 0; i < k; i++){
            res[i] = root;
            for(int j = 0; j < team-1 && root!=null; j++){
                root = root.next;
            }
            if(left > 0 && root != null && team > 0){
                root = root.next;
                left--;
            }
            if(root != null){
                ListNode recode = root.next;
                root.next = null;
                root = recode;
            }
        }
        return res;
    }

#2 Add Two Numbers

给定两个链表分别代表两个非负整数。数位以倒序存储,并且每一个节点包含一位数字。将两个数字相加并以链表形式返回

  • Sol1
    遍历两个链表,依次相加,把进位的数字计入下一组相加的数字中。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int carry = 0;
        ListNode res = new ListNode(0);
        ListNode tmp = res;
        while(l1!=null&&l2!=null){

            int sum = l1.val+l2.val;
            int node = carry + sum;
            carry = 0;
            if(node>9){
                carry++;
            }
            res.next = new ListNode(node%10);
            res = res.next;
            l1 = l1.next;
            l2 = l2.next;
        }
        while (l1 != null) {
            int sum = carry + l1.val;
            res.next = new ListNode(sum % 10);
            carry = sum / 10;
            l1 = l1.next;
            res = res.next;
        }

        while (l2 != null) {
            int sum = carry + l2.val;
            res.next = new ListNode(sum % 10);
            carry = sum / 10;
            l2 = l2.next;
            res = res.next;
        }

        if (carry != 0) {
            res.next = new ListNode(carry);
        }

        return tmp.next;
        // int sum = getNum(l1) + getNum(l2);
        // return getList(sum);
    }
  • Sol2 递归
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1 == null || l2 == null){return l1 == null? l2 : l1;}
        int value = l1.val + l2.val;
        ListNode res = new ListNode(value%10);
        res.next = addTwoNumbers(l1.next, l2.next);
        if(value >= 10){
            res.next = addTwoNumbers(new ListNode(value/10), res.next);
        }
        return res;

#445 Add Two Numbers II
  • Sol 栈转存
       public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Stack<Integer> s1 = new Stack<>();
        Stack<Integer> s2 = new Stack<>();
        while(l1 != null){
            s1.add(l1.val);
            l1 = l1.next;
        }
        while(l2 != null){
            s2.add(l2.val);
            l2 = l2.next;
        }
        int carry = 0;
        ListNode res = new ListNode(0);
        while(!s1.isEmpty() || !s2.isEmpty()){
            int value = carry;
            if(!s1.isEmpty()) value += s1.pop();
            if(!s2.isEmpty()) value += s2.pop();
            // int value = s1.pop() + s2.pop() + carry;
            ListNode tmp = new ListNode(value % 10);
            tmp.next = res.next;
            res.next = tmp;
            carry = value / 10;
        }
        if(carry != 0){
            ListNode tmp = new ListNode(carry);
            tmp.next = res.next;
            res.next = tmp;
        }
        return res.next;
    }

#19 Remove Nth Node From End of List

删除链表中倒数第n个节点

  • Sol 双指针
    加入虚假头节点dummy,使用双指针fast,slow。fast从dummy开始先移动n位。然后fast与slow同时移动,直到fast.next==null,此时slow.next为要删除的节点
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode slow = res;
        ListNode fast = res;
        if(head == null) {
            return null;
        }
        if(head.next == null && n==1){
            return null;
        }
        while(n != 0){
            n--;
            fast = fast.next;
        }
        while(fast.next!=null){
            slow = slow.next;
            fast = fast.next;
        }
        slow.next = slow.next.next;
        return res.next;
    }

#203 Remove Linked List Elements

删除链表中制定元素

  • Sol 加入虚假头节点dummy
    public ListNode removeElements(ListNode head, int val) {
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode cur = res;
        while(cur.next != null){
            if(cur.next.val == val){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        return res.next;
    }

#237 Delete Node in a Linked List

just for fun吧~~

    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }

#83 Remove Duplicates from Sorted List

删除一个有序链表中重复的元素,使得每个元素只出现一次。

  • Sol 比较当前节点有后一个节点
 public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null){return head;}
        ListNode p = head;
        while(p.next != null){
            if(p.val == p.next.val){
                p.next = p.next.next;
            }else{
                p = p.next;
            }
        }
        return head;
    }

#82 Remove Duplicates from Sorted List II

把一个有序链表中所有重复的数字全部删光,删除后不再有原先重复的那些数字。

  • Sol 加入虚假头节点dummy
public ListNode deleteDuplicates(ListNode head) {
        ListNode res = new ListNode(0);
        res.next = head;
        ListNode cur = res;
        ListNode fast = res.next;
        while(cur.next != null){
            while(fast.next!=null && fast.next.val == cur.next.val){
                fast = fast.next;
            }
            if(cur.next == fast){ //没有重复,两个都进一位
                cur = cur.next;
                fast = fast.next;
            }else{
                cur.next = fast.next;
                fast = fast.next;
            }
        }
        return res.next;
    }

#148 Sort List

Sort a linked list in O(n log n) time using constant space complexity.
由于链表在归并操作时并不需要像数组的归并操作那样分配一个临时数组空间,所以这样就是常数空间复杂度了

  • Sol 归并排序+快慢指针
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next!=null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode head1 = head;
        ListNode head2 = slow.next;
        slow.next = null;
        head1 = sortList(head1);
        head2 = sortList(head2);
        head = merge(head1,head2);
        return head;
    }
    private ListNode merge(ListNode a, ListNode b){
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while(a!=null && b != null){
            if(a.val <= b.val){
                p.next = a;
                a = a.next;
            }else{
                p.next = b;
                b = b.next;
            }
            p = p.next;
        }
        if(a==null){
            p.next = b;
        }
        if(b == null){
            p.next = a;
        }
        return dummy.next;
    }

#147 Insertion Sort List
  • Sol 插入排序
    链表只能从前往后比较
    public ListNode insertionSortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode p = head.next;
        ListNode q = head;
        while(p!=null){
            if(p.val >= q.val){
                q = p;
                p = p.next;
            }else{
                ListNode p_next = p.next;
                ListNode tmp = dummy.next;
                ListNode pre_tmp = dummy;
                while(tmp.val < p.val && tmp != q){
                    tmp = tmp.next;
                    pre_tmp = pre_tmp.next;
                }
                pre_tmp.next = p;
                p.next = tmp;
                q.next = p_next;
                p = p_next;
            }
        }
        return dummy.next;
    }

#143 Reorder List

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

  • Sol 快慢指针+翻转链表+链接链表
    去中间节点,将链表分为两段.
    翻转后一段
    拼接
public void reorderList(ListNode head) {
        if(head == null || head.next == null){
            return;
        }
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next!=null && fast.next.next!=null){
            fast = fast.next.next;
            slow = slow.next;
        }
        fast = slow.next;
        slow.next = null;
        //reverse
        ListNode cur = null;
        while(fast!=null){
            ListNode next = fast.next;
            fast.next = cur;
            cur = fast;
            fast = next;
        }

        slow = head;
        while(slow!=null && cur!=null){
            ListNode tmp = cur.next;
            cur.next = slow.next;
            slow.next = cur;
            slow = slow.next.next;
            cur = tmp;
        }
    }

#21 Merge 2 Sorted Lists

修改链表的指针

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        ListNode head = new ListNode(0);
        ListNode p = head;
        while(l1 != null && l2 != null){
            if(l1.val > l2.val){
                p.next = l2;
                p = l2;
                l2 = l2.next;
            }else{
                p.next = l1;
                p = l1;
                l1 = l1.next;
            }
        }
        if(l1 == null){
            p.next = l2;
        }else{
            p.next = l1;
        }
        return head.next;
    }

#23 Merge k Sorted Lists

将k个排序好的链表合并成新的有序链表

  • sol 最小堆--优先队列
public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length==0){return null;}
        PriorityQueue<Integer> q = new PriorityQueue();
        for(ListNode n: lists){
            while(n!=null){
                q.add(n.val);
                n = n.next;
            }
        }
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while(!q.isEmpty()){
            p.next = new ListNode(q.poll());
            p = p.next;
        }
        return dummy.next;
    }

#160 Intersection of Two Linked Lists
  • sol1 长度
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {        
        int l1 = getLength(headA);
        int l2 = getLength(headB);
        while(l1 < l2){
            headB = headB.next;
            l2--;
        }
        while(l1 > l2){
            headA = headA.next;
            l1--;
        }
        while(headA != headB){
            headA = headA.next;
            headB = headB.next;
        }
        return headA == headB? headA: null;
    }
    public int getLength(ListNode a){
        int count = 0;
        while(a != null){
            count++;
            a = a.next;
        }
        return count;
    }
  • sol2 HashSet
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         Set<ListNode> s = new HashSet<>();
         while(headA!=null){
             s.add(headA);
             headA = headA.next;
         }
         while(headB!=null){
             if(s.contains(headB)){
                 return headB;
             }else{
                 headB = headB.next;
             }
         }
        return null;
}
  • sol3 循环
         ListNode a = headA;
         ListNode b = headB;
         while(a != b){
             a = a == null ? headA : a.next;
             b = b == null ? headB : b.next;
         }
         return a;
sol1,sol3,sol2

#141 Linked List Cycle

判断一个链表中是否存在着一个环,能否在不申请额外空间的前提下完成

  • sol 快慢指针
    public boolean hasCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast !=null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){return true;}
        }
        return false;
    }

#142 Linked List Cycle II

如果给定的单向链表中存在环,则返回环起始的位置,否则返回为空。最好不要申请额外的空间。

  • sol 快慢指针
    因为快指针的速度是慢指针的两倍,所以在相同时间内,它走过的路程是慢指针的两倍。快慢指针在C处相遇,头指针到环起点位置的距离与,C到B的距离相等。
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){break;}
        }
        if(fast == null || fast.next == null){return null;}
        slow = head;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }

#234 Palindrome Linked List
  • sol1 Creat a new reversed list
    The time and space are O(n).
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null){return true;}
        ListNode copyReverse = reverse(head);
        while(head != null){
            if(head.val != copyReverse.val){return false;}
            head = head.next;
            copyReverse = copyReverse.next;
        }
        return true;
    }
    public ListNode reverse(ListNode head){
        ListNode prev = new ListNode(head.val);
        ListNode cur = head;
        while(cur.next!=null){
            ListNode next = new ListNode(cur.next.val); 
            next.next = prev;
            prev = next;
            cur = cur.next;
        }
        return prev;
    }
  • sol2 Break and reverse second half
    The time is O(n) and space is O(1).
public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null){return true;}
        ListNode slow = head, fast = head;
        while(fast.next != null && fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        fast = slow.next;
        slow.next = null;
        ListNode pre = null;
        ListNode cur = fast;
        while(cur != null){
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        slow = head;
        while(pre != null){
            if(slow.val != pre.val){return false;}
            slow = slow.next;
            pre =pre.next;
        }
        return true;
}

总结

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

推荐阅读更多精彩内容