LinkedList专题2

203 Remove Linked List Elements

思路:考虑1 : 可能有多个节点符合;考虑2:命中节点是head;考虑3:命中节点是尾节点;考虑4:命中节点是中间的普通节点。
学习1:在linkedList 运用递归的思路还是很常见的,我还没有形成习惯。不断缩小问题规模。

    public ListNode removeElementsV2(ListNode head, int val) {
        if(head==null) return null;
        head.next = removeElementsV2(head.next,val);
        if(head.val == val){
            head = head.next;
        }
        return head;
    }

学习2:因为第一版有head节点的特殊处理,可以采取的措施是:加一个假的head或者是dummy节点。代码漂亮多了。

 public ListNode removeElementsV3(ListNode head, int val) {
        ListNode dummy  = new ListNode(-1);
        dummy .next = head;
        ListNode node = head;
        ListNode preNode = dummy ;
        while(node!=null){
            if(node.val==val){
                preNode.next = node.next;
            }else{
                preNode = node;
            }
            node = node.next;
        }
        return dummy .next;
    }

代码

160 Intersection of Two Linked Lists

思路:两个链表相交的起点。方法一:暴力搜索。listA的每个节点与listB的每个节点比较。时间复杂度O(m*n)。方法二:将listA的每个节点放入map中,遍历listB的每个节点与map比较。时间复杂度O(n),空间复杂度O(n)。这两个都不符合要求。
学习:难点是能够观察到:如果listA和listB有交集,那么从交集点开始之后所有的node是相等的,那么长度也是相同的。所以如果有长度差异,一定是在列表的前端。正如demo中所示。可以分别计算两个列表的长度lenA,lenB。长的列表先移动|lenA-lenB|步;之后两个指针一起移动直到所指节点相同。
代码

19 Remove Nth Node From End of List

思路:从尾部开始计算,删除第n个节点。
如果是从头部开始计算,删除第n个节点就比较好处理了。那就把问题转换一下。从尾部开始的第n个节点=从头部开始的第几个节点。
经过画图举例知道 应该是从头部开始第(len-n+1)节点,len是list长度。
假设要删除的节点nodeA ,应该是nodeA的上一个节点.next = nodeA.next。需要处理nodeA的上一个节点不存在的情况,这里加入一个dummy节点。
思路2:使用两个指针first、second。first先走n+1个节点(因为有dummy节点),然后second再走。当first到达队尾的时候,second距离队尾还有n个节点。
学习网页
代码

23 Merge k Sorted Lists

合并k个已经排序好的列表。这个题目有多种解决思路。参考文章
思路1:把所有数据放在一个List,然后排序list。最后再创建ListNode。时间复杂度 O ( N l o g N ) ,N是所有节点的总数。所有数据放入List需要O(n);排序需要 O ( N l o g N ) ,创建节点需要O(N)。空间复杂度O(N)。
思路2:每次比较k个头节点,选择最小的节点。然后继续比较头节点。时间复杂度:O(kN)。k是数组长度,N是节点总数。每次比较需要比较k-1次,有N次比较。
思路3:对思路2的改进。每次把头结点的值放入优先队列中,取得最小值。时间复杂度: ( O ( N l o g k ) ) 。插入和弹出优先队列需要耗时 O ( l o g k ) ,获得最小值耗时O(1)。
思路4:把k个队列合并问题转为2个队列合并问题。可以第0个和第1个合并得到result;result再继续和第2个、第3个队列合并。时间复杂度: O ( k N ) 。N是两个list的长度。k个队列需要合并k-1次。
思路5:对思路4的改进。使用分治法(Divide and conquer)。合并0,1队列得到0队列;合并2,3队列得到2队列….;第二层 合并0,2队列得到0队列,合并4,6队列得到4队列;第三层合并0,4队列得到0队列…..一直到结束。时间复杂度: O ( N l o g k ) 。N是两个队列的节点总数。因为会合并logk次。
代码

25 Reverse Nodes in k-Group

思路:将整个列表分成几个长度为k的子链表;对每一个子链表反转。长度不为k的子链表保持不动。
思路1:找到每一个子链表开始、结束下标,然后调用 92 Reverse Linked List II
这样会有一些重复的跳过m个节点的操作。可以进一步优化。从head开始数,数够了k步就从head开始反转长度为k的子链表;接着再从当前节点开始数。够k个就反转。不够就退出。
学习:自己反转的代码写的有点凌乱。再次学习了递归思想。
这里写图片描述
如果要反转链表1->2->3->4->5中1到3的部分得到:3->2->1->4->5。左侧从左向右是一种思路,右侧从右向左也是一种方法。

    public ListNode reverseKGroupV2(ListNode head, int k) {
        ListNode currentNode = head;
        int count =0;
        while(currentNode!=null && count!=k){
            count++;
            currentNode = currentNode.next;
        }
        if(count == k ){
            currentNode = reverseKGroupV2(currentNode,k);
            while(count>0){
                ListNode tmp = head.next;
                head.next = currentNode;
                currentNode = head;
                head = tmp;
                count--;
            }
            head = currentNode;
        }
        return head;
    }

代码

61 Rotate List

思路:因为k可能大于len(链表长度)。所以1 计算列表长度;2 k=k % len;3 将指针移动到len-k的位置,这个位置是开始rotate节点的上一个节点。4 反转。
代码

82 Remove Duplicates from Sorted List II

思路:留下链表中没有重复出现的元素。比较简单。直观地解决就可以。
代码

86 Partition List

思路:用两个指针smallHead,biggerHead分别指向小于x的节点列表和大于等于x的节点列表。最后将两个列表合并返回。
代码

143 Reorder List

思路:1 找到一半节点;2 反转第二部分链表;3合并两个链表。重要的是代码怎么写。
代码

147 Insertion Sort List

思路:处理位置i,从0到i-1是已经排序好的。我写代码的时候还是受数组排序的思维影响比较重。在学习代码中,别人把排序好的作为一个list处理。相对来说要比较好。
代码

猜你喜欢

转载自blog.csdn.net/flying_all/article/details/79946714