leetcode 链表处理集锦

1. 对给定的链表进行排序,要求复杂度为O(nlgn)。

算法思想是分治,这样可以达到O(nlgn)。实现时写一个经典getMid函数,然后写合并函数,合并函数可以考虑使用一个多余辅助。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
	ListNode *sortList(ListNode *head) {
		if (head == NULL || head->next==NULL) return head;
		ListNode* mid = getMid(head);
		
		return merge(sortList(head), sortList(mid));
	}
private:
	ListNode *getMid(ListNode* head){
		ListNode*  res = head;
		if (head && head->next){
			ListNode *fast = head->next, *slow = head;
			while (fast && fast->next){
				fast = fast->next->next;
				slow = slow->next;
			}
			res = slow->next;
			slow->next = NULL;
		}
		return res;
	}

	ListNode* merge(ListNode* p1, ListNode* p2){
		ListNode* dummy = new ListNode(0);
		ListNode* cur = dummy;
		while (p1 && p2){
			if (p1->val < p2->val){
				cur->next = p1;
				p1 = p1->next;
			}
			else{
				cur->next = p2;
				p2 = p2->next;
			}
			cur = cur->next;
		}
		cur->next = p1 ? p1 : p2;

		return dummy->next;
	}
};

2. reorder-list。将链表按照如下要求的顺序输出。具体描述如下:

Given a singly linked list LL0L1→…→Ln-1Ln,
reorder it to: L0LnL1Ln-1L2Ln-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}.

根据题意,这道题首先是截取链表的后半段,然后逆置,再逐个合并。截取后半段使用经典的getMid函数。反转可以直接while循环实现。合并时加一个辅助项和一个标志变量,可以逐个地合并起来。

class Solution {
public:
	void reorderList(ListNode *head) {
		ListNode* mid = getMid(head);
		reverse(mid);
		merge(head, mid);
	}
private:
	void merge(ListNode*& p1, ListNode* p2){
		if (p2 == NULL) return;
		ListNode* dummy = new ListNode(0);
		ListNode* cur = dummy;
		bool flag = true;
		while (p1 && p2){
			if (flag){
				cur->next = p1;
				p1 = p1->next;
			}
			else{
				cur->next = p2;
				p2 = p2->next;
			}
			cur = cur->next;
			flag = !flag;
		}
		cur->next = p2;
		p1 = dummy->next;
		return;
	}
	ListNode* getMid(ListNode* head){
		ListNode* res = head;
		if (head == NULL || head->next == NULL) return NULL;
		ListNode *slow = head, *fast = head->next;
		while (fast->next && fast->next->next){//这样可以保证l2长1
			fast = fast->next->next;
			slow = slow->next;
		}
		res = slow->next;
		slow->next = NULL;
		return res;
	}

	void reverse(ListNode*& head){
		if (head == NULL) return;
		ListNode* cur = NULL;
		while (head && head->next){
			ListNode* tmp = head->next;
			head->next = cur;
			cur = head;
			head = tmp;
		}
		head->next = cur;
		return;
	}
};


3. 判断链表是否有环。

使用快慢指针可以判断链表是否有环。

class Solution {
public:
	bool hasCycle(ListNode *head) {
		if (head == NULL) return false;
		ListNode *fast = head, *slow = head;
		while (fast && fast->next){
			fast = fast->next->next;
			slow = slow->next;
			if (fast == slow)
				return true;
		}
		return false;
	}
};

4. 判断链表是否有环,如果有,找出环的起始点。

在快慢指针的基础上,如果相遇则有环,相遇时将fast指针从头部走起,当fast与slow指针再次相遇时,环的起始点就找到了。

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

5. 链表(带随机指针)的拷贝。
两种思路:第一种借助数据结构hash_map。先把节点创建出来,然后再根据hash_map设置其random指针。

第二种思路是:每创建一个节点,将该节点插入到源节点的后边,然后再设置随机指针,最后再断开成为新的链表,实现拷贝。

以下是第二种思路的代码:

/**
 * 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) {}
 * };
 */
class Solution {
public:
	RandomListNode *copyRandomList(RandomListNode *head) {
		RandomListNode *res=head;
		if (head == NULL) return res;
		RandomListNode *cur = head;
		while (cur){
			RandomListNode* tmp = new RandomListNode(cur->label);
			tmp->next = cur->next;
			cur->next = tmp;
			cur = tmp->next;
		}
		cur = head;
		while (cur){
			if (cur->random)
				cur->next->random = cur->random->next;
			cur = cur->next->next;
		}
		cur = head;
		res = head->next;
		while (cur){
			RandomListNode* p1 = cur->next->next;
			if (p1){
				cur->next->next = p1->next;
			}
			cur->next = p1;
			cur = p1;
		}
		return res;
	}
};

6. 把有序链表转换为高度平衡的二叉搜索树。

思路是从中点做根,进行转换,这样就需要geiMid函数,然后递归的转换即可。

class Solution {
public:
	TreeNode *sortedListToBST(ListNode *head) {
		if (head == NULL) return NULL;
        <span style="white-space:pre">	</span>if(head->next == NULL) return new TreeNode(head->val);
		ListNode* mid = getMid(head);
		TreeNode* root = new TreeNode(mid->val);
		root->left = sortedListToBST(head);
		root->right = sortedListToBST(mid->next);
		return root;
	}
private:
	ListNode * getMid(ListNode *head){
		if (head == NULL || head->next == NULL) return NULL;
		ListNode *slow = head, *fast = head->next;
		while (fast->next && fast->next->next){
			fast = fast->next->next;
			slow = slow->next;
		}
		ListNode* res = slow->next;
		slow->next = NULL;
		return res;
	}
};

7. 把链表m到n之间的节点进行反转。

思路:可以采用头插法。从第m个节点开始,将点m到n依次插入当段链表的头部。

class Solution {
public:
	ListNode *reverseBetween(ListNode *head, int m, int n) {
		if (head == NULL || m < 1 || m >= n) return head;
		ListNode dummy(0); dummy.next = head;
		ListNode* pre = &dummy, *cur = head;
		for (int i = 0; i < m - 1; i++)
			pre = pre->next;
		if (pre)
			cur = pre->next;//待插入的点是cur的下一个节点
		for (int i = m; i < n; i++){
			ListNode* tmp = cur->next;
			cur->next = tmp->next;
			tmp->next = pre->next;
			pre->next = tmp;
			//cur = cur->next;
		}
		return dummy.next;
	}
};

8. 根据给定值分割链表,保持各个分割的相对顺序不变。

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given1->4->3->2->5->2and x = 3,
return1->2->2->4->3->5.

两种思路:第一种类似快排的写法,值小就插入小的链表的尾部,值大就越过不处理。采用for循环写法

第二种用两个头指针分别记录比x值大的链表和比x值小的链表。

以下代码是第一种思路:

class Solution {
public:
	ListNode *partition(ListNode *head, int x) {
		ListNode tmpNode(0);
		ListNode* dummy = &tmpNode;
		dummy->next = head;
		ListNode* p = dummy;
		for (ListNode* cur = dummy; cur->next; ){
			if (cur->next->val < x){
				if (cur == p){
					p = p->next;
					cur = cur->next;
				}
				else{
					ListNode* tmp = cur->next;
					cur->next = tmp->next;
					tmp->next = p->next;
					p->next = tmp;
					p = tmp;
				}
			}
			else{
				cur = cur->next;
			}
		}
		return dummy->next;
	}
};

9. 删除排序链表的中的重复元素,重复的只保存一份。

模仿数组,写一个for循环,如果跟前边的值一样,就删除。

class Solution {
public:
	ListNode *deleteDuplicates(ListNode *head) {
		if (head == NULL) return head;
		for (ListNode* cur = head; cur->next;){
			if (cur->next->val == cur->val){
				ListNode* tmp = cur->next;
				cur->next = tmp->next;
				delete tmp;
			}
			else{
				cur = cur->next;
			}
		}
		return head;
	}
};

10. 删除链表中的重复元素。如果出现重复,则全部删除不保留。

设置一个辅助项,然后遍历链表,如果cur->next 和cur->next->next的值相等,则把该值value记录下来,用一个while循环,只要值等于value的节点,删除。

class Solution {
public:
	ListNode *deleteDuplicates(ListNode *head) {
		if (head == NULL || head->next == NULL) return head;
		ListNode tmpNode(0), *dummy = &tmpNode;
		dummy->next = head;
		ListNode* cur = dummy;
		for (cur; cur->next&&cur->next->next;){
			if (cur->next->val == cur->next->next->val){
				ListNode* tmp = cur->next;
				int val = tmp->val;
				while (tmp && tmp->val == val){
					ListNode* p = tmp;
					tmp = tmp->next;
					delete p;
				}
				cur->next = tmp;
			}
			else{
				cur = cur->next;
			}
		}
		//if (cur!=dummy && cur->next && cur->val == cur->next->val){
		//	delete cur->next;
		//	cur->next = NULL;
		//}
		return dummy->next;
	}
};


11. 合并两个有序链表。

添加一个辅助项,然后直接实现合并。

class Solution {
public:
	ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
		if (l1 == NULL || l2 == NULL)
			return l1 ? l1 : l2;
		ListNode tmpNode(INT_MIN), *dummy = &tmpNode, *cur = dummy;
		while (l1 && l2){
			if (l1->val < l2->val){
				cur->next = l1;
				l1 = l1->next;
				cur = cur->next;
			}
			else{
				cur->next = l2;
				l2 = l2->next;
				cur = cur->next;
			}
		}
		cur->next = l1 ? l1 : l2;
		return dummy->next;
	}
};

12. 把链表尾部的k个结点旋转到头部。

第一种思路:首先更新k(防止大于length),然后用距离为k个节点的slow 和 fast指针,断开并反转。

第二种思路:遍历到链表尾部,然后记录链表的长度,并把链表串成环,然后在len-k处进行断开即可。

第一种思路:

class Solution {
public:
	ListNode *rotateRight(ListNode *head, int k) {
		if (head == NULL) return head;
		int len = getLength(head);
		k = k%len;
		if (k == 0)
			return head;
		ListNode *slow = head, *fast = head;
		while (fast && k--)
			fast = fast->next;
		while (fast->next){
			fast = fast->next;
			slow = slow->next;
		}
		ListNode* res = slow->next;
		slow->next = NULL;
		fast->next = head;
		return res;
	}
private:
	int getLength(ListNode* head){
		int count = 0;
		while (head){
			count++;
			head = head->next;
		}
		return count;
	}
};
第二种思路:

class Solution {
public:
	ListNode *rotateRight(ListNode *head, int k) {
		if (head == NULL) return head;
		int len = 1;
        ListNode* cur = head;
        while(cur->next){
            len++;
            cur = cur->next;
        }
        cur->next = head;
        k = k%len;
        cur = head;
        for(int i=0;i<len-k-1;i++){
            cur = cur->next;
        }
        head = cur->next;
        cur->next = NULL;
		return head;
	}
};

13. 把链表中每k个一组,进行反转。不足k个不反转。

首先计算链表的长度length,然后只要满足length>=k就进行一轮反转,然后length -=k.更新好每组的首尾指针即可。

class Solution {
public:
	ListNode *reverseKGroup(ListNode *head, int k) {
		int len = 0;
		ListNode* temp = head;
		while (temp){
			len++;
			temp = temp->next;
		}
		if (len < k)
			return head;
		ListNode dummy(0);
		temp = &dummy;
		while (len >= k){
			ListNode* cur = head;
			ListNode* pre = NULL;
			ListNode* nex = NULL;
			for (int i = 0; i < k; i++){
				nex = cur->next;
				cur->next = pre;
				pre = cur;
				cur = nex;
			}
			temp->next = pre;
			temp = head;
			head = cur;
            len -= k;
		}
		temp->next = head;
		return dummy.next;
	}
};

14. 链表中的节点两两反转。此题是上一题的子集,不过因为是两两反转,所以可以考虑用递归实现,代码简洁。

class Solution {
public:
	ListNode *swapPairs(ListNode *head) {
		if (head == NULL || head->next == NULL)
			return head;
		ListNode* next = head->next;
		head->next = swapPairs(next->next);
		next->next = head;
		return next;
	}
};

15. 合并k个有序链表。

合并k个有序链表或者k个有序数组的合并可以借助优先队列很巧妙的实现。先把各个链表的头节点加入优先队列,每次从头部取出当前层最小的节点,然后push一个最小的节点的下一个节点进入优先队列,如此循环。

class cmp{
public:
	bool operator()(const ListNode* a, const ListNode* b){
		return a->val > b->val;
	}
};

class Solution {
public:
	ListNode *mergeKLists(vector<ListNode *> &lists) {
		if (lists.empty()) return NULL;
		ListNode tmpNode(0), *dummy=&tmpNode, *cur = dummy;
		priority_queue<ListNode*, vector<ListNode*>, cmp> myqueue;
		for (int i = 0; i < lists.size(); i++){
            if(lists[i])
				myqueue.push(lists[i]);
		}
		while (!myqueue.empty()){
			ListNode* tmp = myqueue.top();
			myqueue.pop();
			cur->next = tmp;
            cur = tmp;
			if (tmp->next)
				myqueue.push(tmp->next);
		}
		return dummy->next;
	}
};

16. 移除链表倒数第n个结点。

因为可能删除的节点是头节点,所以添加一个辅助节点,然后用一个距离为n的slow 和 fast指针,进行查找和删除。

class Solution {
public:
	ListNode *removeNthFromEnd(ListNode *head, int n) {
		ListNode tmpNode(0), *dummy = &tmpNode;
		dummy->next = head;
		ListNode *slow = dummy, *fast = dummy;
		while (n--)
			fast = fast->next;
		while (fast->next){
			slow = slow->next;
			fast = fast->next;
		}
		ListNode* tmp = slow->next;
		slow->next = tmp->next;
		delete tmp;
		return dummy->next;
	}
};

17. 两个用链表表示的数字相加。

设置一个辅助项,循环相加,注意处理进位即可。

class Solution {
public:
	ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
		if (l1 == NULL || l2 == NULL)
			return l1 ? l1 : l2;
		ListNode tmpNode(0), *dummy=&tmpNode, *cur=dummy;
		int carry = 0;
		while (l1 && l2){
			int val = l1->val + l2->val + carry;
			cur->next = new ListNode(val % 10);
			carry = val / 10;
			l1 = l1->next;
			l2 = l2->next;
			cur = cur->next;
		}
		ListNode* p = l1 ? l1 : l2;
		while(p){
			int val = p->val + carry;
			cur->next = new ListNode(val % 10);
			carry = val / 10;
			p = p->next;
			cur = cur->next;
		}
		if (carry){
			cur->next = new ListNode(carry);
		}
		return dummy->next;
	}
};





















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值