牛客剑指offer刷题记录(五)
来源:互联网 发布:js event.target.id 编辑:程序博客网 时间:2024/05/21 04:21
复杂链表复制
链表的指针域中,除了有指向下一个节点的链表以外,还有一个指向随机节点的指针。
struct ListNode{ int val; ListNode * next; ListNode * random;};
思路一
常规做法,空间换时间。
先常规的将拷贝的节点用next串起来,遍历一遍原始链表,然后尾插法即可。
在尾插的同时,建立一个由原始节点指针P到拷贝节点指针C的一个map。
再次遍历原始指针,如果指针p指向节点的random域不为NULL,那么通过m[p]->random=m[p->random]
来达到赋值random的目的:
class Solution {public: RandomListNode* Clone(RandomListNode* pHead) { if (nullptr == pHead) return nullptr; RandomListNode * p = pHead; unordered_map<RandomListNode*, RandomListNode*>m; RandomListNode *newHead = new RandomListNode(0); RandomListNode*tail = newHead; while (nullptr != p) { RandomListNode * r = new RandomListNode(p->label); //if (nullptr != p->random) m[p] = r; tail->next = r; tail = r; p = p->next; } tail->next = nullptr; p = pHead; while (nullptr != p) { if (nullptr != p->random) { m[p]->random = m[p->random]; } p = p->next; } return newHead->next; }};
思路二
相当聪明的做法,看了书才能知道,就是把clone的节点接到原节点的后面
例如:
l1->l2->l3
变成
l1->l1’->l2->l2’->l3->l3’->NULL
然后有p->next->random=p->random->next
最后再把这个链表拆成两个链表即可。
class Solution {public: RandomListNode* Clone(RandomListNode* pHead) { if (nullptr == pHead) return nullptr; RandomListNode * p = pHead; while (nullptr != p) { RandomListNode*r = new RandomListNode(p->label); r->next = p->next; p->next = r; p = r->next; } p = pHead; while (nullptr != p) { if (nullptr != p->random) { p->next->random = p->random->next; } p = p->next->next; } RandomListNode * newHead = new RandomListNode(0);//新链表的头结点 RandomListNode *k = newHead; RandomListNode* o = pHead;//这里还需要保证原链表的形状 p = pHead->next; while (nullptr != p) { o->next = o->next->next; k->next = p; k = p; if (nullptr != p->next) p = p->next->next; else break; o = o->next; } return newHead->next; }};
二叉搜索树与双向链表
将二叉搜索树转成双向链表:
原先指向左子树的指针调整为指向双向链表中前一个节点的的指针pre,原先指向右子树的指针调整为指向双向链表后一个节点的指针next.
由于二叉搜索树中序遍历的序列是有序的,而题目要求双向链表也是有序的,因此,我们以中序的方式进行递归。
这里,我们还需要一个辅助的指针,指向当前已经排好的双向链表的最后一个节点。
所以,递归的顺序是,先排左子树,再排当前节点,再排右子树。
排好以后,list指针需要往回溯,找到双向链表的头结点。
class Solution {private: void inorder(TreeNode *root,TreeNode *&list) { if(nullptr==root) return; inorder(root->left,list); root->left = list; if (nullptr != list) list->right = root; list = root; inorder(root->right,list); }public: TreeNode* Convert(TreeNode* pRootOfTree) { if (nullptr == pRootOfTree) return nullptr; TreeNode *list = nullptr; inorder(pRootOfTree, list); while (nullptr!=list&&nullptr != list->left) { list = list->left; } return list; }};
字符串全排列
把字串分成两部分,一部分是字符串的第一个字符,一部分是第一个字符后面所有的字符,将第一个字符分别与后面所有的字符交换:
比如abc,a与b、c分别交换,得到序列bac和cba。
对于子序列,再递归地完成该操作即可。
不过牛客上面有个两个要求:
1.要求字符串出现重复字符,即可能有aac这样的序列
2.要求字符串是字典序的。
对于第2个要求,我们很容易实现,只要将第一个字符后面所有的字符sort一下即可,对于第1个要求,如果出现重复字符,则不执行swap操作,这里需要维护一个visit容器。
class Solution {private: void dfs(vector<string>&result, string str, int start) { if (start == str.size()) { result.push_back(str); //cout << str << endl; } sort(str.begin() + start, str.end());//保证字典序 unordered_set<char>visit;//保证重复不交换 for (int i = start; i < str.size(); ++i) { if (visit.find(str[i]) == visit.end()) { visit.insert(str[i]); swap(str[start], str[i]); dfs(result, str, start + 1); swap(str[start], str[i]); } } }public: vector<string> Permutation(string str) { if (0 == str.size()) return vector<string>(0); vector<string>result; dfs(result, str, 0); return result; }};
数组中出现次数超过一半的数字
最简单的办法莫过于排序取中间的数,这样时间效率就不高了,另外可以采用hash的方式统计一下,这样用额外的空间换取时间倒是可以将时间复杂度控制在线性。最后有一个很牛逼的算法交筛选法。
详细参考:LeetCode169—MajorityElements
class Solution {public: int MoreThanHalfNum_Solution(vector<int> numbers) { int candidates = numbers[0]; int count = 0; for (int i = 0; i < numbers.size(); ++i) { if (count == 0) { count = 1; candidates = numbers[i]; } else { count = (candidates == numbers[i]) ? (count + 1) : (count - 1); } } //下面代码排除没有多数派情况 count = 0; for (int i = 0; i<numbers.size(); i++) { if (numbers[i] == candidates) count++; } if (count <= (numbers.size() ) / 2) return 0; return candidates; }};
最小的k个数
借助容器了。
能想象到,最小的k个数和最大的k个数可以用堆这种结构来保存。这里我用一个优先级队列。事实上红黑树也是可以的,用一个set也是可以的。
优先级队列
class Solution {public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { if (k == 0 || input.size() == 0||k>input.size()) return vector<int>(); priority_queue<int>q; for (int i = 0; i < input.size(); ++i) { q.push(input[i]); if (q.size()>k) q.pop(); } vector<int>v; v.reserve(q.size()); while (!q.empty()) { v.push_back(q.top()); q.pop(); } reverse(v.begin(), v.end()); return v; }};
有序集合
class Solution {public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { if (k == 0 || input.size() == 0||k>input.size()) return vector<int>(); set<int>s; for (int i = 0; i < input.size(); ++i) { s.insert(input[i]); if (s.size()>k) { s.erase(std::prev(s.end())); } } vector<int>v; v.reserve(s.size()); for (auto it = s.begin(); it != s.end(); ++it) { v.push_back(*it); } return v; }};
第二种思路是用partition函数来解决问题,可以想见,快排的思路是比基准小的在左边,比基准pivot大的再右边,先回顾一下快排和partition函数。
int Partition(vector<int>&v, int p, int r){ int x = v[r];//pivot int i = p-1; for (int j = p; j < r; ++j) { if (v[j] <= x) { swap(v[j], v[++i]); } } swap(v[r], v[i+1]); return i +1;}void QuickSort(vector<int>&v, int p, int r){ if (p < r) { int q = Partition(v, p, r); QuickSort(v, p, q - 1); QuickSort(v, q + 1, r); }}
如果基于数组的第k个数字来调整,使得比第k个数字小的在数组左边,比第k个数字大的都在右边,这样左边的k个数字就是k个最小数字。基于这种思路的代码如下:
int Partition(vector<int>&v, int p, int r){ int x = v[r];//pivot int i = p-1; for (int j = p; j < r; ++j) { if (v[j] <= x) { swap(v[j], v[++i]); } } swap(v[r], v[i+1]); return i +1;}class Solution {public:vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { if (k == 0 || input.size() == 0||k>input.size()) return vector<int>(); int p, r; p = 0; r = input.size() - 1; int i = Partition(input, p, r); while (i != k - 1) { if (i > k - 1) { r = i - 1; i = Partition(input, p, r); } else { p = i + 1; i = Partition(input, p, r); } }//end while vector<int>res(input.begin(), input.begin() + k); return res; }};
- 牛客剑指offer刷题记录(五)
- 牛客剑指offer刷题记录(一)
- 牛客剑指offer刷题记录(二)
- 牛客剑指offer刷题记录(三)
- 牛客剑指offer刷题记录(四)
- 牛客剑指offer刷题记录(六)
- 牛客剑指offer刷题记录(七)
- 剑指offer刷题记录1
- 剑指offer刷题记录2
- 剑指offer 日常刷题记录
- 每周刷题记录(持续更新)
- OI刷题记录
- OI刷题记录~
- leetcode刷题记录
- 刷题记录
- 6.22刷题记录
- 7.26-刷题记录
- hdu 刷题记录
- Bootstrap按钮插件
- python rc4
- Andriod 环境搭建
- html.div禁用点击事件
- 拖拽文件上传(Java篇)dropzone.js的简单使用
- 牛客剑指offer刷题记录(五)
- 第七周 leetcode 62. Unique Paths(Medium)
- 图解集合5:不正确地使用HashMap引发死循环及元素丢失
- 使用OLAMI SDK和讯飞语音合成制作一个语音回复的短信小助手
- 算法概论8.8
- 投资者该用什么指标选择量化基金?
- codeforces
- PHP7 Null合并运算符运用
- bzoj 1455 罗马游戏 解题报告