单链表专题
2016-07-23 22:04
357 查看
声明:部分试题、解决方案来自LeetCode,本文旨在学习
这里要分享的是LeetCode上的代码,思路清晰,代码简洁。
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Solution :
时间复杂度O(m+n)
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
Solution :
递归版,时间复杂度O(n),空间复杂度O(1)
For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
Solution:
迭代版,时间复杂度O(n),空间复杂度O(1)
For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
Solution :
递归版,时间复杂度O(n),空间复杂度O(1)
For example, Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
Note:
• Given n will always be valid.
• Try to do this in one pass.
Solution:
设两个指针p; q,让q 先走n 步,然后p 和q 一起走,直到q 走到尾节点,删除p->next 即可。
也可以直接通过计算length-k得到直接需要走多少步
Follow up: Can you solve it without using extra space?
Example:
1 -> 2 -> 3 -> 4 -> 5 -> 3 。。。
输出节点3
分析:
Solution:
时间复杂度O(n),空间复杂度O(1)
You should preserve the original relative order of the nodes in each of the two partitions.
For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.
Solution:
时间复杂度O(n),空间复杂度O(1)
从链表的定义可以看出,这两个链表是单链表,如果两个链表有公共节点,那么这两个链表从某一节点开始,它们的m_pNext都指向同一个节点,之后它们所有的节点都是重合的,不可能再出现分叉。所以拓扑形状看起来是Y型。
一个简单的方法是:首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。在第二次遍历的时候,先在较长的节点上走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的公共的节点。
具体实现的程序如下:
1.数据结构
普通的单链表及构造函数struct Node{ int val; Node* next; Node(int value,Node* n = NULL):val(value),next(n){}; };
2.合并两个有序序列
合并两个有序序列是合并K个有序序列的子问题Node* merge_two_sorted_lists(Node* node_1,Node* node_2) { if(node_1 == NULL) return node_2; if(node_2 == NULL) return node_1; Node* pHead = NULL; if (node_1->val <= node_2->val) { pHead = new Node(node_1->val); if(node_1 != NULL) node_1 = node_1->next; } else { pHead = new Node(node_2->val); if(node_2 != NULL) node_2 = node_2->next; } pHead->next = merge_two_sorted_lists(node_1,node_2); return pHead; }
3.合并K个有序序列
对于这个问题,一般会暴力地想到每次从所有单链表中选择一个最小的加到最终的链表中,但是这样复杂度太高,未充分利用前一次比较的信息,进而我们可以想到优先队列的思路。这里要分享的是LeetCode上的代码,思路清晰,代码简洁。
Node* merge_K_sorted_lists(vector<Node*> &lists) { if(lists.empty()) return NULL; while(lists.size() > 1) { //cout << (*(lists.begin()))->val << endl; lists.push_back(merge_two_sorted_lists(lists[0], lists[1])); lists.erase(lists.begin()); lists.erase(lists.begin()); //vector erase 删除迭代器指定位置元素,返回表示指向下一个值的迭代器 } return lists.front(); }
4.Add Two Numbers
You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Solution :
时间复杂度O(m+n)
ListNode* addTwoNumber(ListNode* la, ListNode* lb) { ListNode dummy(-1); //头结点 int carry = 0; ListNode* prev = &dummy; while (la || lb) { int va = la == NULL ? 0 : la->val; int vb = lb == NULL ? 0 : lb->val; int value = (va+vb+carry)%10; prev->next = new ListNode(value);//尾插法 carry = (va+vb+carry)/10; prev = prev->next; la = la == NULL ? NULL : la->next; lb = lb == NULL ? NULL : lb->next; } if(carry > 0) prev->next = new ListNode(carry); return dummy.next; }
5.翻转链表
ListNode* reverse_list(ListNode* node) { ListNode* pPre = NULL; ListNode* pNode = node; ListNode* pReversedHead = NULL; while (pNode) { ListNode* pNext = pNode->next; if (pNext == NULL) pReversedHead = pNode; pNode->next = pPre; pPre = pNode; pNode = pNext; } return pReversedHead; }
6.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.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
Solution :
递归版,时间复杂度O(n),空间复杂度O(1)
ListNode* reverseKGroup(ListNode* head, int k) { if(head == NULL || head->next == NULL || k < 2) return head; ListNode* next_group = head; for (int i = 0; i < k; i++) { if(next_group) next_group = next_group->next; else return head; } //next_group is the head of next group //new_next_group is the new head of next group after reversion ListNode* new_next_group = reverseKGroup(next_group,k); ListNode* prev = NULL; ListNode* cur = head; while (cur != next_group) { ListNode* next = cur->next; cur->next = prev ? prev : new_next_group; prev = cur; cur = next; } return prev; //prev will be the new head of this group }
7.Remove Duplicates from Sorted List
Given a sorted linked list, delete all duplicates such that each element appear only once.For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
Solution:
迭代版,时间复杂度O(n),空间复杂度O(1)
ListNode* drop_duplicates_node(ListNode* node) { if (node == NULL || node->next == NULL) return node; ListNode dummy(-1,node); ListNode* pNext = node->next; while (node && pNext) { if (node->val == pNext->val) { ListNode* tmp = pNext; node->next = pNext->next; pNext = pNext->next; delete tmp; } else { node = pNext; pNext = pNext->next; } } return dummy.next; }
8.Remove Duplicates from Sorted List II
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
Solution :
递归版,时间复杂度O(n),空间复杂度O(1)
ListNode* deleteDuplicates(ListNode* head) { if(head == NULL || head->next == NULL) return head; ListNode* p = head->next; if (head->val == p->val) { while (p && head->val == p->val) { ListNode* tmp = p; p = p->next; delete tmp; } delete head; return deleteDuplicates(p); //将上面两行替换为注释的这两行,即可解决drop duplicates I //node->next = drop_duplicates(p); //return node; } else { head->next = deleteDuplicates(head->next); return head; } }
9.Remove Nth Node From End of List
Given a linked list, remove the nth node from the end of list and return its head.For example, Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
Note:
• Given n will always be valid.
• Try to do this in one pass.
Solution:
设两个指针p; q,让q 先走n 步,然后p 和q 一起走,直到q 走到尾节点,删除p->next 即可。
也可以直接通过计算length-k得到直接需要走多少步
ListNode* removeNthFromEnd(ListNode* head, int k) { ListNode dummy(-1,head); //定义头结点 ListNode* p = &dummy; ListNode* q = &dummy; for (int i = 0; i < k; i++) //q先走n步 q = q->next; while (q->next) { p = p->next; q = q->next; } ListNode* tmp = p->next; p->next = p->next->next; delete tmp; return dummy.next; }
10.Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.Follow up: Can you solve it without using extra space?
Example:
1 -> 2 -> 3 -> 4 -> 5 -> 3 。。。
输出节点3
分析:
Solution:
时间复杂度O(n),空间复杂度O(1)
ListNode* beginOfCycle(ListNode* head) { ListNode* slow = head; ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { ListNode* slow2 = head; while (slow2 != slow) { slow2 = slow2->next; slow = slow->next; } return slow2; } } return nullptr; }
11.Partition List
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,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.
Solution:
时间复杂度O(n),空间复杂度O(1)
ListNode* partition(ListNode* node, int x) { ListNode left_dummy(-1); //头结点 ListNode right_dummy(-1); ListNode* left_cur = &left_dummy; ListNode* right_cur = &right_dummy; while(node) { if (node->val < x) { left_cur->next = node; left_cur = node; } else { right_cur->next = node; right_cur = node; } node = node->next; } //链接 left_cur->next = right_dummy.next; right_cur->next = NULL; return left_dummy.next; }
12.两个链表中的第一个公共节点
面试这道题的时候很多的面试者第一反应就是采用蛮力的方法:在第一个链表上顺序遍历每个节点,每遍历到一个节点的时候,在第二个链表上顺序遍历每个节点。如果第二个链表上的节点和第一个链表上的节点一样,就说明两个链表在节点上重合,于是就找到了公共的节点。而通常蛮力并不是好的方法。从链表的定义可以看出,这两个链表是单链表,如果两个链表有公共节点,那么这两个链表从某一节点开始,它们的m_pNext都指向同一个节点,之后它们所有的节点都是重合的,不可能再出现分叉。所以拓扑形状看起来是Y型。
一个简单的方法是:首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。在第二次遍历的时候,先在较长的节点上走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的公共的节点。
具体实现的程序如下:
ListNode* FindFirstCommonNode(ListNode* pHead1,ListNode *pHead2) { //得到两个链表的长度 unsigned int nLength1 = GetListLength(pHead1); unsigned int nLength2 = GetListLength(pHead2); int nLengthDif = nLength1 - nLength2; ListNode *pHeadLong = pHead1; ListNode *pHeadShort = pHead2; if(nLength2 > nLength1 ) { ListNode *pHeadLong = pHead2; ListNode *pHeadShort = pHead1; nLengthDif = nLength2 - nLength1; } //先在长链表上走几步,再同时在两个链表上遍历。 for(int i = 0;i < nLengthDif;i++) pHeadLong = pHeadLong->m_pNext; while((pHeadLong != NULL)&&(pHeadShort != NULL) &&(pHeadLong != pHeadShort )) { pHeadLong = pHeadLong->m_pNext; pHeadShort = pHeadShort->m_pNext; } //得到第一个公共节点 ListNode *pFirstCommonNode = pHeadLong ; return pFirstCommonNode; } //求链表长度的函数 unsigned int GetListLength(ListNode *pHead) { unsigned int Length = 0; ListNode *pNode = pHead; while(pNode != NULL) { ++Length; pNode = pNode->m_pNext; } return Length; }
相关文章推荐
- 3.5 编写一个程序,从标准输入读入某职员的工作时间(以小时计)和每小时的工资 数,计算并输出他的工资。若职员月工作时间超过 40 小时,则超过部分按原工资的 1.5 倍 来计算。
- Linux help命令
- 54.Automatic Shared Memory Management (ASMM) has been enabled for your database instance. The initia
- pat 1106. Lowest Price in Supply Chain (25)
- [置顶] STM32以太网程序解析
- VO PO BO QO DAO POJO慨念
- springmvc——数据格式化Formatter
- 3.4 编写一个程序,将用户输入的数字(0~6)转换成相应的星期值输出。如用户输 入 3,则程序输出“Today is Wed”;用户输入 0,则输出“Today is Sun”。如果用户输入 的数据
- Openstack Gnocchi 笔记
- BestCoder Round #84 1001 hdu 5747 模拟
- event面向对象【笔记-未完】
- 进程间通信方式总结
- 3.3 从标准输入读入一个数,把它转为英语单词输出,如读入 234,输出“two three four”。
- Android 自定义View实现照片裁剪框与照片裁剪
- js的call和apply函数的使用
- 缓存-redis
- CSS3样式二
- Github搭建过程简记
- lodash常用函数解释
- Linux(1)——在Linux下安装Nodejs(详细教程,包会),并成功创建一个简单的服务器