leetcode题目总结<4>

还是链表的题目。。。

143. Reorder List

//My code 
//功能:将一个链表的倒数第一个结点插入到第一个结点后面,倒数第一个结点插入到第二个结点后面,以此类推。。。
/**
 * 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)
            return;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        ListNode* prev = slow->next;
        ListNode* cur = prev->next;
        prev->next = NULL;
        ListNode* next;
        while(cur)
        {
            slow->next = cur;
            next = cur->next;
            cur->next = prev;
            prev = cur;
            cur = next;
        }
        next = slow->next;
        slow->next = NULL;
        
        ListNode* p1 = head;
        ListNode* p2;
        ListNode* p3;
        while(p1 && next)
        {
            p2 = p1->next;
            p3 = next->next;
            p1->next = next;
            next->next = p2;
            p1 = p2;
            next = p3;
        }
    }
};
//others code
void reorderList(ListNode *head) {
    if (!head || !head->next) return;
    
    // find the middle node: O(n)
    ListNode *p1 = head, *p2 = head->next;
    while (p2 && p2->next) {
        p1 = p1->next;
        p2 = p2->next->next;
    }
    
    // cut from the middle and reverse the second half: O(n)
    ListNode *head2 = p1->next;
    p1->next = NULL;
    
    p2 = head2->next;
    head2->next = NULL;
    while (p2) {
        p1 = p2->next;
        p2->next = head2;
        head2 = p2;
        p2 = p1;
    }
    
    // merge two lists: O(n)
    for (p1 = head, p2 = head2; p1; ) {
        auto t = p1->next;
        p1 = p1->next = p2;
        p2 = t;
    }
}
总结:这道题也是先看了下思路才写出来的,算法思路是:先利用快慢指针找出链表的中点位置,然后再对中点后面的那部分链进行逆序排练,最后是对链表前部分链和经过排序的后部分链进行插入合并。这里有几点问题要指出来:

       1.对于这道题,当有两个节点是可以不用处理。一开始我没有考虑两个节点的问题,在进行快慢指针遍历的时               候,fast=head;而不是fast=head->next;这就导致了while循环跑过了一次,当有两个节点时,while循环slow指           针跑到第二个结点,这样就导致后面的prev、cur指针本身就是空的的情况,所以建议用快慢指针时将                         fast=head->next;

       2.对于后面的合并,别人给出的答案里就显得很简洁。


142. Linked List Cycle II

//My code 
//功能:判断是个链表是否有循环,如果有循环,返回循环的起始节点,如果没有,返回空
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head)
            return NULL;
        if(head->next == head)
            return head;
        else if(head->next == NULL)
            return NULL;
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            
            if(slow == fast)
            {
                ListNode* p = head;
                while(p && slow)
                {
                    if(p == slow)
                    {
                        return p;
                    }
                    p = p->next;
                    slow = slow->next;
                }
            }
        }
        return NULL;
    }
};
//others code

ListNode *detectCycle(ListNode *head) {
    if (head == NULL || head->next == NULL) return NULL;
    
    ListNode* firstp = head;
    ListNode* secondp = head;
    bool isCycle = false;
    
    while(firstp != NULL && secondp != NULL) {
        firstp = firstp->next;
        if (secondp->next == NULL) return NULL;
        secondp = secondp->next->next;
        if (firstp == secondp) { isCycle = true; break; }
    }
    
    if(!isCycle) return NULL;
    firstp = head;
    while( firstp != secondp) {
        firstp = firstp->next;
        secondp = secondp->next;
    }

    return firstp;
}
总结:判断链表是否有循环是个很常见的算法题,以往我们看到的链表循环都是表尾的指针指向表头,但是还有一种就是表尾的指针也可能指向链表中的某个节点,从而形成了局部循环,但是这样就破坏了唯一前驱结点的链表特性了。本题就是要判断是否存在链表循环,如果存在,找出循环的其实结点。算法思路是这样的:

      1.首先还是设置快慢指针,快指针每次走两格,慢指针每次走一格,根据快慢指针是否会再次相等来判断链表是            否存在循环。

      2.若链表存在循环,上面的链表的相等节点作为一个起始点,链表的头结点作为另外一个起始点,两个起始点同             时遍历,每次一格,这两个节点相遇的结点就是循环的起始结点。

算法证明:

                                


        假设快慢指针在上图的pos点位置第一次相遇,第一次相遇时,慢指针走了k,因此快指针走了2k补,另设圆环周 为r步,因此有2k-k = n*r;得到k = n*r;从上图可得知,LenA = k - x = n*r - x = (n-1)*r + (r - x) = (n - 1)*r + y;这里去n=1,那么可得出LenA = y.因此上面算法中从第一次相遇点和链表头结点同时遍历,他们相遇的结点即为环的其实结点得到证明。


92. Reverse Linked List II

//功能:给定m,n两个数,其中1<=m<=n<=length(链表的长度),要求使链表中m,n之间的那一段反转。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
   ListNode* reverseBetween(ListNode* head, int m, int n) {
	if(m == n || !head || !head->next)
		return head;
	ListNode* dummy = new ListNode(0);
	dummy->next = head;
	ListNode* prev;
	ListNode* cur;
	ListNode* post;
	ListNode* tail = dummy;

	for(int i = 0; i < m-1; i++)
		tail = tail->next;
	prev = tail->next;
	cur = prev->next;

	for(int i = 0; i < n-m ;i++)
	{
		post = cur->next;
		cur->next = prev;
		prev = cur;
		cur = post;
	}
	tail->next->next = cur;
	tail->next = prev;
	return dummy->next;
}
};
总结:这题应该不算太难,却花了很长的时间得不到正确的答案,上面的代码算是比较精简了。也很容易理解。只不过很多细节的地方值得注意。


86. Partition List

//功能:给定一个数x,要求将链表中小于x的结点按原来的顺序排列在链表的前面,大于等于x的部分排列在x的后面
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
       ListNode node1(0);
       ListNode node2(0);
       ListNode* p1 = &node1;
       ListNode* p2 = &node2;
       while(head)
       {
           if(head->val < x)
           {
               p1->next = head;
               p1 = p1->next;
           }
           else
           {
               p2->next = head;
               p2 = p2->next;
           }
           head = head->next;
       }
       p2->next = NULL;
       p1->next = node2.next;
       
       return node1.next;
    }
};
总结:这题思路想到了,却没写出来,主要是没想要新建两个头结点吧,以前都是用头插法创建链表,突然来个后插反而不知所措了。

82. Remove Duplicates from Sorted List II

//My wrong answer
//功能:删除掉链表中重复的元素(只要重复了全部删除)
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head || !head->next)
            return head;
        ListNode* dummy = new ListNode(0);
        ListNode* prev = dummy;
        while(head)
	    {
		    if(head->next && head->next->val == head->val)
		    {
			    while(head->next && head->next->val == head->val)
				    head = head->next;
			    if(!head->next)
			    {
				    prev->next = NULL;
			    }
			    else
			    {
				    head = head->next;
			    }
			
		    }
		    else
		    {
			    prev->next = head;
			    prev = head;
			    head = head->next;
		    }
    	}
    	prev->next = head;
        return dummy->next;
    }
};
//My correct answer
 
   ListNode* deleteDuplicates(ListNode* head) {
        	if(!head || !head->next)
		return head;
	ListNode* dummy = new ListNode(0);
	ListNode* prev = dummy;
	while(head)
	{
		if(head->next && head->next->val == head->val)
		{
			ListNode* temp = head;
			while(temp && head->val == temp->val)
				temp = temp->next;
			prev->next = temp;
			head = temp;
		}
		else
		{
			prev->next = head;
			prev = head;
			head = head->next;
		}
	}
	
	return dummy->next;
    }


//others answer

ListNode *deleteDuplicates(ListNode *head) {
        ListNode **runner = &head;
        
        if(!head || !head->next)return head;
        
        while(*runner)
        {
            if((*runner)->next && (*runner)->next->val == (*runner)->val)
            {
                ListNode *temp = *runner;
                while(temp && (*runner)->val == temp->val)
                    temp = temp->next;
                
                *runner = temp;
            }
            else
                runner = &((*runner)->next);
        }
        
        return head;
    }
总结:这题我列出了三个答案,其中第一个是我写错的答案,其实已经修改了很多次了,但就是有些情况没有考虑全。其实参考第二份答案也可以看到,思路跟步骤已经很接近了,就是中间while循环那部分没有处理好,以至于老是出错,第二份答案是在参考别人做的3的基础上修改的,中间的那个temp指针用的很好,一下子就解决了我的问题,而我就是没有想到。最后想说的是2和3的区别在于是构建一个头结点去修改还是直接用修改指针的指针的方式直接修改头结点。

61. Rotate List
//功能:给定一个值k,在倒数第K个值处反转链表,若K大于链表长,则不反转。

/**
 * 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) return head;
        
        int len=1; // number of nodes
        ListNode *newH, *tail;
        newH=tail=head;
        
        while(tail->next)  // get the number of nodes in the list
        {
            tail = tail->next;
            len++;
        }
        tail->next = head; // circle the link

        if(k %= len) 
        {
            for(auto i=0; i<len-k; i++) tail = tail->next; // the tail node is the (len-k)-th node (1st node is head)
        }
        newH = tail->next; 
        tail->next = NULL;
        return newH;
    }
};

//another ways
ListNode* rotateRight(ListNode* head, int k) {
    ListNode newHead(0);
	newHead.next = head;
	ListNode newhead(0), *fast = head, *slow;
	if(head == NULL || head->next == NULL) return head;
	int count = 1, len = 1;
	// Find the length of the ListNode in case the rotate place is very large
	while(fast->next) {
		len++;
		fast = fast->next;
	}
	// Find the relative place shift based on the ListNode length
	k = len - k%len;
	if(k == 0) return head;
	fast = head;
	count = 1;
	// Find the node ready to rotate
	while(count < k) {
		fast = fast->next;
		count++;
	}
	slow = fast;
	// Find the end of ListNode
	while(fast&&fast->next) fast = fast->next;
	fast->next = newHead.next;
	newHead.next = slow->next;
	slow->next = NULL;
	return newHead.next;
    }
总结:这道题涉及到链表的反转,其实并不难,关键是题目意思觉得说的不够明确,对于k>length没有说怎么办,看了别人的答案才知道K是循环的,也就是去k%length。这里给出来两种别人做的答案。共同的地方就是都需要遍历一遍来确定链表的长度,都是采用len-k的方式来正向遍历找到断裂位置。第一种方式比较巧妙,它首先把链表链接成一个环,再去找断裂点。第二种方式就是很普通的方式,按照正常的思维去连接,断开。


猜你喜欢

转载自blog.csdn.net/dby3579/article/details/52228048
今日推荐