Linked List

String一章节墨迹了六七天。后面AC15 以下的题目不做了,没有什么启发性,都是些就题论题,又很难想,实现繁琐的题目。目前时间紧张,要提升到更重要的方面还有很多,所以果断跳过了。 下周重做String(AC 28 -19) 时候再整理. 现在的计划是八月底过完第二遍(190题左右算一遍 AC 15以下暂避)

Linked List一直是我软肋。 我心不够细致。希望这遍能有质的提升。


Merge Two Sorted Lists : *

这题是第一道有点启发的list 题目。

1.做法肯定是constant space。 就new 了一个 node, 其余的只是把原有的node 接来接去。

2. 要想插入到node之前,必须保存node的前一个node。 其实替换/删除 node也是需要保存前一个的,在不允许改变node.val的情况下。

public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prev = new ListNode(0);
        prev.next = l1;
        ListNode head = prev;
        while(l1 != null && l2 != null){
            if(l1.val > l2.val){
                ListNode temp = l2.next;
                l2.next = l1;
                prev.next = l2;
                l2 = temp;
            }else{
                l1 = l1.next;
            }
            prev = prev.next;
        }
        if(l2 != null)//This is necessary. Notice prev will only match the tail when l2 still has nodes left.
            prev.next = l2;
        return head.next;
    }
}

Swap Nodes in Pairs  : *

经典的小题,还记得第一次做做了很久,第二遍就顺利多了。

1。guard node的使用不说多说了

2。 prev  cur nxt  三巨头。

public class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode guard = new ListNode(0);
        guard.next = head;
        ListNode prev = guard;
        ListNode cur = head;
        while(cur != null && cur.next != null){
            ListNode nxt = cur.next;
            prev.next = nxt;
            ListNode temp = nxt.next;
            nxt.next = cur;
            cur.next = temp;
            prev = cur;
            cur = cur.next;
        }
        return guard.next;
    }
}

Convert Sorted List to Binary Search Tree : ****

太喜欢这道题了。 这才是我所欣赏的算法艺术。简单梳理几点,这道题要火候到了自然理解,不到的话写才明白也难知其精髓

1. 老生常谈, BST重要性质就是 in-order traversal 保留有序性。

2. recursion 的pattern就是触底反弹(回). 这也是追踪观察recursion过程的一个要领。

3. 这个题要想order(n)内解决,就必须用in-order traversal, 这样linked list只需要扫一遍从头到尾。按照in-order的顺序来构建tree!自然先从left开始。

4. 第四点,返回条件是 if(l > r){ return null} (其实这个和sorted array 返回 BST是一样的,理清思路一步一步来可以想到的) 。 

 

这个题可以启发到 可以根据 in-order post-order pre-order的list 来构建binary tree么?其实如果这个题换个说,说给你一个tree的 in-order traversal的list,让你重建tree,是一样的。


public class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        int count = 0;
        ListNode node = head;
        while(head != null){
            count ++;
            head = head.next;
        }
        List
   
   
    
     list = new ArrayList
    
    
     
     ();
        list.add(node);
        return helper(list,1,count);
    }
    
    public TreeNode helper(List
     
     
      
       list, int l, int r){
        if(l > r){
            return null;
        }
        int m = (l+r)/2;
        TreeNode left = helper(list,l,m-1);
        TreeNode root = new TreeNode(list.get(0).val);
        root.left = left;
        list.set(0,list.get(0).next);
        TreeNode right = helper(list,m+1,r);
        root.right = right;
        return root;
    }
}
     
     
    
    
   
   

Partition List : **

//关于linked list有一个很容易陷入的误区。像下面第一种做法,其实除去指针外是不消耗空间的,就是把node接来接去。猛的一想还以为
//需要额外空间的,是一种来自array的误导。所以第一周方法显然是最优最易于实现的。
class Solution {  
public:  
    ListNode *partition(ListNode *head, int x) {   
        ListNode *head1,*head2,*p1,*p2;  
        head1=new ListNode(0);  
        head2=new ListNode(0);  
        p1=head1;  
        p2=head2;  
        while(head!=NULL)  
        {  
            if(head->val
   
   
    
    next=new ListNode(head->val);  
                p1=p1->next;                  
            }  
            else   
            {  
                p2->next=new ListNode(head->val);  
                p2=p2->next;  
            }  
            head=head->next;  
        }  
        if(head2->next!=NULL)p1->next=head2->next;  
        ListNode *temp=head1->next;  
        delete(head1);  
        delete(head2);  
        return temp;  
    }  
};//http://blog.csdn.net/havenoidea  


//这是一种直接的想法,但是实现起来不简单。我做来挺久,最后发现是少了 “cur != end” 的判断。

public class Solution {
    public ListNode partition(ListNode head, int x) {
            if(head == null || head.next == null)
                return head;
            ListNode end = head;
            int count = 1;
            while(end.next != null){
                count ++;
                end = end.next;
            }
            
            ListNode res = new ListNode(0);
            res.next = head;
            ListNode prev = res;
                        //ListNode prev = new ListNode(0);
                        //prev.next = head;
                        //ListNode res = prev;
            //上面俩个写法是等价的,关于java 的传参问题不应该再有疑问。
            ListNode cur = head;
            while(count > 0 && cur != null){
                if(cur.val < x){
                    prev = cur;
                    cur = cur.next;
                }else {
                    if(cur != end){
                        ListNode nxt = cur.next;
                        prev.next = nxt;
                        end.next = cur;
                        cur.next = null;
                        cur = nxt;
                        end = end.next;
                    }
                }
                count -- ;
            }
            return res.next;
        }
}
   
   

Remove Nth Node From End of List : **

挺喜欢这个题目。 

一点。 list不扫一遍不知道长度,那么在一遍中如何能知道目前所在node的绝对位置呢? 

用俩个node的相对位置决定。

例如经典的找中点,一快一慢,快的一次走俩,慢的一次走一个,快的到终点时候,慢的所在位置就是终点。

这个题非常雷同,快的先走到第n个node,然后一起走,快的到终点了,慢的在的就是从后往前数到第n个。而我们要的是这个的前一个,所以让快的到倒数第二个,慢的正好停在倒数第n个的前一个,就可以慢倒数第n个去掉了。

public class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode f = head;
        ListNode s = head;
        while(n-1>0){
            f = f.next;
            n--;
        }
        while(f.next!= null && f.next.next != null){
            s = s.next;
            f = f.next;
        }
        if(f.next == null){
            head = head.next;
            return head;
        }else{
            s.next = s.next.next;
        }
        return head;
    }
}


Insertion Sort List : ***

这个题真是。。。做了很久。思路很简单,但是有一个致命点细节问题,表面是细节,其实是关乎实现的大问题。用另一种似乎近似的实现,做了很久也做不对,放弃了。

具体看代码描述

public class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode guard = new ListNode(0);
        //注意,guard没有向通常那样直接与head相连。
        //这里的意图是实现决定的,在insertion sort里,我们去维护前面元素的一个独立的sorted list。
        //我们要这个sorted list的尾元素的next 保持是null
        //**********
        //当处理第一个node时候,他前面没有node,所有他前面是没有sorted list的。我们把他加入sorted list后,我们要
        //保持sorted list的边界,这个list 此时只有这一个node,要node与他本来的next 切断才行。
        //**********
        //我试图不用这种实现,把guard直接连上了head 并且试图检查backtrack是否为cur 来决定sorted list是否扫完一遍,无果,这其中的联系很复杂,
        //会有node自己连上自己进入死循环,在leetcode上会持续报 memory limit exceeded。最后也没有成功做出来这种实现。
        ListNode cur = head;
        ListNode backtrack = guard;
        while(cur != null){
            backtrack = guard;
            ListNode  nxt = cur.next;
            while(backtrack.next != null && backtrack.next.val < cur.val ){
                backtrack = backtrack.next;
            }
            cur.next = backtrack.next;
            backtrack.next = cur;
            cur = nxt;
        }
        return guard.next;
    }
}

Reverse Linked List II : **

这种题没啥好说的,关键是细节上当场能不能保持清醒,一遍做对。也不知道面到这种题目是好事还是坏事。总之这个reverse点操作还是要做熟。

//code ganker的思路,插入法
    public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode guard = new ListNode(0);
        guard.next = head;
        ListNode prev = guard;
        int i = 1;
        while(i < m){
            prev = prev.next;
            i++;
        }
        if(prev.next == null)
            return head;
        ListNode close = prev.next;
        ListNode cur = prev.next.next;
        while(i < n){
            ListNode temp = cur.next;
            cur.next = prev.next;
            prev.next = cur;
            close.next = temp;
            cur = temp;
            i++;
        }
        return guard.next;
    }
  
  //自己的实现,调转法
    
    public class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        int diff = n-m;
        ListNode guard = new ListNode(0);
        guard.next = head;
        ListNode node = guard;
        if(m == n)
            return head;
        while(m>1){
            node = node.next;
            m--;
        }
        ListNode prev = node;
        ListNode cur = node.next;
        ListNode nxt = node.next.next;
        while(diff > 0){
            ListNode temp = nxt.next;
            nxt.next = cur;
            cur = nxt;
            nxt = temp;
            diff--;
        }
        prev.next.next = nxt;
        prev.next = cur;
        return guard.next;
    }
}



Reverse Nodes in k-Group : ****

倒叙,删除,添加是linked list必须熟练但节本操作,核心都是 prev  cur  nxt 这三个node。 技巧是要求head前面加一个guard node

这个题昨晚做了很久,总算弄出来。其实思路是有的,方法也明确,问题都出在边界问题的上。其实边界问题还是好处理的,把边界情况单独想一下,不要觉得麻烦,

其实很明白的。而且代码本身往往是揭示了需要检查哪些exception的。说白了在外层while循环内部,每次向前移动了node(尤其是在while loop 内),都要考虑检查null exception。

链接是之前单独整理的这个题目。


Sort List : **

public class Solution {
    public ListNode sortList(ListNode head) {
        return mergeSort(head);
    }
    
    public ListNode mergeSort(ListNode head){
        if(head == null || head.next == null){
            return head;
        }
        ListNode s = head;
        ListNode f = head;
        while(f.next != null && f.next.next != null){
            f = f.next.next;
            s = s.next;
        }
        ListNode head1 = head;
        ListNode head2 = s.next;
        s.next = null;//Cut off second half!
        //This is also required in the question : Insertion Sort List. 
        head1 = mergeSort(head1);
        head2 = mergeSort(head2);
        return merge(head1,head2);
    }   
    public ListNode merge(ListNode head1, ListNode head2){
        ListNode guard = new ListNode(0);
        ListNode node = guard;
        while(head1 != null && head2 != null){
            if(head1.val > head2.val){
                node.next = head2;
                head2 = head2.next;
            }else{
                node.next = head1;
                head1 = head1.next;
            }
            node = node.next;
        }
        while(head1 != null){
            node.next = head1;
            head1 = head1.next;
            node = node.next;
        }
        while(head2 != null){
            node.next = head2;
            head2 = head2.next;
            node = node.next;
        }
        return guard.next;
    }
}

Palindrome Linked List  :**

还是变着法考reverse

public class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null)
            return true;
        ListNode f = head;
        ListNode s = head;
        ListNode node = head;
        int count = 0;
        while(node != null){
            node = node.next;
            count ++;
        }
        while(f.next != null && f.next.next != null){
            f = f.next.next;
            s = s.next;
        }
        
        ListNode head2 = s.next;
        s.next = null;
        ListNode prev = new ListNode(0);
        prev.next = head;
        ListNode cur  = head;
        ListNode nxt = cur.next;
        while(nxt != null){
            ListNode temp = nxt.next;
            nxt.next = cur;
            cur = nxt;
            nxt = temp;
        }
        prev.next.next = null;
        prev.next = cur;
        ListNode head1 = prev.next;
        if(count%2 == 1){
            head1 = head1.next;
        }
        while(head1 != null && head2 != null){
            if(head1.val != head2.val){
                return false;
            }
            head1 = head1.next;
            head2 = head2.next;
        }
        return true;
    }
}

Rotate List   : **

public class Solution {
    public ListNode rotateRight(ListNode head, int k) {
    //always check this
     if(head == null || head.next == null)
        return head;
     ListNode prev = new ListNode(0);
     prev.next = head;
     ListNode node = head;
     int count = 0;
     while(node != null){
         node = node.next;
         count ++ ;
     }
     //Trap here
     k = k%count;
     if(k == 0)
        return head;
    //***    
     node = prev;
     for(int i=1; i<=count-k; i++){
         node = node.next;
     }
     //same as:
     //node = head;
     //for(int i=2; i<=count-k; i++){
     //    node = node.next;
     //}
    //***
     ListNode temp = node.next;
     ListNode node2 = temp;
     node.next = null;
     while(node2.next != null){
         node2 = node2.next;
     }
     node2.next = prev.next;
     prev.next = temp;
     return prev.next;
    }
}

Merge k Sorted Lists : ****

这个题给四星。我一直觉得recursion是我的算法里第一个掌握精髓的技巧。但是由于我60%的recursion的练习都是关于Tree的,导致我的思维上习惯性的在非遍历多Tree的问题上

recursion的神经不敏感。这是需要突破的思维限制。这个题的recursion用的很简单,但是我总觉很巧妙,很难想。这也正是我recursion这一关最后的一个突破点,自由联想。

这个其实无需解释多了,一看就明白。和每俩个,再把每俩个的和的结果继续和。典型的recursion 模式。 时间复杂度时 T(n) = T(n/2) + k*n


另外附priority queue的做法,但是个人觉得这更多依赖数据结构内部的算法了。但是还是很经典的思路。

public class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0)
            return null;
        return helper(lists,0,lists.length-1);
        
    }
    
    public ListNode helper(ListNode[] lists, int l, int r){
        if(l >= r){
            return lists[l];
        }
        int m = (l+r)/2;
        
        return merge( helper(lists, l, m), helper(lists,m+1,r));
        
    }
    
    public ListNode merge(ListNode head1, ListNode head2){
        ListNode guard = new ListNode(0);
        ListNode node = guard;
        while(head1 != null && head2 != null){
            if(head1.val < head2.val){
                node.next =  head1;
                head1 = head1.next;
            }else{
                node.next  = head2;
                head2 = head2.next;
            }
            node = node.next;
        }
        if(head1 == null){
            node.next = head2;
        }else{
            node.next = head1;
        }
        return guard.next;
    }
}
//另附priority queue 做法。
public class Solution {
    public ListNode mergeKLists(List
   
   
    
     lists) {
        if (lists.size() == 0)
            return null;
        PriorityQueue
    
    
     
      queue = new PriorityQueue
     
     
      
      (lists.size(), new Comparator
      
      
       
       () {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });
        for (int i = 0; i < lists.size(); i++) {
            if (lists.get(i) != null)
                queue.add(lists.get(i));
        }
 
        ListNode head = new ListNode(0);
        ListNode p = head;
        while (!queue.isEmpty()) {
            ListNode node = queue.poll();
            p.next = node;
            if (node.next != null)
                queue.add(node.next);
            p = p.next;
        }
        return head.next;
    }
}
      
      
     
     
    
    
   
   

Reorder List  : ***
做来做去,都离不开reverse。 偶然混点merge活着 insertion

public class Solution {
    public void reorderList(ListNode head) {
        //whenever you would reverse a linked list, check this at very beginning
        if(head == null || head.next == null)
            return;
        ListNode f = head;
        ListNode s = head;
        while(f.next != null && f.next.next != null){
            f = f.next.next;
            s = s.next;
        }
        ListNode cur = s.next;
        s.next = null;
        
        ListNode prev = new ListNode(0);
        prev.next = cur;
        ListNode nxt = cur.next;
        ListNode temp;
        while(nxt != null){
            temp = nxt.next;
            nxt.next = cur;
            cur = nxt;
            nxt = temp;
        }
        prev.next.next = nxt;
        prev.next = cur;
        ListNode head2 = prev.next;
        while(head != null && head2 != null){
            temp = head.next;
            head.next = head2;
            head = temp;
            temp = head2.next;
            head2.next = head;
            head2 = temp;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值