链表相关的leetcode重要题目

Leetcode 92:反转链表II

解决这道题需要三个步骤:

  1. 找到需要反转的第一个节点。可以通过头节点前进m-1步,找到反转开始的位置。
  2. 将需要反转的部分进行反转。参考Leetcode 206:反转链表
  3. 将反转部分与剩余部分进行链接。其中分为两种情况,m=1与m>1。当m=1时,仅需要将反转部分反转前的头节点的next指向反转部分反转前尾节点的后继,新链表的头节点为反转部分反转前的尾节点。当m>1时,还需要将反转部分反转前头节点的前驱的next指向反转部分反转前的末节点,新链表的头节点仍为原链表的头节点。假设m=2,n=4,其中有以下几个关键节点:
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        int change_len = n - m + 1; //需要反转的长度
        ListNode *pre_head = NULL; //反转部分的前驱节点
        ListNode *result = head;
        while(head && --m) //移动到反转的开始位置
        {
        	pre_head = head;
        	head = head->next;
        }
        ListNode *tmp = head; //反转部分头节点,即反转后的尾节点
        ListNode *new_head = NULL;
		while(head && change_len) //反转部分链表
        {
			ListNode *next = head->next;
			head->next = new_head;
			new_head = head;
			head = next;
			change_len--;
		}
		tmp->next = head; //反转后的尾节点链接反转段的后一个节点
		if (pre_head) //前驱节点不为空,说明m>1
        {
			pre_head->next = new_head; //反转部分的前驱节点连接反转后的头节点
		}
		else
        {
			result = new_head;
		}
		return result;
    }
};

Leetcode 234:回文链表
回文链表就是说从链表的头节点到尾节点,和从链表的尾节点到头节点遍历得到的结果是相同的。简单的做法是利用栈结构,从链表头节点开始将节点依次压入栈中,再依次弹出并连接得到的链表如果与原链表相同,这个链表就是回文链表。如果要求空间复杂度为O(1), 通过快慢指针确定中点,将链表从中点分为两个链表,反转后半部分链表,比较两个子链表的元素。

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL)
            return true;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast->next != NULL && fast->next->next != NULL) //使用快慢指针确定中点,节点数为奇时,指向中点;节点数为偶时,指向中间两个数的前者。
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* head1 = slow->next;
        slow->next = NULL; //从中点的后一个节点开始反转链表
        while(head1 != NULL)
        {
            ListNode* next = head1->next;
            head1->next = slow;
            slow = head1;
            head1 = next;   
        }
        head1 = slow;
        bool res = true;
        while(head1 != NULL && head != NULL)
        {
            if(head1->val != head->val)
                return false;
            head1 = head1->next;
            head = head ->next;
        }
        return res;   
    }
};

Leetcode 142:环形链表II

解决这个问题需要两个步骤:

  1. 使用快慢指针判断链表是否存在环。慢指针在链表中一次走一步,快指针一次走两步,如果链表中存在环,快慢指针总会到达同一个节点meet。

  2. 从链表的头节点head以及meet同时出发,两个指针速度相同,相遇时即为环的起点。

    设绿色部分为a,蓝色部分为b,黄色部分为c。可知快指针走过的路程时慢指针的两倍,即2(a+b)=a+b+c+b,可以得到a=c,所以head节点与meet节点相遇时即为环的起点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* meet = NULL;
        while(fast)
        {
            fast = fast->next;
            slow = slow->next;
            if(!fast)
                return NULL;
            else
                fast = fast->next;
            if(fast == slow)
            {
                meet = fast;
                break;
            }
        }
        if (meet == NULL)
            return NULL;
        while(head && meet)
        {
            if(head == meet)
                return meet;
            head = head->next;
            meet = meet->next;
        } 
        return NULL;
    }
};

Leetcode 138:复制带随机指针的链表

解决这个问题需要三个步骤:

  1. 复制节点,将复制的节点连接到原节点之后,原节点的next连接到复制的节点之后。
  2. 复制随机指针。
  3. 分离节点。
class Solution {
public:
    Node* copyRandomList(Node* head) 
    {
        if (!head) return nullptr;
        Node* curnode = head;
        while (curnode) //遍历原链表复制节点
        { 
            Node* copy = new Node(curnode->val, curnode->next, nullptr);
            Node* tmp = curnode->next;
            curnode->next = copy;
            curnode = tmp;
        }
        curnode = head;
        while (curnode) //遍历链表连接复制节点的随机指针
        { 
            if (curnode->random) 
                curnode->next->random = curnode->random->next;
            curnode = curnode->next->next;
        }
        curnode = head;
        Node* new_head = head->next; //复制节点的头节点
        while (curnode->next) //分离节点
        { 
            Node* tmp = curnode->next;
            curnode->next = curnode->next->next;
            curnode = tmp;
        }
        return new_head;
    }
}; 

Leetcode 23:合并K个排序链表

解决此题的思路类似于归并排序,对于两个排序列表的合并可以参考Leetcode 21:合并两个有序链表,在对k个排序链表进行合并时,可以通过分治的思想,对k个链表两两进行合并。

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0)
            return NULL;
        if(lists.size() == 1)
            return lists[0];
        if(lists.size() == 2)
            return mergeTwoLists(lists[0],lists[1]);
        int size = lists.size();
        int mid = size/2;
        vector<ListNode*> tmp1;
        vector<ListNode*> tmp2;
        for(int i = 0;i < mid;i++)
        {
            tmp1.push_back(lists[i]);
        }
        for(int i = mid;i < size;i++)
        {
            tmp2.push_back(lists[i]);
        }
        ListNode* l1 = mergeKLists(tmp1);
        ListNode* l2 = mergeKLists(tmp2);
        return mergeTwoLists(l1,l2);   
    }
    
    ListNode* mergeTwoLists(ListNode* list1,ListNode* list2) //合并两个有序链表
    {
        ListNode new_head(0);
        ListNode* prenode = &new_head;
        while(list1&&list2)
        {
            if(list1->val>list2->val)
            {
                prenode->next=list2;
                list2 = list2->next;
            }
            else
            {
                prenode->next = list1;
                list1 = list1->next;
            }
            prenode = prenode->next;
        } 
        
        if(list1) //如果list1不为空,将剩余节点连接在已合并的链表之后
            prenode->next = list1;
        if(list2) //list2同理
            prenode->next = list2;
        return new_head.next;
    }
};
posted @ 2019-06-11 23:31  番茄起司汤  阅读(174)  评论(0编辑  收藏  举报