(双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List

 

本题是从1开始计数;n一定是合法的。

能否只遍历一遍链表?

n = 2, 则要删除4和5。使用两个指针来寻找要删除的区间。

 

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        //虚拟头结点
        assert(n>=0);
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* p = dummy;
        ListNode* q = dummy;
        for(int i=0; i<n+1;i++){
            assert(q);
            //将q向后移n+1次
            q = q->next; 
        }
        //p和q之间相差n+1步
        while(q!=NULL){
            p = p->next;
            q = q->next;
        }
        //此时p指向待删除指针的前一个指针
        ListNode* delNode = p->next;
        p->next = delNode->next;
        delete delNode;
        
        ListNode* ret = dummy->next;
        delete dummy;
        
        return ret;
    }
};

思路:快慢指针,一开始让fast和slow指向链表的头结点,然后让fast指向链表旋转k位后的头结点,然后fast和slow同时向后移动,直到fast指向原链表的最后一个元素,此时,slow指向新链表最好一个元素,slow->next指向新链表的head。

 注意:需要提前遍历一遍链表,求出其长度n,然后k = k%n

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
//1->2->3->4->5->NULL
//k=2
//初始使slow和fast间隔为k
//fast最终指向原链表最后一个元素(即5),slow指向新链表最后一个元素(即3),slow->next指向新链表的head
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head==NULL)
            return NULL;
        ListNode* fast = head;
        int n = 0;  //链表长度
        while(fast){
            n++;
            fast = fast->next;
        }
        fast = head;
        ListNode* slow = head;
        k = k%n;
        for(int i=0; i<k; ++i)
            fast = fast->next;
        while(fast->next){
            slow = slow->next;
            fast = fast->next;
        }
        fast->next = head;
        head = slow->next;
        slow->next = NULL;
        return head;
    }
};

 

 注意:1)如果 k>n 那么在使p, q移动时容易报错产生空指针。所以需要先遍历一遍链表求出其总长度,然后再 k = k % n。

 2)在遍历链表时注意n初始值的设立。

3)使用双指针来取得需要旋转的区间。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head==NULL) return NULL;
        
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* p = dummy;
        ListNode* q = dummy;
        
        //遍历一遍链表得到总长度
        ListNode* r = head;
        int n = 0;
        while(r != NULL){
            n += 1;
            r = r->next;
        }
        //取余数,防止k比n大的情况
        k = k % n;
        
        if(k == 0) return head;
        
        for(int i=0;i<k;i++){
            assert(q);
            q = q->next;
        }
        while(q->next != NULL){
            p = p->next;
            q = q->next;
        }
        
        //此时p指向需要旋转节点的前一个,q指向最后一个旋转节点
        ListNode* ans = p;
        p = p->next;    //p指向第一个旋转节点
        ans->next = NULL;
        dummy->next = p;
        q->next = head;
        
        ListNode* ret = dummy->next;
        delete dummy;
        return ret;
    }
};

 

 

思路:获得链表中间的元素后将链表分为左右两个链表,把右链表翻转之后,将两个链表分别穿插。

使用中间指针。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        if(!head || !head->next|| !head->next->next)
            return;
        //快慢指针 找到链表中点
        ListNode* fast = head, *slow = head;
        while(fast->next && fast->next->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        
        //翻转链表后半段
        ListNode* mid = slow->next;  //链表中点
        slow->next = NULL;
        ListNode* pre = NULL;
        while(mid){
            ListNode* next = mid->next;
            mid->next = pre;
            pre = mid;
            mid = next;
        }
        
        //合并
        while(head && pre){
            ListNode* cur = head->next, *pre_next = pre->next;
            head->next = pre;
            pre->next = cur;
            pre = pre_next;
            head = cur;
        }
        
    }
};

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        if(!head || !head->next || !head->next->next)
            return;
        
        //快慢指针找到链表中点
        ListNode* p_fast = head;
        ListNode* p_slow = head;
        
        while(p_fast->next && p_fast->next->next){
            p_slow = p_slow->next;
            p_fast = p_fast->next->next;
        }  
        
        //将后一半链表翻转
        ListNode* mid = p_slow->next;
        p_slow->next = NULL;
        ListNode* last = mid, *pre = NULL;
        while(last){
            ListNode* next = last->next;
            last->next = pre;
            pre = last;
            last = next;
        }
         
        //将两个链表合并
        while(pre && head){
            ListNode* next = head->next;
            head->next = pre;
            pre = pre->next;
            head->next->next = next;
            head = next;    
        }
    }
};

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        
        
        ListNode* dummy = new ListNode(-1);
        dummy -> next = head; 
        
        ListNode* fast = dummy;
        ListNode* slow = dummy;
        
        while(fast!=NULL && fast->next!=NULL){
            slow = slow->next;      //slow最终指向链表的中间节点
            fast = fast->next->next;    
           
        }
        
        //slow这时指向链表的中间节点
        ListNode* ans = slow->next;
        slow->next = NULL;
        
        //翻转第二个链表
        ListNode* last = ans, *pre = NULL;
        while(last)
        {
            ListNode* next = last->next;
            last->next = pre;
            //将后一个节点赋值给前一个节点
            pre = last;
            last = next;
        }
        
        while(pre!=NULL && head!=NULL){
            ListNode* next = head->next;
            head->next = pre;
            pre = pre->next;
            head->next->next = next;
            head = next;      
        } 
    }
    
};
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        if(!head || !head->next ||!head->next->next)
            return;
        ListNode* fast= head, *slow = head;
        while(fast->next && fast->next->next){
            slow = slow->next;
            fast = fast->next->next;
        }
        
        ListNode* mid = slow->next, *pre = NULL, *next = NULL;
        slow->next = NULL;  //令前半段链表最后一个元素的后继为空
        
        while(mid){
            next = mid->next;
            mid->next = pre;
            pre = mid;
            mid = next;
        }
        
        ListNode *h_n = NULL, *pre_n = NULL;
        while(head && pre){
            h_n = head->next;
            pre_n = pre->next;
            head->next = pre;
            pre->next = h_n;
            head = h_n;
            pre = pre_n;
        }
        
    }
};

注意: slow->next = NULL;  //令前半段链表最后一个元素的后继为空

这句一定要记得写,不然会 time limit !!!!

 

 

回文:正着看和反着看是一样的。

若是数组实现:则用两个指针,一个从前向后移,一个从后向前移,来分别判断是否对应的元素相同。

思路1:使用快慢指针找中点的原理,在每次慢指针走一步时都把值存入栈中,然后等到达中点了,链表的前半段都存入栈中了,由于栈先进后出的性质,可以和后半段链表按照回文对应的顺序比较了。

思路2:若是要采用O(1)的时间复杂度,那就不能使用stack了。可以在找到中点后,将后半段的链表翻转一下,这样就可以按照回文的顺序比较了。

注意边界条件的判断!

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head==NULL || head->next == NULL) return true;
        
        //分为两个链表
        ListNode* slow = head, *fast = head;
        while(fast->next && fast->next->next){
            slow = slow->next;
            fast = fast->next->next;
        }
        
        //翻转后一个链表
        ListNode* last = slow->next, *pre = NULL;
        while(last){
            ListNode* next = last->next;
            last->next = pre;
            pre = last;
            last = next;
        }  //最终pre指向第二个链表的头结点
        
        while(pre){
            if(pre->val != head->val) return false;
            pre=pre->next;
            head = head->next;
        }
        return true;
    }
};

 

posted @ 2018-12-26 19:13  爱学英语的程序媛  阅读(222)  评论(0编辑  收藏  举报