算法筆記:鏈表

//leetcode中還有花樣鏈表題,這里幾個例子,冰山一角

  1. 求單鏈表中結點的個數----時間復雜度O(n)
    這是最最基本的了,應該能夠迅速寫出正確的代碼,注意檢查鏈表是否為空。參考代碼如下:
    public static int getListLength(Node head) {
        // 注意頭結點為空情況
        if (head == null) {
            return 0;
        }

        int len = 0;
        Node cur = head;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        return len;
    }
  1. 將單鏈表反轉----時間復雜度O(n)(也可以先inplace的翻轉,再遍歷,空間復雜度降到O(1))
  2. 從頭到尾遍歷原鏈表,每遍歷一個結點,將其摘下放在新鏈表的最前端。
  3. 如果鏈表為空或只有一個節點,無需反轉,直接返回原鏈表表頭。
    注意鏈表為空和只有一個結點的情況。參考代碼如下:
public static Node reverseList(Node head) {
        if (head == null || head.next == null) {
            return head;
        }

        Node reHead = null;         // 反轉后新鏈表最前面的node
        Node cur = head;

        while (cur != null) {
            Node preCur = cur;      // 用preCur保存住對要處理節點的引用
            cur = cur.next;             // cur更新到下一個節點
            preCur.next = reHead;   // 更新當前節點的next引用,當前.next指向 翻轉后鏈表最前面的node ==>當前節點成為翻轉后鏈表最前面的node
            reHead = preCur;            // reHead更新
        }
        return reHead;
    }
  1. leetcode例題

206. Reverse LinkedList

沒做過一上來感覺不太好想,有個視頻,看到一半恍然大悟:https://www.youtube.com/watch?v=sYcOK51hl-A
這類題都開始要求用iteration寫一次再用recursive寫一個

 //假設1->2->3->4,先從head=1開始,要翻轉,最后一個會變成head,所以head一步一步向后挪,每一步也一起翻轉指向
public class Solution {
            public ListNode reverseList(ListNode head) {
                ListNode prev = null;
                while(head!=null){
                    ListNode nextHead = head.next;
                    head.next = prev;
                    prev=head;
                    //這里得到的prev即下一個head前面的數,也就是下一個head要指向的數,當head=最后一個node(tail)時,prev=tail,循環結束
                    head = nextHead;
                } 
                return prev;
            }
            //recursive
            public ListNode reverseList(ListNode head) {
                return reverseRecursive(head,null);
            }
            public ListNode reverseRecursive(ListNode head,ListNode prev){
                if(head==null) return prev;
                ListNode nextHead = head.next;
                head.next=prev;
                //下面傳參其實就相當于這兩句:prev=head;head = nextHead;
                return reverseRecursive(nextHead,head);
            }
        }

92. Reverse Linked List II

For example:
Given 1->2->3->4->5->NULL, m = 2 and n = 4,
return 1->4->3->2->5->NULL.

根據題中的例子,第二個for循環開始
loop1:
1 --> 2 --> 3 --> 4 --> 5 --> NULL
p     c     n
cur.next = next.next;
2 --> 4
next.next = prev.next;
3 --> 2
prev.next = next;
1 --> 3
==> 1 --> 3 --> 2 --> 4 --> 5 --> NULL
    p          c      n
loop2:
cur.next = next.next;
2 --> 5
next.next = prev.next;
4 --> 3
prev.next = next;
1 --> 4
==> 1 --> 4 --> 3 --> 2 --> 5 --> NULL
public ListNode reverseBetween(ListNode head, int m, int n) {
            if(m == n) return head;
            ListNode dummy = new ListNode(0);
            dummy.next = head;
            ListNode prev = dummy;
            ListNode cur = head;
            //在reverse之前的部分平移
            for(int i = 0; i < m - 1; i++){
                prev = prev.next;
                cur = cur.next;
            }
            for(int i = 0; i < n - m; i++){
                ListNode next = cur.next;
                cur.next = next.next;
                next.next = prev.next;
                prev.next = next;
            }
            return dummy.next;
        }
  1. 查找單鏈表中的倒數第K個結點(k > 0)----時間復雜度O(n)
  2. 最普遍的方法是,先統計單鏈表中結點的個數,然后再找到第(n-k)個結點。注意鏈表為空,k為0,k為1,k大于鏈表中節點個數時這三種情況情況。代碼不寫了。
  3. 另一個思路主要就是使用兩個指針,先讓前面的指針走到正向第k個結點,這樣前后兩個指針的距離差是k-1,之后前后兩個指針一起向前走,前面的指針走到最后一個結點時,后面指針所指結點就是倒數第k個結點。

19. Remove Nth Node From End of List

walker and runner, init walker,runner both as dummy, move runner n steps, so that the gap between runner and walker =n, then move runner and walker together, when runner get to the end of List, walker is before the nth from the end node, walker.next=walke.next.next, skip original walker.next

public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummy = new ListNode(0);
            dummy.next = head;
            ListNode walker = dummy;
            ListNode runner = dummy;
            // after for loop, gap between runner and walker =n
            for(int i = 1; i <= n; i++){
                runner = runner.next;
            }
            while(runner.next!=null){
                runner = runner.next;
                walker = walker.next;
            }
            walker.next=walker.next.next;//skip nth node
            return dummy.next;
        }
  1. 查找單鏈表的中間結點----時間復雜度O(n)
    此題可應用于上一題類似的思想。也是設置兩個指針,只不過這里是,兩個指針同時向前走,walker one step each time,runner two steps each time,runner get to the end, walker==mid,即第(n/2+1)個結點。還是分三種情況:鏈表結點個數為1和2的情況和其他。
public static Node getMiddleNode(Node head) {
        if (head == null || head.next == null) {
            return head;
        }
        Node walker = head;     ] 
        Node runner = head;
        // 前面指針每次走兩步,直到指向最后一個結點,后面指針每次走一步
        while (walker.next != null) {
            walker = walker.next;
            runner = runner.next;
            if (runner.next != null) {
                runner = runner.next;
            }
        }
        return walker;
    }
  1. 從尾到頭打印單鏈表----時間復雜度O(n)
    對于這種顛倒順序的問題,我們應該就會想到棧,后進先出。所以,這一題要么自己使用棧,要么讓系統使用棧,也就是遞歸。注意鏈表為空的情況。
public static void reversePrintListStack(Node head) {
        Stack<Node> s = new Stack<Node>();
        Node cur = head;
        while (cur != null) {
            s.push(cur);
            cur = cur.next;
        }
        while (!s.empty()) {
            cur = s.pop();
            System.out.print(cur.val + " ");
        }
    }
    /**
    使用遞歸(優雅!)
     */
    public static void reversePrintListRec(Node head) {
        if (head == null) {
            return;
        } else {
            reversePrintListRec(head.next);
            System.out.print(head.val + " ");
        }
    }
  1. Merge two sorted linked list----時間復雜度為O(max(len1, len2)),O(1)的空間
    已知兩個單鏈表pHead1 和pHead2 各自有序,把它們合并成一個鏈表依然有序
    這個類似歸并排序。尤其注意兩個鏈表都為空,和其中一個為空時的情況。

21. Merge Two Sorted Lists

還是iteration和recursion,iteration代碼太長了,由此可見遞歸的好處,代碼簡介易懂
iteration注意 l1,l2挨個merge的時候為了方便,l1,l2在merge后指向自己next,即后移,同時head即新鏈表的當前node也后移,另外這里也是head不確定的情況,所以用dummy

//recursion
public ListNode mergeTwoLists(ListNode l1, ListNode l2){
    if(l1 == null) return l2;
    if(l2 == null) return l1;
    if(l1.val < l2.val){
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else{
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
}
//iteration
public static Node mergeSortedList(Node head1, Node head2) {
        // 其中一個鏈表為空的情況,直接返回另一個鏈表頭,O(1)
        if (head1 == null) {
            return head2;
        }
        if (head2 == null) {
            return head1;
        }
        Node mergeHead = null;
        // 先確定下來mergeHead是在哪里
        if (head1.val < head2.val) {
            mergeHead = head1;
            head1 = head1.next;         // 跳過已經合并了的元素,指向已merge的node的next
            mergeHead.next = null;  // 斷開mergeHead和后面的聯系
        } else {
            mergeHead = head2;
            head2 = head2.next;
            mergeHead.next = null;
        }
        Node mergeCur = mergeHead;
        while (head1 != null && head2 != null) {
            if (head1.val < head2.val) {
                mergeCur.next = head1;       // 把找到較小的元素合并到merge中
                head1 = head1.next;              // 跳過已經合并了的元素
                mergeCur = mergeCur.next;    // 找到下一個準備合并的元素
                mergeCur.next = null;            // 斷開mergeCur和后面的聯系
            } else {
                mergeCur.next = head2;
                head2 = head2.next;
                mergeCur = mergeCur.next;
                mergeCur.next = null;
            }
        }
        // 合并剩余的元素,這個很重要,而且大部分merge算法最后都需要merge剩余東西這一步
        if (head1 != null) {
            mergeCur.next = head1;
        } else if (head2 != null) {
            mergeCur.next = head2;
        }
        return mergeHead;
    }

23. Merge k Sorted Lists

根據priority queue的特性,我們可以通過重寫compare方法利用priority queue實現,還有dummy,從后向前拼接。
和下面sort里179一樣,都重寫了compare。一個是sort方法內,一個是priority queue

      public ListNode mergeKLists(ListNode[] lists) {
          if (lists==null||lists.length==0) return null;
          PriorityQueue<ListNode> queue= new PriorityQueue<ListNode>(lists.length,new Comparator<ListNode>(){
              @Override
              /*
              1. 這里compare方法可以直接return n1.val-n2.val;
              */
              public int compare(ListNode n1, ListNode n2){
                  if(n1.val<n2.val) return -1;
                  else if(n1.val==n2.val) return 0;
                  else return 1;
              }
          });
          ListNode dummy = new ListNode(0);
          ListNode tail = dummy;
          for(ListNode n:lists){
              if(n!=null) queue.add(n);
          }
          while(!queue.isEmpty()){
              tail.next = queue.poll();
              tail=tail.next;
              if(tail.next!=null){
                  queue.add(tail.next);
              } 
          }
          return dummy.next;
      }
  1. 判斷一個單鏈表中是否有環----時間復雜度為O(n)
    這里也是用到兩個指針。如果一個鏈表中有環,也就是說用一個指針去遍歷,是永遠走不到頭的。因此,我們可以用兩個指針去遍歷,一個指針一次走兩步,一個指針一次走一步,如果有環,兩個指針肯定會在環中相遇。

141. Linked List Cycle

  1. 用雙指針的思路,walker moves step by step. runner moves two steps at time. if the Linked List has a cycle walker and runner will meet at some
    point.
  2. 解法代碼下一題中其實是包含的,但我還是把這個代碼貼出來了,因為判定條件那里需要注意,這道題的寫法是,先判斷了head==null,之后while中判斷runner.next和runner.next.next,個人理解是runner跑的快,需要注意判斷runner而不是walker。下一題的寫法看起來跟這個不同,其實一樣
public boolean hasCycle(ListNode head) {
            if(head==null) return false;
            ListNode walker = head;
            ListNode runner = head;
            // runner跑的快,在前面,所以判斷runner.next, runner.next.next
            while(runner.next!=null&&runner.next.next!=null){
                walker = walker.next;
                runner = runner.next.next;
                if(walker==runner) return true;
            }
            return false;
        }

142. Linked List Cycle2

關于判定條件的一個問題上道題中解釋了
這個題目的思路不太好想,discuss中有一個很好的解釋,貼過來,其中關鍵的兩點是,walker走過的距離和cycle長度的關系,以及walker,runner相遇之后再通過head和walker一齊走,相遇點是cycle起點這層關系

Explanations

Definitions:
Cycle = length of the cycle, if exists.
C is the beginning of Cycle, S is the distance of slow pointer from C when slow pointer meets fast pointer.
Distance(slow) = C + S, Distance(fast) = 2 * Distance(slow) = 2 * (C + S). To let slow poiner meets fast pointer, only if fast pointer run 1 cycle more than slow pointer. Distance(fast) - Distance(slow) = Cycle
=> 2 * (C + S) - (C + S)    = Cycle
=>  C + S = Cycle
=>  C = Cycle - S
=> This means if slow pointer runs (Cycle - S) more, it will reaches C. So at this time, if there's another point2(we use head Here) running from head
=> After C distance, point2 will meet slow pointer at C, where is the beginning of the cycle.
public ListNode detectCycle(ListNode head) {
            ListNode walker = head;
            ListNode runner = head;
            //這里不加runner.next.next!=null也ac
            while(runner!=null&&runner.next!=null&&runner.next.next!=null){
                runner = runner.next.next;
                walker = walker.next;
                if(runner==walker){
                    while(head!=walker){
                        head = head.next;
                        walker = walker.next;
                    }
                    return walker;
                }
            }
            return null;
        }
  1. 判斷兩個單鏈表是否相交----時間復雜度為O(len1+len2),因為只需要一個額外指針保存最后一個節點地址,空間復雜度為O(1)。
    如果兩個鏈表相交于某一節點,那么在這個相交節點之后的所有節點都是兩個鏈表所共有的。也就是說,如果兩個鏈表相交,那么最后一個節點肯定是共有的。先遍歷第一個鏈表,記住最后一個節點,然后遍歷第二個鏈表,到最后一個節點時和第一個鏈表的最后一個節點做比較,如果相同,則相交,否則不相交。
public static boolean isIntersect(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return false;
        }

        Node tail1 = head1;
        // 找到鏈表1的最后一個節點
        while (tail1.next != null) {
            tail1 = tail1.next;
        }

        Node tail2 = head2;
        // 找到鏈表2的最后一個節點
        while (tail2.next != null) {
            tail2 = tail2.next;
        }

        return tail1 == tail2;
    }
  1. 求兩個單鏈表相交的第一個節點----時間復雜度,O(len1+len2)
    對第一個鏈表遍歷,計算長度len1,同時保存最后一個節點的地址。
    對第二個鏈表遍歷,計算長度len2,同時檢查最后一個節點是否和第一個鏈表的最后一個節點相同,若不相同,不相交,結束。
    兩個鏈表均從頭節點開始,假設len1大于len2,那么將第一個鏈表先遍歷len1-len2個節點,此時兩個鏈表當前節點到第一個相交節點的距離就相等了,然后一起向后遍歷,知道兩個節點的地址相同。
 * 
     *              ----    len2
     *                   |__________
     *                   |
     *       ---------   len1
     *       |---|<- len1-len2
     */
    public static Node getFirstCommonNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        int len1 = 1;
        Node tail1 = head1;
        while (tail1.next != null) {
            tail1 = tail1.next;
            len1++;
        }

        int len2 = 1;
        Node tail2 = head2;
        while (tail2.next != null) {
            tail2 = tail2.next;
            len2++;
        }

        // 不相交直接返回NULL
        if (tail1 != tail2) {
            return null;
        }

        Node n1 = head1;
        Node n2 = head2;

        // 略過較長鏈表多余的部分
        if (len1 > len2) {
            int k = len1 - len2;
            while (k != 0) {
                n1 = n1.next;
                k--;
            }
        } else {
            int k = len2 - len1;
            while (k != 0) {
                n2 = n2.next;
                k--;
            }
        }

        // 一起向后遍歷,直到找到交點
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }

        return n1;
    }

160. Intersection of Two Linked Lists

  1. 一個general的方法, 比較兩個linked list的長度,把較長的一個鏈表后移幾位,從長度和另一鏈表相等處開始比較node是否相同。
    一開始在想相交之后還會不會分開,比如一開始就相交,那長度不等情況下先向后移就說不過去了,但是這里應該是利用了鏈表特性,每個node都指向另一個node,所以相交之后就一定都一樣了。
  2. 一個很機智的方法,感覺用到了類似single linked list中判斷是否有cycle時候用的runner 和walker雙指針的方法,這個題中的“雙指針”總會在intersection處相遇或者沒有intersection在最后的null相遇.
    disscuss區大神的分析:
  3. use two iterations here. In the first iteration, we will reset the pointer of one linkedlist to the head of another linkedlist after it reaches the tail node. In the second iteration, we will move two pointers until they points to the same node. Our operations in first iteration will help us counteract the difference.
    So if two linkedlist intersects, the meeting point in second iteration must be the intersection point. If the two linked lists have no intersection at all, then the meeting pointer in second iteration must be the tail node of both lists, which is null
  4. The problem description especially required the code to run in O(n) time and O(1) space. Thus I came up with the most direct way.
      Just count the lengths of both lists, set two pointers from the list heads, align them to equipotential position and move'em forward until they coincide.
      That would be the answer we seek.
      Time complexity should be O(n + m), if you name the lengths of both lists to be "n" and "m". Extra space required is O(1).
  5. Notice:只貼一下第二個方法,第一個方法很簡單,分別遍歷鏈表直到空,通過counter獲取長度,然后通過兩個長度差值移動指向較長鏈表的node的位置,在等長之后比較node是否相同,是就返回該node。
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;
}
  1. 已知一個單鏈表中存在環,求進入環中的第一個節點
    上面貼的leetcode142
    首先判斷是否存在環,若不存在結束。在環中的一個節點處斷開(當然函數結束時不能破壞原鏈表),這樣就形成了兩個相交的單鏈表,求進入環中的第一個節點也就轉換成了求兩個單鏈表相交的第一個節點。參考代碼如下:
    /**
     * 求進入環中的第一個節點 用快慢指針做(本題用了Crack the Coding Interview的解法,因為更簡潔易懂!)
     */
    public static Node getFirstNodeInCycle(Node head) {
        Node slow = head;
        Node fast = head;

        // 1) 找到快慢指針相遇點
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) { // Collision
                break;
            }
        }

        // 錯誤檢查,這是沒有環的情況
        if (fast == null || fast.next == null) {
            return null;
        }

        // 2)現在,相遇點離環的開始處的距離等于鏈表頭到環開始處的距離,
        // 這樣,我們把慢指針放在鏈表頭,快指針保持在相遇點,然后
        // 同速度前進,再次相遇點就是環的開始處!
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }

        // 再次相遇點就是環的開始處
        return fast;
    }
  1. 給出一單鏈表頭指針pHead和一節點指針pToBeDeleted,O(1)時間復雜度刪除節點pToBeDeleted----總體的平均時間復雜度還是O(1)
    對于刪除節點,我們普通的思路就是讓該節點的前一個節點指向該節點的下一個節點,這種情況需要遍歷找到該節點的前一個節點,時間復雜度為O(n)。對于鏈表,鏈表中的每個節點結構都是一樣的,所以我們可以把該節點的下一個節點的數據復制到該節點,然后刪除下一個節點即可。要注意最后一個節點的情況,這個時候只能用常見的方法來操作,先找到前一個節點,但總體的平均時間復雜度還是O(1)。參考代碼如下:
public void delete(Node head, Node toDelete){  
        if(toDelete == null){  
            return;  
        }  
        if(toDelete.next != null){          // 要刪除的是一個中間節點  
            toDelete.val = toDelete.next.val;       // 將下一個節點的數據復制到本節點!  
            toDelete.next = toDelete.next.next;  
        }  
        else{       // 要刪除的是最后一個節點!  
            if(head == toDelete){       // 鏈表中只有一個節點的情況    
                head = null;  
            }else{  
                Node node = head;  
                while(node.next != toDelete){   // 找到倒數第二個節點  
                    node = node.next;  
                }  
                node.next = null;  
            }  
        }  
    }  

規律總結

  1. DummyNode
    做鏈表題目時,如果head可能被改變,我們需要創建一個虛擬節點,叫DummyNode,把頭部掛在它的后面。這樣就算頭部變化了之后,只要返回DummyNode.next就能輕松得到新頭部。
  2. Merge LinkedList是相當基礎的題目,merge的半成品代碼上面也有提到。
  3. Reverse linkedList最簡單的寫法就是創建DummyNode,然后把舊的鏈表不斷插入到DummyNode的后面,就能輕松地返回鏈表了。
  4. 操作鏈表的時候,我們經常會改變某些Node。如果后面還需要再用到被改變掉節點的原始值,請一定記得用tmp先把它保存起來。

ref:

  1. http://blog.csdn.net/luckyxiaoqiang/article/details/7393134
  2. http://blog.csdn.net/fightforyourdream/article/details/16353519
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • 背景 一年多以前我在知乎上答了有關LeetCode的問題, 分享了一些自己做題目的經驗。 張土汪:刷leetcod...
    土汪閱讀 12,762評論 0 33
  • 目錄 1. 棧和隊列1.用兩個隊列實現棧2.用兩個棧實現隊列3.實現一個棧,可以用常數級時間找出棧中的最小值4.判...
    MigrationUK閱讀 3,049評論 4 20
  • 2. Add Two Numbers 先初始化兩個結點,一個用來做head,一個作為指引node不斷向下延續的指針...
    Morphiaaa閱讀 927評論 0 0
  • 鏈表 概念 說到鏈表,coder們都不會陌生,在日常開發中或多或少都會用到它。它是鏈式存儲的線性表,簡稱鏈表。鏈表...
    扈扈哈嘿閱讀 2,084評論 0 5
  • 相信有很多初識簡書的朋友,會像我一樣,很在乎粉絲的增減,多一個關注會高興好一陣,而今天,有一個人關注了我,我們沒有...
    左臉美麗i閱讀 287評論 4 3