445. Add Two Numbers II 题解报告(链表)

    ListNode* reverseKGroup(ListNode* head, int k) {
       // if(!head||k==1)return head;
        int cnt=0;
        ListNode* p = head, *newh = new ListNode(0), *pre = newh;
        while(p){
              
            //找到第k+1个节点p
            while(p&&cnt!=k){
                cnt++;
                p = p->next;
            }
            if(cnt!=k){
                pre->next = head;
                break;
            }
            else{
                //反转之后的最后一个节点
                ListNode *last = head;
                while(cnt>0){
                    ListNode *nxt = head->next;
                    head->next = p;
                    p = head;
                    head = nxt;
                    cnt --;//尽量不要在while循环判断语句中加上--或者++
                }
                //上一次反转的最后节点衔接这一次反转的头结点
                pre->next = p;
                
                //初始化
                p = head;
                pre = last;
            }
        }
        return newh->next;
    }
非递归

 

【例子1】445. Add Two Numbers II 

You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Follow up:
What if you cannot modify the input lists? In other words, reversing the lists is not allowed.

Example:

Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7

思路:不能反转,不能够改变链表。由于计算是从链表的最后一位数字开始,所以,可以采用后进先出的栈。两个栈分别存放各个链表的数字,出栈的时候,计算结果大于0,则进1

代码:

 ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        stack<int> s1,s2;
        ListNode* p1 = l1,*p2=l2;
        while(p1!=NULL){
            s1.push(p1->val);
            p1=p1->next;
        }
        while(p2!=NULL){
            s2.push(p2->val);
            p2=p2->next;
        }
        ListNode* newh = new ListNode(1);
        int res=0;
        while(1){
            if(!s1.empty()){
                res += s1.top();
                s1.pop();
            }else{
                if(s2.empty())break;
            }
            if(!s2.empty()){
                res += s2.top();
                s2.pop();
            }
            
            ListNode* t =  new ListNode(res%10);
            t->next = newh->next;
            newh->next = t;
            res = res/10;
        }
        return res?newh:newh->next;
    }
View Code

【例子2】143. Reorder List

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

You must do this in-place without altering the nodes' values.

For example,
Given {1,2,3,4}, reorder it to {1,4,2,3}.

思路:链表变换顺序,主要有两种方式,一是反转,二是归并。这两种方式都只需要o1的空间。在这里,我们可以先将链表划分成两半,分别反转,然后归并这两部分的链表,这样就能够得到要求的链表序列了。

代码:

   void reorderList(ListNode* head) {
        if(!head||!head->next)return;
        ListNode *prev, *slow = head, *fast = head, *h1 = head;
        while(fast && fast->next) {
            prev = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        prev->next = NULL;//注意归并前要将链表切断
        ListNode *h2=reverse(slow);
        merge(h1,h2);    
    }
    
   
    
    ListNode* reverse(ListNode* head){
        if(!head)return NULL;
        ListNode *pre = new ListNode(0);
        while(head){
            ListNode *nxt = head->next;
            head->next = pre->next;
            pre->next = head;
            head = nxt;
        }
        return pre->next;
    }
    
    void merge(ListNode* h1,ListNode* h2){
        while(h1){
            ListNode *p = h1->next;
            ListNode *q = h2->next;
            h1->next = h2;
            if(!p)break;
            h2->next = p;
            h1 = p;
            h2 = q;
            
        }
    }
View Code

【例子3】 25. Reverse Nodes in k-Group

Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.

k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.

You may not alter the values in the nodes, only nodes itself may be changed.

Only constant memory is allowed.

For example,
Given this linked list: 1->2->3->4->5

For k = 2, you should return: 2->1->4->3->5

For k = 3, you should return: 3->2->1->4->5

思路:这是反转链表的一个变形,递归和非递归都能够解决。递归的思路就是,先从反转前面k个节点,再连接reverseKGroup(remain)。如果不够k个,则直接返回head。

非递归,反转每一小节,难点在于前后衔接。

代码:

递归
    ListNode* reverseKGroup(ListNode* head, int k) {
       // if(!head||k==1)return head;
        int cnt=0;
        ListNode* p = head, *newh = new ListNode(0), *pre = newh;
        while(p){
              
            //找到第k+1个节点p
            while(p&&cnt!=k){
                cnt++;
                p = p->next;
            }
            if(cnt!=k){
                pre->next = head;
                break;
            }
            else{
                //反转之后的最后一个节点
                ListNode *last = head;
                while(cnt>0){
                    ListNode *nxt = head->next;
                    head->next = p;
                    p = head;
                    head = nxt;
                    cnt --;//尽量不要在while循环判断语句中加上--或者++
                }
                //上一次反转的最后节点衔接这一次反转的头结点
                pre->next = p;
                
                //初始化
                p = head;
                pre = last;
            }
        }
        return newh->next;
    }
非递归

 

【例子4】单链表的交点

Write a program to find the node at which the intersection of two singly linked lists begins.

 

For example, the following two linked lists:

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

begin to intersect at node c1.

思路:最开始我的思路是知道长度差,然后先遍历两个链表,得到长度,然后根据长度差就能够找到交点了。这个思路的缺陷就是需要对两个链表多遍历一次,然而,还有一种只需遍历一次就能够找到相交节点的算法。p1和p2分别从A和B这两条链表的头结点开始遍历,这时候,得到相同则返回,否则必定有一个链表先遍历完,这时候再将这个指针移动到另一个链表的开头,恰好让两个指针间隔了长度差个节点。

代码:

    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p1 = headA,*p2 = headB;
        if(!p1||!p2)return NULL;
        while(p1&&p2&&p1!=p2){
            p1 = p1->next;
            p2 = p2->next;
            if(p1==p2)break;
            if(!p1)p1=headB;
            if(!p2)p2=headA;
        }
        return p1;
    }
View Code

【例子5】找到单链表中开始循环的节点

 

 上面分析了 根据这张图

思路:利用快慢指针,首先找到快慢指针都相同的节点,例如e点,两个节点达到这里时,它们之间的路程隔了n圈,这时候,k+x=mT,因为s1 = k + x + n1T,s2 = k + x + n2T, s2 = 2s1。进一步,得到k = y+(m-1)T。也就是说,两个节点同时从E出发,它们必定在D点相遇。

代码:

ListNode*  findBeginning(ListNode *head){
    ListNode *fast = head, *slow = head;
    while (fast&&fast->next&&fast!=slow){  
        fast = fast->next->next;  
        slow = slow->next;  
    }
    if(fast){
         slow = head;
        while(slow!=fast){
            slow = slow->next;
            fast = fast->next;
        }
    }
    return fast;
}
View Code

【例子6】138. Copy List with Random Pointer

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/

思路:比较简单的方法就是建立一个hashmap,将origin node 和copy node一一对应起来。另一种思路其实很简单,就是在链表各节点之间插入前一点的复制节点,例如

1->1`->2->2`。。然后就可以很方便地复制random指针了,orgin->next(copy)->random = orgin->random->next.

 

【例子7】

算法导论上面的问题,每个元素仅仅只用一个指针来实现双向链表。

Explain how to implement doubly linked lists using only one pointer value x: np per item instead of
the usual two (next and prev). Assume that all pointer values can be interpreted as k-bit
integers, and define x: np to be x: np x: next XOR x: prev, the k-bit “exclusive-or” of x:
next and x: prev. (The value NIL is represented by 0.) Be sure to describe what information you
need to access the head of the list. Show how to implement the SEARCH, INSERT, and DELETE
operations on such a list. Also show how to reverse such a list in O.1/ time.

思路:考虑到异或运算,x:next = XOR(x: prev, x: np),同样的,x: prev = XOR(x: np, x: next),如果是头结点或者尾节点,就需要就prev或者next变为0。所以,当我们知道list的head之后,就能够得它的所有节点。head.next = XOR(0,head)。如果要反转链表只用o1,我们需要给链表设置一个tail,tail = XOR(prev,0),这时候只需要将list->tail 和list->tail互换即可。

参考:

http://www.slader.com/textbook/9780262033848-introduction-to-algorithms-3rd-edition/241/exercises/8/

 

posted on 2017-08-09 13:16  1592653  阅读(193)  评论(0编辑  收藏  举报